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 all • Expand 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 all • Expand 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);
}
}
}