How to deal with weapon action function in scripting

Discuss anything ZDoom-related that doesn't fall into one of the other categories.
User avatar
Graf Zahl
Lead GZDoom+Raze Developer
Lead GZDoom+Raze Developer
Posts: 48320
Joined: Sat Jul 19, 2003 10:19 am
Location: Germany

Re: How to deal with weapon action function in scripting

Post by Graf Zahl »

FishyClockwork wrote:
Graf Zahl wrote:

Code: Select all

if( BFG9000(target.player.ReadyWeapon))
This got me wondering, is load order going to be an issue?

IE is this gonna get me an error?
No. The compiler resolves all constants, classes and global structs before processing the class contents.
User avatar
Leonard2
Posts: 313
Joined: Tue Aug 14, 2012 6:10 pm

Re: How to deal with weapon action function in scripting

Post by Leonard2 »

Graf Zahl wrote:Why not pointers? For the simple reason that they are the biggest can of worms that can be done in a scripting language maybe?
Let's be clear. Pointers are bad - that is, unless you do system programming. They have no place in scripting. By prohibiting them you eliminate a vast amount of potential stability issues right out of the box.
The VM could actually support having an int pointer if it was added to the grammar. The question still is: What would you want to do with it? If you look at the internal classes you'll see that objects very, very rarely point to random unorganized data. This can not be serialized so it is basically broken by design.
You didn't completely get my point.
I can already have and use pointers, only that instead of using the nice star syntax, I need to define a class type that holds the data that I want to point to.
That means that if I want a pointer to an int, I have to create a specific class that holds just an int and use that instead.
If I make any sort of struct that holds any specific data and I want a pointer to it, I would need to duplicate its definition but make it a class-type instead.
This is just madness. That means I would need to have a duplicate of every type defined as a class if I want to have pointers to them.
My code would just be endlessly spammed. A struct version for proper objects and a class version for references.
This is exactly the kind of stupid limitation that made people hate DECORATE by the way.
Graf Zahl wrote:A weapon can run non-weapon functions, and for those the player has to be 'self'. If the semantics are changed to make the weapon 'self' for special weapon functions and the player for everything else it becomes far too confusing. I think the little rule breaking with self is preferable in such a case, at least the semantics are the same.
No that's not what I meant either.
I apologize if I'm just terrible at explaining things.
Let's go back to your original post:
Graf Zahl wrote:Internally that function above gets defined as

Code: Select all

void A_MyCoolWeaponAttack(MyCoolWeapon self, Actor caller, StateInfo stateinfo, class<Actor> projectile)
And it HAS to be defined like that, if it was used from inside the weapon's spawn state, so that it is possible to access the class fields (e.g. mycoolvariable)
Good. That's perfect. That means that I can indeed properly use "mycoolvariable" in my action function code without needing to write out "invoker.mycoolvariable".
Graf Zahl wrote:My first idea was to use the second parameter, formerly 'stateowner' as self and rename the actual 'self' to 'caller'.
Let's stop for a moment and remember: you said yourself that only the classes inheriting from "StateProvider" will be able to define states that can be used by other classes.
That means that all the other classes will always properly call their states by themselves and only themselves which means that self is in fact stateowner itself (this is the reasoning that I used when I made the assignment operators fix recently when used in weapons).
This is still good. That means that I can properly use "mycoolvariable" in my action functions without needing to write out "invoker.mycoolvariable".
This is important because when I write my code, I'm absolutely sure that the variable that I declared can be properly used by the owning class itself.
Graf Zahl wrote:But when called from a weapon state this will fail, because essentially it will get called like

Code: Select all

   A_MyCoolWeaponAttack(player, self, stateinfo, "MyCoolProjectile");
which creates a type mismatch between 'player' and 'MyCoolWeapon'.
At this point you come up with the "StateProvider" solution.
The action function will be declared such that I still have access to the weapon itself via invoker (which means invoker is in fact stateowner).
Graf Zahl wrote:Normally, 'self' really should be the weapon, but that'd cause so many problems that it's not an option, because it'd be incompatible with anonymous DECORATE functions, which get executed in the same context. This is 10 years of baggage that cannot be undone.
[...somewhere else...]
But this causes other problems that, for example, calling A_Die from a weapon state, would try to kill the weapon, not the player.
So as to remain compatible with decorate, internally the pointers arguments are passed in the same order as before (a0, a1 and a2 in the VM).
We do not change that fact in any way so that calling A_Die in decorate from a weapon state will not try to kill the weapon but the player.

Now this is what I'm asking: while still preserving the internal pointers in order to stay compatible with decorate, when I write "mycoolvariable" in my "StateProvider" class's code in zscript, I want the compiler to process it as "invoker.mycoolvariable".
This is what I was referring to by talking about consistency. We keep the use of stateowner for member access just like in every other classes so that just like in every other classes I can refer to its members by writing them out normally.
And if I want to call A_Die on the player, I can simply use "caller.A_Die()" instead.
This way, everything is consistent for a modder. My code would behave exactly like any other code from any other class that wouldn't inherit from "StateProvider".
I don't see how that would cause any confusion.
Excuse me if I was still not clear enough this time, feel free to say so.
User avatar
Graf Zahl
Lead GZDoom+Raze Developer
Lead GZDoom+Raze Developer
Posts: 48320
Joined: Sat Jul 19, 2003 10:19 am
Location: Germany

Re: How to deal with weapon action function in scripting

Post by Graf Zahl »

Leonard2 wrote:
Graf Zahl wrote:Why not pointers? For the simple reason that they are the biggest can of worms that can be done in a scripting language maybe?
Let's be clear. Pointers are bad - that is, unless you do system programming. They have no place in scripting. By prohibiting them you eliminate a vast amount of potential stability issues right out of the box.
The VM could actually support having an int pointer if it was added to the grammar. The question still is: What would you want to do with it? If you look at the internal classes you'll see that objects very, very rarely point to random unorganized data. This can not be serialized so it is basically broken by design.
You didn't completely get my point.
I can already have and use pointers, only that instead of using the nice star syntax, I need to define a class type that holds the data that I want to point to.
That means that if I want a pointer to an int, I have to create a specific class that holds just an int and use that instead.
If I make any sort of struct that holds any specific data and I want a pointer to it, I would need to duplicate its definition but make it a class-type instead.
This is just madness. That means I would need to have a duplicate of every type defined as a class if I want to have pointers to them.
My code would just be endlessly spammed. A struct version for proper objects and a class version for references.
This is exactly the kind of stupid limitation that made people hate DECORATE by the way.
What's the obsession with pointers? Even in the C-code you cannot use pointers that way because they have no serializable state, i.e. they never survive a savegame. If you want several actors point to a single counter variable you have to think up something different that actually CAN be serialized.
Having pointer variables makes sense in very few contexts, e.g. passing a struct to a function as a parameter will be done with a reference, as will variables that get marked as 'out'. But storing a pointer as a member in a class is a complete no-go. That has nothing to do with syntax choice but with fundamental engine design. It just cannot work.

And your entire class/struct madness makes no sense as well. If you define a struct it isn't a class and a class isn't a struct. You cannot mix them as they are completely different entities. A struct is just a collection of data, while an object (i.e. a class instance) has a lot of semantics attached to it.
User avatar
Leonard2
Posts: 313
Joined: Tue Aug 14, 2012 6:10 pm

Re: How to deal with weapon action function in scripting

Post by Leonard2 »

Graf Zahl wrote:as will variables that get marked as 'out'.
Marked as out?
That sounds just as confusing as that struct/class difference.
I'm sorry if I may sound like an always-complaining asshole to you (your first statement seems to indicate that) but I'm only trying to give my opinion and nothing more really..
Graf Zahl wrote:And your entire class/struct madness makes no sense as well
Here:
randi wrote:All structs are values, and all classes are references.
Graf Zahl wrote:A struct is just a collection of data, while an object (i.e. a class instance) has a lot of semantics attached to it.
Well what's the difference between

Code: Select all

class myint
{
	int var;
}
and

Code: Select all

struct myint
{
	int var;
}
Graf Zahl wrote:Even in the C-code you cannot use pointers that way because they have no serializable state, i.e. they never survive a savegame. If you want several actors point to a single counter variable you have to think up something different that actually CAN be serialized.
Well as randi said classes are already references so how are those going to be serialized?
User avatar
Graf Zahl
Lead GZDoom+Raze Developer
Lead GZDoom+Raze Developer
Posts: 48320
Joined: Sat Jul 19, 2003 10:19 am
Location: Germany

Re: How to deal with weapon action function in scripting

Post by Graf Zahl »

Leonard2 wrote:
Graf Zahl wrote:A struct is just a collection of data, while an object (i.e. a class instance) has a lot of semantics attached to it.
Well what's the difference between

Code: Select all

class myint
{
	int var;
}
and

Code: Select all

struct myint
{
	int var;
}
The class inherits from DObject. The struct is just data.

Graf Zahl wrote:Even in the C-code you cannot use pointers that way because they have no serializable state, i.e. they never survive a savegame. If you want several actors point to a single counter variable you have to think up something different that actually CAN be serialized.
Well as randi said classes are already references so how are those going to be serialized?
Objects are stored as indices into an object table. Of course, to be able to do that it has to inherit from DObject.
The process works as follows:

- game tries to save a pointer to an object.
- serializer looks if pointer was stored already.
- if no, pointer is stored in the object table, then index is written
- if yes, index of previous store is written.

At the end of the savegame:
- object table is written - this can add new objects to the table.

When loading the whole thing works in reverse. First all objects are created empty, then everything is read back.

Return to “General”