Virtual/Action A_FireProjectile doesn't exist on owner

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Virtual/Action A_FireProjectile doesn't exist on owner

Postby peewee_RotA » Sat Jul 31, 2021 4:33 am

I have a weapon base that uses some virtual methods to select what to fire based on some states of the player. This weapon calls an Action from one of its weapon states, which then calls one of a half dozen Virtual methods based on some logic. Those virtual methods have Overrides in the inheriting class. So essentially I have an Action calling Overrides in a child class from weapon states. The problem is that in that virtual method, the owner doesn't have a A_FireProjectile method but it does have SpawnPlayerMissile which can do the same thing.

There are a few known quirks here, so I'll explain them as I understand them:

A) Action methods cannot be Virtual and Virtual methods cannot be Actions. I think this is because of pointer scope as described in...

B) When an action is called from a weapon frame, the self pointer becomes the player, and the methods are technically run from the player.

C) From within an Action, In order to call a method that is NOT an action and not a method on the Player, you can use "invoker.MyMethodName()" to call it from the weapon.
Note: This is the typical solution to calling a virtual method from within an action.

D) When invoker.MyMethodName() is used from an action, "owner" gets you back to the player and is needed to call any player method that you would call normally without using an object pointer.

All that being said, you can call "A_FireProjectile" from an Action method in a weapon, which assumes that the self is the player. However, when getting into a virtual method using "invoker.MyMethodName()" the owner.player does not have a method named "A_FireProjectile". Attempting to call it will cause an error.

Since there are some weird pointer situations I also tried owner.A_FireProjectile and owner.player.A_FireProjectile and even owner.player.mo.A_FireProjectile. Calling it without a pointer doesn't throw an error but also does nothing. Since owner.SpawnPlayerMissile() works.

Expand to see code samples:
Spoiler:


Since this works I'm going to continue the pattern, but I was curious if there was an explanation to why the method is available in Actions but not if I call it from the actual player object.

Thanks in advance!
peewee_RotA
 
Joined: 07 Feb 2014

Re: Virtual/Action A_FireProjectile doesn't exist on owner

Postby Graf Zahl » Sat Jul 31, 2021 4:40 am

It's because the calling convention for 'action' functions from weapon frames is different. They are not part of Actor, but of Weapon. And in order to call them properly the invoking function needs to be marked 'action' as well so that it can forward the needed state. A virtual cannot do that.

This is all a relic of some very old decision to make actor code pointers work for weapons in Dehacked that was made almost 20 years ago and messed up two calling interfaces that should not have been mixed in the first place.
User avatar
Graf Zahl
Lead GZDoom+Raze Developer
Lead GZDoom+Raze Developer
 
Joined: 19 Jul 2003
Location: Germany

Re: Virtual/Action A_FireProjectile doesn't exist on owner

Postby peewee_RotA » Sat Jul 31, 2021 4:47 am

Thanks for the explanation!

So I think what I missed is that A_FireProjectile is an action and SpawnPlayerMissile is just a method on the Player object.
peewee_RotA
 
Joined: 07 Feb 2014

Re: Virtual/Action A_FireProjectile doesn't exist on owner

Postby Graf Zahl » Sat Jul 31, 2021 5:06 am

Yes. SpawnPlayerMissile is the low level worker that's getting called by A_FireProjectile and lots of other functions. It got less features as a result but since it does not depend on the interface it is easier to call.
User avatar
Graf Zahl
Lead GZDoom+Raze Developer
Lead GZDoom+Raze Developer
 
Joined: 19 Jul 2003
Location: Germany

Re: Virtual/Action A_FireProjectile doesn't exist on owner

Postby peewee_RotA » Mon Aug 09, 2021 8:41 am

As a followup, the majority for what I needed to do worked when using invoker to call a virtual method. Once you get to the virtual method, owner is your player and most actions can be taken there.

The problem is that out of about literally 36 methods, I had 1 that needed to set a state that called another action. Once the virtual method is called from the invoker, it looks like the self pointer stack is broken. For example if you then set the state and run another action, player.mo becomes null.

So in order to support new state labels with additional actions on the stack, I've decided to try "overriding" in a round about way.

The base weapon now contains an action that then sets state. Those states then can contain actions. What this means is that the behavior can be overridden on all child class weapons simply by defining that state label.
Code: Select allExpand view
class BaseWeapon : Weapon
{
   action void A_SetWeapState(StateLabel stateName)
   {
      player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.FindState(stateName)); //Creating a simple method for setting state by player sprite method. Setting via "setstatelabel" seems to have issues deep in the action callstack.
   }

   action void A_FireSpell()
   {
      if (player == null)
         return;

           let magePlayer = XRpgMagePlayer(player.mo);
           if (magePlayer && magePlayer.ActiveSpell)
           {
         switch (magePlayer.ActiveSpell.SpellType)
         {
         case SPELLTYPE_FIRE:
            A_SetWeapState("FlameSpell");
            break;
         case SPELLTYPE_ICE:
            A_SetWeapState("IceSpell");
            break;
         case SPELLTYPE_POISON:
            A_SetWeapState("PoisonSpell");
            break;
         }
           }
   }
}


The downside... all child class weapons HAVE to define those states. But the easy solution is to just add all of the states above
Code: Select allExpand view
AltFire:
   CONE B 3;
   CONE C 4;
   CONE D 3;
   CONE E 5;
   CONE F 1 A_FireSpell();
IceSpell:
PoisonSpell: //These have to be defined. Doing this will just fall through to the end of the AltFireFinish frames
AltFireFinish:
   CONE G 3;
   CONE A 9;
   CONE A 10 A_ReFire;
   Goto Ready;
FlameSpell:
   CONE F 2 Bright A_FireFlameSpell; //Call some custom action to shoot a flame missile.
   Goto AltFireFinish;
peewee_RotA
 
Joined: 07 Feb 2014


Return to Scripting

Who is online

Users browsing this forum: No registered users and 0 guests