Spawn() new entity as argument inside function body

Discuss programming in the QuakeC language.
Post Reply
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Spawn() new entity as argument inside function body

Post by toneddu2000 »

Hi guys, I'm trying to do something like this

Code: Select all

void myspawnfunc(entity ent)
{
ent = spawn();
setlocation(ent,[-200,20,200]);
ent.health = 100;
//and so on..
}
Then calling the function with an entity already declared

Code: Select all

entity myshinynewent;

myspawnfunc(myshinynewent);
But it just won't work. If I change the spawning function removeing the spawn()

Code: Select all

void myspawnfunc(entity ent)
{
setlocation(ent,[-200,20,200]);
ent.health = 100;
//and so on..
}
and then spawning the entity OUTSIDE function block

Code: Select all

entity myshinynewent;

myshinynewent = spawn();
myspawnfunc(myshinynewent);
It works. But, of course, I consider it a very ugly way of coding.

I think it's not possible to do it in QuakeC but, why not ask? :)

Thanks in advance

PS: I use FTE but I think it's the same for every engine
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Spawn() new entity as argument inside function body

Post by Spike »

Code: Select all

void(__out entity ent) zomgwtf =
{
ent = spawn();
otherstuff(ent);
};
entity foo;
zomgwtf(foo); //__out means that the argument is actually written to, rather than being a simple copy. use __inout if you want to both read AND write it.
is one way.
note that __inout is basically equivelent to c++'s & prefix on function arguments, except doesn't internally use pointers so it actually works properly in every quake engine.

but of course, this is probably better

Code: Select all

entity() zomgwtf =
{
entity ent = spawn();
otherstuff(ent);
return ent;
}
entity foo = zomgwtf();
if your aim is to retrieve or create something, then its generally best to actually use the return value for that, like the above... it just makes much more sense. the singular exception is if you're returning an error code instead, in combination with an __out.

of course, if it really is a spawn function then you have no arguments and you have no choice but to do:

Code: Select all

void() item_zomgwtf =
{
otherstuff(self);
};
self = spawn();
item_zomgwtf();
you don't really have any other choice in the above case, at least if people are going to be placing 'item_zomgwtf' entities in their favourite map editors. generally its better to reuse code that dupe it.
alternatively you should just make that spawn function a wrapper that just calls realzomgwtf(self); and make a second wrapper of the form: entity()zomgwtf={return realzomgwtf(spawn());}; for your internal use. because that works too.

of course, if it were a class, you could do:

Code: Select all

class zomgwtf
{
void() zomgwtf = //the constructor
{
otherstuff(this);
};
};
zomgwtf foo = spawn(zomgwtf, origin: [-200,2,200], angle_y: 90); //or whatever other fields you want to initialise before the constructor is actually called. hurrah for extra args.
maybe you'll feel that the above is cleaner or neater or something, but its also entirely likely that you want to avoid that syntax like the plague. sometimes you just have to keep things simple.


anyway, the point is that arguments are COPIES of the value. In the case of an entity or function or string or whatever, then its a copy of the REFERENCE to that entity/etc, and not a copy of the entity/etc itself. Exactly the same as an index into some table that the caller has no idea about, because that's exactly what this is. If you change the value of the copy then you've changed the copy, not the original. If you change the data in the table that the copy indexes to then obviously you've also changed the data in the table that the original also indexes to - assuming you didn't change the index.
Its quite simple once you understand what's actually happening.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Spawn() new entity as argument inside function body

Post by toneddu2000 »

Thanks a lot Spike for the detailed answer.
note that __inout is basically equivelent to c++'s & prefix on function arguments, except doesn't internally use pointers so it actually works properly in every quake engine.
I knew it! I knew it some sort of pointers logic was involved!

The class definition is tempting too. I guess my next project will be oop!
anyway, the point is that arguments are COPIES of the value. In the case of an entity or function or string or whatever, then its a copy of the REFERENCE to that entity/etc, and not a copy of the entity/etc itself. Exactly the same as an index into some table that the caller has no idea about, because that's exactly what this is. If you change the value of the copy then you've changed the copy, not the original. If you change the data in the table that the copy indexes to then obviously you've also changed the data in the table that the original also indexes to - assuming you didn't change the index.
Its quite simple once you understand what's actually happening.
Yes, I noticed something like this. I noticed that, if I create a spawning function with optional arguments tied to the entity .field, and I spawn a new entity that DOESN'T fill that argument, the spawned entity will use .field from the previous spawned entity. I just didn't understand the table example, which table? Are you talking about address table which pointers point to?

Thanks Spike for all this knowledge you teach! :biggrin:
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Spawn() new entity as argument inside function body

Post by Spike »

there were no pointers in those examples

Code: Select all

void(entity *ent) zomgwtf =
{
 *ent = spawn();
 ent->foo = 5;
 otherstuff(*ent);
};
entity foo;
zomgwtf(&foo);
there's your pointer example. obviously __out is a bit simpler and works in more engines, so that's why I didn't bother describing pointers.


regarding optional arguments, you should generally not use these in your own code.
specifically, unlike the engine, the qc normally has no way to tell exactly how many arguments were passed, and any arguments which were not passed will have undefined values. if you then pass those undefined values on to the engine, the engine will see those arguments as present, and will then also trip up because their values are undefined.
You can justify using them in the prior arguments indicate whether the later arguments are valid, but the qcc cannot help validate that. As such, its probably better to just remove the optionals and explicitly pass 0s or something.

When I wrote about tables, I was trying to be generic.
I can try and be specific in the case of function tables instead if that would help...
In QC, function references are just references - they are an index into a table of functions defined within the progs.dat
this function table is essentially just an array of structs. one of those fields is the qc instruction index that the function starts at.
so, when you pass a function, you're actually passing an index into that table. and the engine goes and reads that first_instruction field of that table and jumps to that instruction. There's some other stuff in there too, of course, like arguments, but meh.
strings on the other hand, are a byte index into a bit block of chars inside the progs. change the index into the table and you change the string, but you can also cast them to pointers and memcpy data over them, changing the data that those strings refer to without actually changing the string references themselves.
entities work the same way. the entity type is actually just an index. in some engines its a simple index, in others its a byte offset from world. either way you can change the data that's stored at that index by writing all of its fields, which will also affect any other references with the same index, and all without changing the index itself.

so yeah, references/pointers are separate and distinct from the data that they refer to. one is an index, one is a block of memory. you can change the index itself, or you can change the data that the index refers to without changing the index which will of course be visible through all other references/indexes to the same block of memory. in the case of functions that block of memory isn't directly visible, in the case of entities its readable and writable, in the case of strings its normally read-only. either way, the separation between the reference and the data itself is the same.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Spawn() new entity as argument inside function body

Post by toneddu2000 »

there were no pointers in those examples
Yes, of course, my bad
there's your pointer example. obviously __out is a bit simpler and works in more engines, so that's why I didn't bother describing pointers.
Thanks a lot for the pointer example. I'll dig into it!
regarding optional arguments, you should generally not use these in your own code.
specifically, unlike the engine, the qc normally has no way to tell exactly how many arguments were passed, and any arguments which were not passed will have undefined values. if you then pass those undefined values on to the engine, the engine will see those arguments as present, and will then also trip up because their values are undefined.
You can justify using them in the prior arguments indicate whether the later arguments are valid, but the qcc cannot help validate that. As such, its probably better to just remove the optionals and explicitly pass 0s or something.
Thanks, infact I immediately got rid of them! :)
In QC, function references are just references - they are an index into a table of functions defined within the progs.dat
this function table is essentially just an array of structs. one of those fields is the qc instruction index that the function starts at.
so, when you pass a function, you're actually passing an index into that table. and the engine goes and reads that first_instruction field of that table and jumps to that instruction.
Wow! :shock: Thanks a lot for the explanation, I'm starting understanding how engine qc works

A thing that I never understood about qc functions is, when you use a function as an argument of another function, for example void foo(int i, void() whattheheck, float meh), it's impossible (for me, of course)to check, into the function block, if the whattheheck function argument is null. I also tried if(whattheheck == __NULL__) but it doesn't work
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Post Reply