mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-23 12:32:34 +00:00
fixed some issues with reviving monsters
- fixed: Thing_Raise didn't properly set the spawn health. - fixed: Thing_Raise did not the CanRaise state flag. - fixed: Reviving a monster must also reset the damage type. - fixed: Thing_Raise reset the actor after calling the raise state, but it should be before, just as the Archvile code is doing. - consolidated some common code of Thing_Raise and Archvile resurrection into AActor methods.
This commit is contained in:
parent
a9f1b54d2d
commit
93aa1ea2c4
4 changed files with 152 additions and 151 deletions
|
@ -1011,6 +1011,8 @@ public:
|
|||
bool isSlow();
|
||||
void SetIdle();
|
||||
void ClearCounters();
|
||||
FState *GetRaiseState();
|
||||
void Revive();
|
||||
|
||||
FState *FindState (FName label) const
|
||||
{
|
||||
|
|
222
src/p_enemy.cpp
222
src/p_enemy.cpp
|
@ -2525,146 +2525,122 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates)
|
|||
FBlockThingsIterator it(FBoundingBox(viletryx, viletryy, 32*FRACUNIT));
|
||||
while ((corpsehit = it.Next()))
|
||||
{
|
||||
if (!(corpsehit->flags & MF_CORPSE) )
|
||||
continue; // not a monster
|
||||
|
||||
if (corpsehit->tics != -1 && // not lying still yet
|
||||
!corpsehit->state->GetCanRaise()) // or not ready to be raised yet
|
||||
continue;
|
||||
|
||||
raisestate = corpsehit->FindState(NAME_Raise);
|
||||
if (raisestate == NULL)
|
||||
continue; // monster doesn't have a raise state
|
||||
|
||||
if (corpsehit->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
|
||||
continue; // do not resurrect players
|
||||
|
||||
// use the current actor's radius instead of the Arch Vile's default.
|
||||
fixed_t maxdist = corpsehit->GetDefault()->radius + self->radius;
|
||||
|
||||
maxdist = corpsehit-> GetDefault()->radius + self->radius;
|
||||
|
||||
if ( abs(corpsehit-> x - viletryx) > maxdist ||
|
||||
abs(corpsehit-> y - viletryy) > maxdist )
|
||||
continue; // not actually touching
|
||||
#ifdef _3DFLOORS
|
||||
// Let's check if there are floors in between the archvile and its target
|
||||
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)
|
||||
FState *raisestate = corpsehit->GetRaiseState();
|
||||
if (raisestate != NULL)
|
||||
{
|
||||
fixed_t zdist1, zdist2;
|
||||
if (P_Find3DFloor(testsec, corpsehit->x, corpsehit->y, corpsehit->z, false, true, zdist1)
|
||||
!= P_Find3DFloor(testsec, self->x, self->y, self->z, false, true, zdist2))
|
||||
// use the current actor's radius instead of the Arch Vile's default.
|
||||
fixed_t maxdist = corpsehit->GetDefault()->radius + self->radius;
|
||||
|
||||
maxdist = corpsehit->GetDefault()->radius + self->radius;
|
||||
|
||||
if (abs(corpsehit->x - viletryx) > maxdist ||
|
||||
abs(corpsehit->y - viletryy) > maxdist)
|
||||
continue; // not actually touching
|
||||
#ifdef _3DFLOORS
|
||||
// Let's check if there are floors in between the archvile and its target
|
||||
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)
|
||||
{
|
||||
// Not on same floor
|
||||
if (vilesec == corpsec || abs(zdist1 - self->z) > self->height)
|
||||
fixed_t zdist1, zdist2;
|
||||
if (P_Find3DFloor(testsec, corpsehit->x, corpsehit->y, corpsehit->z, false, true, zdist1)
|
||||
!= P_Find3DFloor(testsec, self->x, self->y, self->z, false, true, zdist2))
|
||||
{
|
||||
// Not on same floor
|
||||
if (vilesec == corpsec || abs(zdist1 - self->z) > self->height)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
corpsehit->velx = corpsehit->vely = 0;
|
||||
// [RH] Check against real height and radius
|
||||
corpsehit->velx = corpsehit->vely = 0;
|
||||
// [RH] Check against real height and radius
|
||||
|
||||
fixed_t oldheight = corpsehit->height;
|
||||
fixed_t oldradius = corpsehit->radius;
|
||||
int oldflags = corpsehit->flags;
|
||||
fixed_t oldheight = corpsehit->height;
|
||||
fixed_t oldradius = corpsehit->radius;
|
||||
int oldflags = corpsehit->flags;
|
||||
|
||||
corpsehit->flags |= MF_SOLID;
|
||||
corpsehit->height = corpsehit->GetDefault()->height;
|
||||
bool check = P_CheckPosition (corpsehit, corpsehit->x, corpsehit->y);
|
||||
corpsehit->flags = oldflags;
|
||||
corpsehit->radius = oldradius;
|
||||
corpsehit->height = oldheight;
|
||||
if (!check) continue;
|
||||
corpsehit->flags |= MF_SOLID;
|
||||
corpsehit->height = corpsehit->GetDefault()->height;
|
||||
bool check = P_CheckPosition(corpsehit, corpsehit->x, corpsehit->y);
|
||||
corpsehit->flags = oldflags;
|
||||
corpsehit->radius = oldradius;
|
||||
corpsehit->height = oldheight;
|
||||
if (!check) 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 = NULL;
|
||||
if (self->lastenemy == corpsehit) self->lastenemy = NULL;
|
||||
if (temp == self->target) temp = NULL;
|
||||
}
|
||||
self->target = temp;
|
||||
|
||||
// Make the state the monster enters customizable.
|
||||
FState * state = self->FindState(NAME_Heal);
|
||||
if (state != NULL)
|
||||
{
|
||||
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.
|
||||
const PClass *archvile = PClass::FindClass("Archvile");
|
||||
if (archvile != NULL)
|
||||
// got one!
|
||||
temp = self->target;
|
||||
self->target = corpsehit;
|
||||
A_FaceTarget(self);
|
||||
if (self->flags & MF_FRIENDLY)
|
||||
{
|
||||
self->SetState (archvile->ActorInfo->FindState(NAME_Heal));
|
||||
// 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 = NULL;
|
||||
if (self->lastenemy == corpsehit) self->lastenemy = NULL;
|
||||
if (temp == self->target) temp = NULL;
|
||||
}
|
||||
}
|
||||
S_Sound (corpsehit, CHAN_BODY, "vile/raise", 1, ATTN_IDLE);
|
||||
info = corpsehit->GetDefault ();
|
||||
|
||||
if (corpsehit->state == corpsehit->FindState(NAME_GenericCrush))
|
||||
{
|
||||
corpsehit->Translation = info->Translation; // Clean up bloodcolor translation from crushed corpses
|
||||
}
|
||||
if (ib_compatflags & BCOMPATF_VILEGHOSTS)
|
||||
{
|
||||
corpsehit->height <<= 2;
|
||||
// [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)
|
||||
self->target = temp;
|
||||
|
||||
// Make the state the monster enters customizable.
|
||||
FState * state = self->FindState(NAME_Heal);
|
||||
if (state != NULL)
|
||||
{
|
||||
// Make raised corpses look ghostly
|
||||
if (corpsehit->alpha > TRANSLUC50)
|
||||
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.
|
||||
const PClass *archvile = PClass::FindClass("Archvile");
|
||||
if (archvile != NULL)
|
||||
{
|
||||
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;
|
||||
self->SetState(archvile->ActorInfo->FindState(NAME_Heal));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
corpsehit->height = info->height; // [RH] Use real mobj height
|
||||
corpsehit->radius = info->radius; // [RH] Use real radius
|
||||
}
|
||||
corpsehit->flags = info->flags;
|
||||
corpsehit->flags2 = info->flags2;
|
||||
corpsehit->flags3 = info->flags3;
|
||||
corpsehit->flags4 = info->flags4;
|
||||
corpsehit->flags5 = info->flags5;
|
||||
corpsehit->flags6 = info->flags6;
|
||||
corpsehit->flags7 = info->flags7;
|
||||
corpsehit->health = corpsehit->SpawnHealth();
|
||||
corpsehit->target = NULL;
|
||||
corpsehit->lastenemy = NULL;
|
||||
S_Sound(corpsehit, CHAN_BODY, "vile/raise", 1, ATTN_IDLE);
|
||||
info = corpsehit->GetDefault();
|
||||
|
||||
// [RH] If it's a monster, it gets to count as another kill
|
||||
if (corpsehit->CountsAsKill())
|
||||
{
|
||||
level.total_monsters++;
|
||||
if (corpsehit->state == corpsehit->FindState(NAME_GenericCrush))
|
||||
{
|
||||
corpsehit->Translation = info->Translation; // Clean up bloodcolor translation from crushed corpses
|
||||
}
|
||||
if (ib_compatflags & BCOMPATF_VILEGHOSTS)
|
||||
{
|
||||
corpsehit->height <<= 2;
|
||||
// [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 > TRANSLUC50)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// You are the Archvile's minion now, so hate what it hates
|
||||
corpsehit->CopyFriendliness (self, false);
|
||||
corpsehit->SetState (raisestate);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -6074,6 +6074,49 @@ int AActor::SpawnHealth()
|
|||
}
|
||||
}
|
||||
|
||||
FState *AActor::GetRaiseState()
|
||||
{
|
||||
if (!(flags & MF_CORPSE))
|
||||
{
|
||||
return NULL; // not a monster
|
||||
}
|
||||
|
||||
if (tics != -1 && // not lying still yet
|
||||
state->GetCanRaise()) // or not ready to be raised yet
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IsKindOf(RUNTIME_CLASS(APlayerPawn)))
|
||||
{
|
||||
return NULL; // do not resurrect players
|
||||
}
|
||||
|
||||
return FindState(NAME_Raise);
|
||||
}
|
||||
|
||||
void AActor::Revive()
|
||||
{
|
||||
AActor *info = GetDefault();
|
||||
flags = info->flags;
|
||||
flags2 = info->flags2;
|
||||
flags3 = info->flags3;
|
||||
flags4 = info->flags4;
|
||||
flags5 = info->flags5;
|
||||
flags6 = info->flags6;
|
||||
flags7 = info->flags7;
|
||||
DamageType = info->DamageType;
|
||||
health = SpawnHealth();
|
||||
target = NULL;
|
||||
lastenemy = NULL;
|
||||
|
||||
// [RH] If it's a monster, it gets to count as another kill
|
||||
if (CountsAsKill())
|
||||
{
|
||||
level.total_monsters++;
|
||||
}
|
||||
}
|
||||
|
||||
FDropItem *AActor::GetDropItems()
|
||||
{
|
||||
unsigned int index = GetClass()->Meta.GetMetaInt (ACMETA_DropItems) - 1;
|
||||
|
|
|
@ -410,18 +410,11 @@ void P_RemoveThing(AActor * actor)
|
|||
|
||||
bool P_Thing_Raise(AActor *thing)
|
||||
{
|
||||
if (thing == NULL)
|
||||
return false; // not valid
|
||||
|
||||
if (!(thing->flags & MF_CORPSE) )
|
||||
return true; // not a corpse
|
||||
|
||||
if (thing->tics != -1)
|
||||
return true; // not lying still yet
|
||||
|
||||
FState * RaiseState = thing->FindState(NAME_Raise);
|
||||
FState * RaiseState = thing->GetRaiseState();
|
||||
if (RaiseState == NULL)
|
||||
{
|
||||
return true; // monster doesn't have a raise state
|
||||
}
|
||||
|
||||
AActor *info = thing->GetDefault ();
|
||||
|
||||
|
@ -443,25 +436,12 @@ bool P_Thing_Raise(AActor *thing)
|
|||
return false;
|
||||
}
|
||||
|
||||
S_Sound (thing, CHAN_BODY, "vile/raise", 1, ATTN_IDLE);
|
||||
|
||||
thing->SetState (RaiseState);
|
||||
thing->flags = info->flags;
|
||||
thing->flags2 = info->flags2;
|
||||
thing->flags3 = info->flags3;
|
||||
thing->flags4 = info->flags4;
|
||||
thing->flags5 = info->flags5;
|
||||
thing->flags6 = info->flags6;
|
||||
thing->flags7 = info->flags7;
|
||||
thing->health = info->health;
|
||||
thing->target = NULL;
|
||||
thing->lastenemy = NULL;
|
||||
|
||||
// [RH] If it's a monster, it gets to count as another kill
|
||||
if (thing->CountsAsKill())
|
||||
{
|
||||
level.total_monsters++;
|
||||
}
|
||||
S_Sound (thing, CHAN_BODY, "vile/raise", 1, ATTN_IDLE);
|
||||
|
||||
thing->Revive();
|
||||
|
||||
thing->SetState (RaiseState);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue