#include "actor.h" #include "info.h" #include "p_lnspec.h" #include "a_action.h" #include "m_random.h" #include "s_sound.h" #include "d_player.h" #include "p_local.h" #include "p_terrain.h" #include "p_enemy.h" #include "statnums.h" #include "templates.h" static FRandom pr_freezedeath ("FreezeDeath"); static FRandom pr_icesettics ("IceSetTics"); static FRandom pr_freeze ("FreezeDeathChunks"); /***************************** IceChunk ************************************/ class AIceChunk : public AActor { DECLARE_ACTOR (AIceChunk, AActor) }; FState AIceChunk::States[] = { S_NORMAL (ICEC, 'A', 10, NULL , &States[1]), S_NORMAL (ICEC, 'B', 10, A_IceSetTics , &States[2]), S_NORMAL (ICEC, 'C', 10, A_IceSetTics , &States[3]), S_NORMAL (ICEC, 'D', 10, A_IceSetTics , NULL), }; IMPLEMENT_ACTOR (AIceChunk, Any, -1, 0) PROP_RadiusFixed (3) PROP_HeightFixed (4) PROP_Flags (MF_DROPOFF) PROP_Flags2 (MF2_LOGRAV|MF2_CANNOTPUSH|MF2_FLOORCLIP) PROP_SpawnState (0) END_DEFAULTS /***************************************************************************/ // A chunk of ice that is also a player ------------------------------------- class AIceChunkHead : public APlayerChunk { DECLARE_ACTOR (AIceChunkHead, APlayerChunk) }; FState AIceChunkHead::States[] = { S_NORMAL (PLAY, 'A', 0, NULL , &States[1]), S_NORMAL (ICEC, 'A', 10, A_IceCheckHeadDone , &States[1]) }; IMPLEMENT_ACTOR (AIceChunkHead, Any, -1, 0) PROP_RadiusFixed (3) PROP_HeightFixed (4) PROP_DamageType (MOD_ICE) PROP_Flags (MF_DROPOFF) PROP_Flags2 (MF2_LOGRAV|MF2_CANNOTPUSH) PROP_SpawnState (0) END_DEFAULTS //========================================================================== // // A_SetFloorClip // //========================================================================== void A_SetFloorClip (AActor *actor) { actor->flags2 |= MF2_FLOORCLIP; actor->AdjustFloorClip (); } //========================================================================== // // A_UnSetFloorClip // //========================================================================== void A_UnSetFloorClip (AActor *actor) { actor->flags2 &= ~MF2_FLOORCLIP; actor->floorclip = 0; } //========================================================================== // // A_HideThing // //========================================================================== void A_HideThing (AActor *actor) { actor->renderflags |= RF_INVISIBLE; } //========================================================================== // // A_UnHideThing // //========================================================================== void A_UnHideThing (AActor *actor) { actor->renderflags &= ~RF_INVISIBLE; } //============================================================================ // // A_FreezeDeath // //============================================================================ void A_FreezeDeath (AActor *actor) { int t = pr_freezedeath(); actor->tics = 75+t+pr_freezedeath(); actor->flags |= MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_ICECORPSE; actor->flags2 |= MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ|MF2_SLIDE; actor->flags3 |= MF3_CRASHED; actor->height = actor->GetDefault()->height; S_Sound (actor, CHAN_BODY, "misc/freeze", 1, ATTN_NORM); if (actor->player) { actor->player->damagecount = 0; actor->player->poisoncount = 0; actor->player->bonuscount = 0; } else if (actor->flags3&MF3_ISMONSTER && actor->special) { // Initiate monster death actions LineSpecials [actor->special] (NULL, actor, false, actor->args[0], actor->args[1], actor->args[2], actor->args[3], actor->args[4]); actor->special = 0; } } //============================================================================ // // A_IceSetTics // //============================================================================ void A_IceSetTics (AActor *actor) { int floor; actor->tics = 70+(pr_icesettics()&63); floor = P_GetThingFloorType (actor); if (Terrains[floor].DamageMOD == MOD_FIRE) { actor->tics >>= 2; } else if (Terrains[floor].DamageMOD == MOD_ICE) { actor->tics <<= 1; } } //============================================================================ // // A_IceCheckHeadDone // //============================================================================ void A_IceCheckHeadDone (AActor *actor) { if (actor->player == NULL) { actor->Destroy (); } } //============================================================================ // // A_FreezeDeathChunks // //============================================================================ void A_FreezeDeathChunks (AActor *actor) { int i; int numChunks; AActor *mo; if (actor->momx || actor->momy || actor->momz) { actor->tics = 3*TICRATE; return; } S_Sound (actor, CHAN_BODY, "misc/icebreak", 1, ATTN_NORM); // [RH] In Hexen, this creates a random number of shards (range [24,56]) // with no relation to the size of the actor shattering. I think it should // base the number of shards on the size of the dead thing, so bigger // things break up into more shards than smaller things. // An actor with radius 20 and height 64 creates ~40 chunks. numChunks = MAX (4, (actor->radius>>FRACBITS)*(actor->height>>FRACBITS)/32); i = (pr_freeze.Random2()) % (numChunks/4); for (i = MAX (24, numChunks + i); i >= 0; i--) { mo = Spawn ( actor->x + (((pr_freeze()-128)*actor->radius)>>7), actor->y + (((pr_freeze()-128)*actor->radius)>>7), actor->z + (pr_freeze()*actor->height/255)); mo->SetState (mo->SpawnState + (pr_freeze()%3)); if (mo) { mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2; mo->momx = pr_freeze.Random2 () << (FRACBITS-7); mo->momy = pr_freeze.Random2 () << (FRACBITS-7); A_IceSetTics (mo); // set a random tic wait mo->RenderStyle = actor->RenderStyle; mo->alpha = actor->alpha; } } if (actor->player) { // attach the player's view to a chunk of ice AIceChunkHead *head = Spawn (actor->x, actor->y, actor->z+VIEWHEIGHT); head->momz = FixedDiv(head->z-actor->z, actor->height)<<2; head->momx = pr_freeze.Random2 () << (FRACBITS-7); head->momy = pr_freeze.Random2 () << (FRACBITS-7); head->player = actor->player; actor->player = NULL; head->health = actor->health; head->angle = actor->angle; head->player->mo = head; head->pitch = 0; head->RenderStyle = actor->RenderStyle; head->alpha = actor->alpha; if (head->player->camera == actor) { head->player->camera = head; } } // [RH] Do some stuff to make this more useful outside Hexen if (actor->flags4 & MF4_BOSSDEATH) { A_BossDeath (actor); } A_NoBlocking (actor); actor->Destroy (); } //---------------------------------------------------------------------------- // // CorpseQueue Routines (used by Hexen) // //---------------------------------------------------------------------------- // Corpse queue for monsters - this should be saved out #define CORPSEQUEUESIZE 64 class DCorpsePointer : public DThinker { DECLARE_CLASS (DCorpsePointer, DThinker) HAS_OBJECT_POINTERS public: DCorpsePointer (AActor *ptr); void Destroy (); void Serialize (FArchive &arc); AActor *Corpse; DWORD Count; // Only the first corpse pointer's count is valid. private: DCorpsePointer () {} }; IMPLEMENT_POINTY_CLASS(DCorpsePointer) DECLARE_POINTER(Corpse) END_POINTERS DCorpsePointer::DCorpsePointer (AActor *ptr) : DThinker (STAT_CORPSEPOINTER), Corpse (ptr) { Count = 0; // Thinkers are added to the end of their respective lists, so // the first thinker in the list is the oldest one. TThinkerIterator iterator (STAT_CORPSEPOINTER); DCorpsePointer *first = iterator.Next (); if (first != this) { if (first->Count >= CORPSEQUEUESIZE) { DCorpsePointer *next = iterator.Next (); next->Count = first->Count; first->Destroy (); return; } } ++first->Count; } void DCorpsePointer::Destroy () { // Store the count of corpses in the first thinker in the list TThinkerIterator iterator (STAT_CORPSEPOINTER); DCorpsePointer *first = iterator.Next (); int prevCount = first->Count; if (first == this) { first = iterator.Next (); } if (first != NULL) { first->Count = prevCount - 1; } if (Corpse != NULL) { Corpse->Destroy (); } Super::Destroy (); } void DCorpsePointer::Serialize (FArchive &arc) { arc << Corpse << Count; } // Pointers to members cannot be assigned to single array elements, // so this class is deprecated. It exists now only for compatibility with // old savegames. class DCorpseQueue : public DThinker { DECLARE_CLASS (DCorpseQueue, DThinker) HAS_OBJECT_POINTERS public: void Serialize (FArchive &arc); void Tick (); protected: AActor *CorpseQueue[CORPSEQUEUESIZE]; }; IMPLEMENT_CLASS(DCorpseQueue) void DCorpseQueue::Serialize (FArchive &arc) { int foo = 0; int i; Super::Serialize (arc); for (i = 0; i < CORPSEQUEUESIZE; ++i) arc << CorpseQueue[i]; arc << foo; } void DCorpseQueue::Tick () { for (int i = 0; i < CORPSEQUEUESIZE; ++i) { if (CorpseQueue[i] != NULL) { new DCorpsePointer (CorpseQueue[i]); } } Destroy (); } // throw another corpse on the queue void A_QueueCorpse (AActor *actor) { new DCorpsePointer (actor); } // Remove an actor from the queue (for resurrection) void A_DeQueueCorpse (AActor *actor) { TThinkerIterator iterator (STAT_CORPSEPOINTER); DCorpsePointer *corpsePtr; while ((corpsePtr = iterator.Next()) != NULL) { if (corpsePtr->Corpse == actor) { corpsePtr->Corpse = NULL; corpsePtr->Destroy (); return; } } } //============================================================================ // // A_SetInvulnerable // //============================================================================ void A_SetInvulnerable (AActor *actor) { actor->flags2 |= MF2_INVULNERABLE; } //============================================================================ // // A_UnSetInvulnerable // //============================================================================ void A_UnSetInvulnerable (AActor *actor) { actor->flags2 &= ~MF2_INVULNERABLE; } //============================================================================ // // A_SetReflective // //============================================================================ void A_SetReflective (AActor *actor) { actor->flags2 |= MF2_REFLECTIVE; } //============================================================================ // // A_UnSetReflective // //============================================================================ void A_UnSetReflective (AActor *actor) { actor->flags2 &= ~MF2_REFLECTIVE; } //============================================================================ // // A_SetReflectiveInvulnerable // //============================================================================ void A_SetReflectiveInvulnerable (AActor *actor) { actor->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE; } //============================================================================ // // A_UnSetReflectiveInvulnerable // //============================================================================ void A_UnSetReflectiveInvulnerable (AActor *actor) { actor->flags2 &= ~(MF2_REFLECTIVE|MF2_INVULNERABLE); } //========================================================================== // // A_SetShootable // //========================================================================== void A_SetShootable (AActor *actor) { actor->flags2 &= ~MF2_NONSHOOTABLE; actor->flags |= MF_SHOOTABLE; } //========================================================================== // // A_UnSetShootable // //========================================================================== void A_UnSetShootable (AActor *actor) { actor->flags2 |= MF2_NONSHOOTABLE; actor->flags &= ~MF_SHOOTABLE; } //=========================================================================== // // A_NoGravity // //=========================================================================== void A_NoGravity (AActor *actor) { actor->flags |= MF_NOGRAVITY; } //=========================================================================== // // A_Gravity // //=========================================================================== void A_Gravity (AActor *actor) { actor->flags &= ~MF_NOGRAVITY; actor->flags2 &= ~MF2_LOGRAV; } //=========================================================================== // // A_LowGravity // //=========================================================================== void A_LowGravity (AActor *actor) { actor->flags &= ~MF_NOGRAVITY; actor->flags2 |= MF2_LOGRAV; } //=========================================================================== // // FaceMovementDirection // //=========================================================================== void FaceMovementDirection (AActor *actor) { switch (actor->movedir) { case DI_EAST: actor->angle = 0<<24; break; case DI_NORTHEAST: actor->angle = 32<<24; break; case DI_NORTH: actor->angle = 64<<24; break; case DI_NORTHWEST: actor->angle = 96<<24; break; case DI_WEST: actor->angle = 128<<24; break; case DI_SOUTHWEST: actor->angle = 160<<24; break; case DI_SOUTH: actor->angle = 192<<24; break; case DI_SOUTHEAST: actor->angle = 224<<24; break; } } //---------------------------------------------------------------------------- // // PROC A_CheckBurnGone // //---------------------------------------------------------------------------- void A_CheckBurnGone (AActor *actor) { if (actor->player == NULL) { actor->Destroy (); } }