Raised Actor's Resurrector/Inflictor pointer?

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.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)

Raised Actor's Resurrector/Inflictor pointer?

Postby Wade Buzzhall » Wed May 18, 2022 9:15 am

Greetings,

I'm trying to write a WorldThingRevived event handler for a raised monster to copy certain properties from the monster that raised them, but unlike Spawning actors, raised monsters don't have a pointer to a Master actor, and I can't seem to find anything in the documentation that would let a raised monster track their Resurrector, unlike WorldThingDied which has also an Inflictor entity besides the Thing entity.

Is there any way to get a pointer to the monster that has resurrected an actor?
Wade Buzzhall
 

Re: Raised Actor's Resurrector/Inflictor pointer?

Postby DragonFlayer » Wed May 18, 2022 9:23 am

Whoops, forgot to login when posting this, replying so I can get notifications
User avatar
DragonFlayer
 
Joined: 02 Apr 2019

Re: Raised Actor's Resurrector/Inflictor pointer?

Postby 22alpha22 » Sun May 22, 2022 8:56 pm

From what I can tell, combing the wiki, gzdoom.pk3, and the source, none of the resurrection functions assign a pointer to the resurrectee that would reference the actor performing the resurrection. It looks like if you want a pointer to the monster performing the resurrection, you will have to write your own resurrection function. I would give an example of the Archvile's function to use as a base but it seems that it has not been scriptified and remains a native hardcoded function.

Here is a native version of the resurrection function, perhaps you might be able to zscriptify it and add a pointer to the monster doing the resurrecting.
Code: Select allExpand view
bool P_CheckForResurrection(AActor* self, bool usevilestates, FState* state = nullptr, FSoundID sound = 0)
{
   const AActor *info;
   AActor *temp;
      
   if (self->movedir != DI_NODIR)
   {
      const double absSpeed = fabs (self->Speed);
      DVector2 viletry = self->Vec2Offset(
         absSpeed * xspeed[self->movedir],
         absSpeed * yspeed[self->movedir], true);

      FPortalGroupArray check(FPortalGroupArray::PGA_Full3d);

      FMultiBlockThingsIterator it(check, self->Level, viletry.X, viletry.Y, self->Z() - 64, self->Top() + 64, 32., false, NULL);
      FMultiBlockThingsIterator::CheckResult cres;
      while (it.Next(&cres))
      {
         AActor *corpsehit = cres.thing;
         FState *raisestate = corpsehit->GetRaiseState();
         if (raisestate != NULL)
         {
            // use the current actor's radius instead of the Arch Vile's default.
            double maxdist = corpsehit->GetDefault()->radius + self->radius;

            if (fabs(corpsehit->X() - cres.Position.X) > maxdist ||
               fabs(corpsehit->Y() - cres.Position.Y) > maxdist)
               continue;         // not actually touching
            // Let's check if there are floors in between the archvile and its target

            if (corpsehit->Sector->PortalGroup != self->Sector->PortalGroup)
            {
               // if in a different section of the map, only consider possible if a line of sight exists.
               if (!P_CheckSight(self, corpsehit))
                  continue;
            }
            else
            {
               sector_t *vilesec = self->Sector;
               sector_t *corpsec = corpsehit->Sector;
               // We only need to test if at least one of the sectors has a 3D floor.
               sector_t *testsec = vilesec->e->XFloor.ffloors.Size() ? vilesec :
                  (vilesec != corpsec && corpsec->e->XFloor.ffloors.Size()) ? corpsec : NULL;
               if (testsec)
               {
                  double zdist1, zdist2;
                  if (P_Find3DFloor(testsec, corpsehit->Pos(), false, true, zdist1)
                     != P_Find3DFloor(testsec, self->Pos(), false, true, zdist2))
                  {
                     // Not on same floor
                     if (vilesec == corpsec || fabs(zdist1 - self->Z()) > self->Height)
                        continue;
                  }
               }
            }

            corpsehit->Vel.X = corpsehit->Vel.Y = 0;
            // [RH] Check against real height and radius

            double oldheight = corpsehit->Height;
            double oldradius = corpsehit->radius;
            ActorFlags oldflags = corpsehit->flags;

            corpsehit->flags |= MF_SOLID;
            corpsehit->Height = corpsehit->GetDefault()->Height;
            bool check = P_CheckPosition(corpsehit, corpsehit->Pos());
            corpsehit->flags = oldflags;
            corpsehit->radius = oldradius;
            corpsehit->Height = oldheight;
            if (!check || !P_CanResurrect(self, corpsehit)) continue;

            // got one!
            temp = self->target;
            self->target = corpsehit;
            A_FaceTarget(self);
            if (self->flags & MF_FRIENDLY)
            {
               // If this is a friendly Arch-Vile (which is turning the resurrected monster into its friend)
               // and the Arch-Vile is currently targetting the resurrected monster the target must be cleared.
               if (self->lastenemy == temp) self->lastenemy = nullptr;
               if (self->lastenemy == corpsehit) self->lastenemy = nullptr;
               if (temp == self->target) temp = NULL;
            }
            self->target = temp;

            // Make the state the monster enters customizable.
            if (state == nullptr) state = self->FindState(NAME_Heal);
            if (state != nullptr)
            {
               self->SetState(state);
            }
            else if (usevilestates)
            {
               // For Dehacked compatibility this has to use the Arch Vile's
               // heal state as a default if the actor doesn't define one itself.
               PClassActor *archvile = PClass::FindActor(NAME_Archvile);
               if (archvile != NULL)
               {
                  self->SetState(archvile->FindState(NAME_Heal));
               }
            }
            if (sound == 0) sound = "vile/raise";
            S_Sound(corpsehit, CHAN_BODY, 0, sound, 1, ATTN_IDLE);
            info = corpsehit->GetDefault();

            if (GetTranslationType(corpsehit->Translation) == TRANSLATION_Blood)
            {
               corpsehit->Translation = info->Translation; // Clean up bloodcolor translation from crushed corpses
            }
            if (self->Level->i_compatflags & COMPATF_VILEGHOSTS)
            {
               corpsehit->Height *= 4;
               // [GZ] This was a commented-out feature, so let's make use of it,
               // but only for ghost monsters so that they are visibly different.
               if (corpsehit->Height == 0)
               {
                  // Make raised corpses look ghostly
                  if (corpsehit->Alpha > 0.5)
                  {
                     corpsehit->Alpha /= 2;
                  }
                  // This will only work if the render style is changed as well.
                  if (corpsehit->RenderStyle == LegacyRenderStyles[STYLE_Normal])
                  {
                     corpsehit->RenderStyle = STYLE_Translucent;
                  }
               }
            }
            else
            {
               corpsehit->Height = info->Height;   // [RH] Use real mobj height
               corpsehit->radius = info->radius;   // [RH] Use real radius
            }

            corpsehit->Revive();

            // You are the Archvile's minion now, so hate what it hates
            corpsehit->CopyFriendliness(self, false);
            corpsehit->SetState(raisestate);

            return true;
         }
      }
   }
   return false;
}
User avatar
22alpha22
So lonely...
 
Joined: 21 Feb 2014
Location: Montana, USA
Operating System: Windows 10/8.1/8/201x 64-bit
Graphics Processor: nVidia with Vulkan support

Re: Raised Actor's Resurrector/Inflictor pointer?

Postby DragonFlayer » Mon May 23, 2022 6:14 am

22alpha22 wrote:From what I can tell, combing the wiki, gzdoom.pk3, and the source, none of the resurrection functions assign a pointer to the resurrectee that would reference the actor performing the resurrection. It looks like if you want a pointer to the monster performing the resurrection, you will have to write your own resurrection function. I would give an example of the Archvile's function to use as a base but it seems that it has not been scriptified and remains a native hardcoded function.

Here is a native version of the resurrection function, perhaps you might be able to zscriptify it and add a pointer to the monster doing the resurrecting.
Code: Select allExpand view
bool P_CheckForResurrection(AActor* self, bool usevilestates, FState* state = nullptr, FSoundID sound = 0)
{
   const AActor *info;
   AActor *temp;
      
   if (self->movedir != DI_NODIR)
   {
      const double absSpeed = fabs (self->Speed);
      DVector2 viletry = self->Vec2Offset(
         absSpeed * xspeed[self->movedir],
         absSpeed * yspeed[self->movedir], true);

      FPortalGroupArray check(FPortalGroupArray::PGA_Full3d);

      FMultiBlockThingsIterator it(check, self->Level, viletry.X, viletry.Y, self->Z() - 64, self->Top() + 64, 32., false, NULL);
      FMultiBlockThingsIterator::CheckResult cres;
      while (it.Next(&cres))
      {
         AActor *corpsehit = cres.thing;
         FState *raisestate = corpsehit->GetRaiseState();
         if (raisestate != NULL)
         {
            // use the current actor's radius instead of the Arch Vile's default.
            double maxdist = corpsehit->GetDefault()->radius + self->radius;

            if (fabs(corpsehit->X() - cres.Position.X) > maxdist ||
               fabs(corpsehit->Y() - cres.Position.Y) > maxdist)
               continue;         // not actually touching
            // Let's check if there are floors in between the archvile and its target

            if (corpsehit->Sector->PortalGroup != self->Sector->PortalGroup)
            {
               // if in a different section of the map, only consider possible if a line of sight exists.
               if (!P_CheckSight(self, corpsehit))
                  continue;
            }
            else
            {
               sector_t *vilesec = self->Sector;
               sector_t *corpsec = corpsehit->Sector;
               // We only need to test if at least one of the sectors has a 3D floor.
               sector_t *testsec = vilesec->e->XFloor.ffloors.Size() ? vilesec :
                  (vilesec != corpsec && corpsec->e->XFloor.ffloors.Size()) ? corpsec : NULL;
               if (testsec)
               {
                  double zdist1, zdist2;
                  if (P_Find3DFloor(testsec, corpsehit->Pos(), false, true, zdist1)
                     != P_Find3DFloor(testsec, self->Pos(), false, true, zdist2))
                  {
                     // Not on same floor
                     if (vilesec == corpsec || fabs(zdist1 - self->Z()) > self->Height)
                        continue;
                  }
               }
            }

            corpsehit->Vel.X = corpsehit->Vel.Y = 0;
            // [RH] Check against real height and radius

            double oldheight = corpsehit->Height;
            double oldradius = corpsehit->radius;
            ActorFlags oldflags = corpsehit->flags;

            corpsehit->flags |= MF_SOLID;
            corpsehit->Height = corpsehit->GetDefault()->Height;
            bool check = P_CheckPosition(corpsehit, corpsehit->Pos());
            corpsehit->flags = oldflags;
            corpsehit->radius = oldradius;
            corpsehit->Height = oldheight;
            if (!check || !P_CanResurrect(self, corpsehit)) continue;

            // got one!
            temp = self->target;
            self->target = corpsehit;
            A_FaceTarget(self);
            if (self->flags & MF_FRIENDLY)
            {
               // If this is a friendly Arch-Vile (which is turning the resurrected monster into its friend)
               // and the Arch-Vile is currently targetting the resurrected monster the target must be cleared.
               if (self->lastenemy == temp) self->lastenemy = nullptr;
               if (self->lastenemy == corpsehit) self->lastenemy = nullptr;
               if (temp == self->target) temp = NULL;
            }
            self->target = temp;

            // Make the state the monster enters customizable.
            if (state == nullptr) state = self->FindState(NAME_Heal);
            if (state != nullptr)
            {
               self->SetState(state);
            }
            else if (usevilestates)
            {
               // For Dehacked compatibility this has to use the Arch Vile's
               // heal state as a default if the actor doesn't define one itself.
               PClassActor *archvile = PClass::FindActor(NAME_Archvile);
               if (archvile != NULL)
               {
                  self->SetState(archvile->FindState(NAME_Heal));
               }
            }
            if (sound == 0) sound = "vile/raise";
            S_Sound(corpsehit, CHAN_BODY, 0, sound, 1, ATTN_IDLE);
            info = corpsehit->GetDefault();

            if (GetTranslationType(corpsehit->Translation) == TRANSLATION_Blood)
            {
               corpsehit->Translation = info->Translation; // Clean up bloodcolor translation from crushed corpses
            }
            if (self->Level->i_compatflags & COMPATF_VILEGHOSTS)
            {
               corpsehit->Height *= 4;
               // [GZ] This was a commented-out feature, so let's make use of it,
               // but only for ghost monsters so that they are visibly different.
               if (corpsehit->Height == 0)
               {
                  // Make raised corpses look ghostly
                  if (corpsehit->Alpha > 0.5)
                  {
                     corpsehit->Alpha /= 2;
                  }
                  // This will only work if the render style is changed as well.
                  if (corpsehit->RenderStyle == LegacyRenderStyles[STYLE_Normal])
                  {
                     corpsehit->RenderStyle = STYLE_Translucent;
                  }
               }
            }
            else
            {
               corpsehit->Height = info->Height;   // [RH] Use real mobj height
               corpsehit->radius = info->radius;   // [RH] Use real radius
            }

            corpsehit->Revive();

            // You are the Archvile's minion now, so hate what it hates
            corpsehit->CopyFriendliness(self, false);
            corpsehit->SetState(raisestate);

            return true;
         }
      }
   }
   return false;
}



Yeah, this is what I was worried of, I guess I'll have to figure something out from it, thank you for pointing me to that function!

I was trying to modify CutmanMike's TwitchyDoom to make both spawned and resurrected monsters inherit the 'friendly' properties of their friendly monster master/resurrector, I was trying to do with WorldThingRevived the same thing I did with WorldThingSpawned, then realized raised enemies aren't even flagged as friendly? somehow they don't attack the player if the Archvile is friendly, while not having the friendly flag themselves.

This is what that looks like and why I'm trying to make raised monsters be aware of their resurrector:

Code: Select allExpand view
Class Friendliness_Handler : EventHandler
{
   override void WorldThingSpawned(WorldEvent e)
   {
      if(!e.Thing.bISMONSTER)
      {
         //Console.Printf("Spawned Actor (" .. e.Thing.GetClassName() ..") is not a Monster");
         //if(e.Thing.Master)
         //{
         //   Console.Printf("Spawned non-Monster Actor (" .. e.Thing.GetClassName() ..") has a Master (" .. e.Thing.Master.GetClassName() ..")");
         //}
         return;
      }
      //Console.Printf("Spawned Actor (" .. e.Thing.GetClassName() .. ") is a Monster");
      Actor mst = e.Thing.Master;
      if(mst)
      {
         //Console.Printf("Spawned Actor (" .. e.Thing.GetClassName() ..") has a Master (" .. mst.GetClassName() ..")");
         if(mst.bFRIENDLY)
         {
            //Console.Printf("Spawned Actor's (" .. e.Thing.GetClassName() ..") Master (" .. mst.GetClassName() ..") is Friendly");
            e.Thing.bFRIENDLY = mst.bFRIENDLY;
            e.Thing.bSHOOTABLE = mst.bSHOOTABLE;
            e.Thing.bNOBLOCKMONST = mst.bNOBLOCKMONST;
            e.Thing.bSOLID = mst.bSOLID;
            e.Thing.target = mst.target;
            e.Thing.lastheard = mst.lastheard;
            if(e.Thing.bCOUNTKILL)
            {
               e.Thing.A_ChangeCountFlags(0);
               e.Thing.bCOUNTKILL = false;
            }
            if(mst.FindInventory("Twitchy_FriendlySpawnProtection"))
            {
               e.Thing.GiveInventory("Twitchy_FriendlySpawnProtection", 1);
            }
            if(mst.FindInventory("Twitchy_FriendlyFireProtection"))
            {
               e.Thing.GiveInventory("Twitchy_FriendlyFireProtection", 1);
            }
            if(mst.FindInventory("Twitchy_FriendlyFollower"))
            {
               e.Thing.GiveInventory("Twitchy_FriendlyFollower", 1);
            }
            if(mst.FindInventory("Twitchy_Username"))
            {
               String username = mst.FindInventory("Twitchy_Username").GetTag();
               Twitchy_Username.Show(e.Thing, username);
            }
            Twitchy_Actor_MapMarker.Show(e.Thing);
         }
      }
   }
User avatar
DragonFlayer
 
Joined: 02 Apr 2019


Return to Scripting

Who is online

Users browsing this forum: No registered users and 0 guests