first:
That was mellow-drama. QC really doesn't support Object Oriented style inheritance, but it's plenty abstract. The most popular entity in QC is "self". self is nothing, it's also anything, maybe it's everything - if self happens to be world. self is entirely an abstraction, so is other & activator... the list continues and can be expanded. None of these entities have an identity of their own. self inherits the identity of the very first entity to start that chain of processes, unless specifically changed (in QC) to something else along the way. The others are assigned either in QC or by an interaction.
An example of an interaction would be if you touch a button. The button is self and you are other. You became other by touching the button. That was an interaction. However, it is possible for a monster to be other by touching that same button. So, other is an abstraction of an activator.
We have only covered 2 pointers (vars) and we have covered every possible entity in the map. Either one of those 2 could be anything. There are more. I won't be covering any more of them here, but they are easy to find. Just go to defs.qc and find everything of the type (.)entity
---
Inheritance works more as an implementation filled with selfs and others. It is that very abstraction that allows us to reuse code across multiple varying types of entities, creating a form of inheritance through implementation. Let's finally look at some code
So, we called Bob in Chris and because of that Chris now has brown hair and eyes. Was this because Chris inherited the properties of Bob? Well, no, not really. This is where it becomes an inheritance through implementation. Chris called Bob so "self" is Chris. If we called just Bob, Bob would have brown hair and eyes, but since we called Bob in Chris, technically Bob doesn't even exist, all that exists is "self" and self is Chris. Chris did not inherit the properties of Bob he implemented the function of Bob, but through that an inheritance was achieved. The above script is identical to
On the surface this way may actually seem shorter, but you must consider that it is only shorter if everyone is exactly like Chris. If only one thing is different you would have to rewrite everything plus change the one different thing.
QC is riddled from top to bottom with mass repetition. Every single spot where a model is being instantiated is being done with nearly identical code. Multiple lines of code that are just repeated and repeated, where it could all be turned into one process with some conditions.
an example.
something to note: this is the uber simple version that would work in most cases. There are other things to be considered here but it is beyond the scope of this discussion to hilight them.
Now, you could call model_instance() in most functions that instantiate a model, instead of the current way, like this
either
or
What's the difference? The first one would work for actual models (mostly) as well as brushes (like doors) and the second way would be for a brush that you want to have the volume of, but do not want to see the texture of.
so.. "You're saying you replaced 3 lines with 2...". Yup that's what I'm saying....if all you ever want to do is instantiate one model. However this process can be built upon. Let's consider a possible script flow.
It's QC, we go from the bottom up. func_field only has to set a few values, or make sure they are set, because typed_instance() will determine everything about it's type and state, ending with calling model_instance, which links the model into the world and creates it if necessary.
So, now I have replaced like 60 lines with 1 (typed_instance()), BUT I have left the door open to bypass typed_instance and go straight for model_instance, should the time arise where typed_instance does not properly check some custom entity I am making.
QC as it stands now, is a very poor example of how to program. Go open subs.qc - find SUB_CalcMoveEnt and SUB_CalcAngleMoveEnt. Go look at those 2 "different" functions and tell me what's so different about them. They could easily be turned into one code that has a condition for the one line that is actually different. How about:
This would add one more argument to the call, but it would eliminate the need for a duplicate. Now there is only one function that does both jobs. You could expand on the interface as well. What if a model has a spawnflag for rotation
in this example the rotate flags existance would make m = true and therefore go the AngleMove route. What if I told you that we don't even need all that. What if I told you that 6 functions could be made into 3 and fully decide whether to move origin or angles? I'm not going to teach you that here, but know that subs.qc alone is just a bunch of unnecessary repetition.
If you want to be good at programming in QC, you should probably start with being good at programming. I hope this discussion has opened your mind to how things could be done more efficiently... less confusingly. What if I told you that I could eliminate 56 functions in QC? How would modding change if there was 56 less duplicate bullshits to worry about? Is there really 56 of them? IDK, I just made that up (so, probably no), but what I'm not making up is the potential for a more streamlined source. I believe that understanding how to fabricate inheritance is the beginning of tapping that potential.
~Gypsy
*inheritance through implementation: that as a phrase is just something I made up that can accurately impress upon you what is happening. That is not a thing you should be trying to act like you know what the f you are talking about... lol. There was no inheritance, so my statement is not accurate in reality. In reality, a chunk of Chris was removed and turned into Bob and then brought back into Chris. It is assumed that Chris had brown hair and eyes regardless of Bob.
Originally posted by me
An example of an interaction would be if you touch a button. The button is self and you are other. You became other by touching the button. That was an interaction. However, it is possible for a monster to be other by touching that same button. So, other is an abstraction of an activator.
We have only covered 2 pointers (vars) and we have covered every possible entity in the map. Either one of those 2 could be anything. There are more. I won't be covering any more of them here, but they are easy to find. Just go to defs.qc and find everything of the type (.)entity
---
Inheritance works more as an implementation filled with selfs and others. It is that very abstraction that allows us to reuse code across multiple varying types of entities, creating a form of inheritance through implementation. Let's finally look at some code
Code:
.string hair; .string eyes; .float age; void() Bob = { self.hair = "brown"; self.eyes = "brown"; }; void() Chris = { Bob(); self.age = 37.4; };
Code:
.string hair; .string eyes; .float age; void() Chris = { self.hair = "brown"; self.eyes = "brown"; self.age = 37.4; };
QC is riddled from top to bottom with mass repetition. Every single spot where a model is being instantiated is being done with nearly identical code. Multiple lines of code that are just repeated and repeated, where it could all be turned into one process with some conditions.
an example.
Code:
void() model_instance = { setmodel(self, self.model); setorigin(self, self.origin); setsize(self,self.mins,self.maxs); }
Now, you could call model_instance() in most functions that instantiate a model, instead of the current way, like this
either
Code:
precache_model(self.model); model_instance();
Code:
model_instance(); self.model = "";
so.. "You're saying you replaced 3 lines with 2...". Yup that's what I'm saying....if all you ever want to do is instantiate one model. However this process can be built upon. Let's consider a possible script flow.
Code:
void()model_instance = { //makes a model instance }; void() typed_instance = { //checks and or sets the solid, movetype, takedamage & health values of any model model_instance(); }; void() func_field = { //set a few constants for this type typed_instance(); }
So, now I have replaced like 60 lines with 1 (typed_instance()), BUT I have left the door open to bypass typed_instance and go straight for model_instance, should the time arise where typed_instance does not properly check some custom entity I am making.
QC as it stands now, is a very poor example of how to program. Go open subs.qc - find SUB_CalcMoveEnt and SUB_CalcAngleMoveEnt. Go look at those 2 "different" functions and tell me what's so different about them. They could easily be turned into one code that has a condition for the one line that is actually different. How about:
Code:
void(entity ent, vector dest, float tspeed, void() func, float m) SUB_EntMove = { local entity stemp = self; self = ent; if(m)SUB_CalcAngleMove(dest,tspeed,func); else SUB_CalcMove(dest,tspeed,func) self = stemp; }
Code:
SUB_EntMove(other, other.pos1, 100, callback, (self.spawnflags & ROTATE));
If you want to be good at programming in QC, you should probably start with being good at programming. I hope this discussion has opened your mind to how things could be done more efficiently... less confusingly. What if I told you that I could eliminate 56 functions in QC? How would modding change if there was 56 less duplicate bullshits to worry about? Is there really 56 of them? IDK, I just made that up (so, probably no), but what I'm not making up is the potential for a more streamlined source. I believe that understanding how to fabricate inheritance is the beginning of tapping that potential.
~Gypsy
*inheritance through implementation: that as a phrase is just something I made up that can accurately impress upon you what is happening. That is not a thing you should be trying to act like you know what the f you are talking about... lol. There was no inheritance, so my statement is not accurate in reality. In reality, a chunk of Chris was removed and turned into Bob and then brought back into Chris. It is assumed that Chris had brown hair and eyes regardless of Bob.
Comment