diff --git a/src/g_heretic/a_dsparil.cpp b/src/g_heretic/a_dsparil.cpp index b6a8c6a1e..aa0bd46ed 100644 --- a/src/g_heretic/a_dsparil.cpp +++ b/src/g_heretic/a_dsparil.cpp @@ -139,7 +139,7 @@ void P_DSparilTeleport (AActor *actor) DSpotState *state = DSpotState::GetSpotState(); if (state == NULL) return; - spot = state->GetSpotWithMinDistance(PClass::FindClass("BossSpot"), actor->x, actor->y, 128*FRACUNIT); + spot = state->GetSpotWithMinMaxDistance(PClass::FindClass("BossSpot"), actor->x, actor->y, 128*FRACUNIT, 0); if (spot == NULL) return; prevX = actor->x; diff --git a/src/g_shared/a_specialspot.cpp b/src/g_shared/a_specialspot.cpp index dbf2cffca..44ee6b848 100644 --- a/src/g_shared/a_specialspot.cpp +++ b/src/g_shared/a_specialspot.cpp @@ -147,14 +147,20 @@ struct FSpotList // //---------------------------------------------------------------------------- - ASpecialSpot *GetSpotWithMinDistance(fixed_t x, fixed_t y, fixed_t distance) + ASpecialSpot *GetSpotWithMinMaxDistance(fixed_t x, fixed_t y, fixed_t mindist, fixed_t maxdist) { if (Spots.Size() == 0) return NULL; int i = pr_spot() % Spots.Size(); int initial = i; - while (P_AproxDistance(Spots[i]->x - x, Spots[i]->y - y) < distance) + fixed_t distance; + + while (true) { + distance = P_AproxDistance(Spots[i]->x - x, Spots[i]->y - y); + + if ((distance >= mindist) && ((maxdist == 0) || (distance <= maxdist))) break; + i = (i+1) % Spots.Size(); if (i == initial) return NULL; } @@ -329,10 +335,10 @@ ASpecialSpot *DSpotState::GetNextInList(const PClass *type, int skipcounter) // //---------------------------------------------------------------------------- -ASpecialSpot *DSpotState::GetSpotWithMinDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t distance) +ASpecialSpot *DSpotState::GetSpotWithMinMaxDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t mindist, fixed_t maxdist) { FSpotList *list = FindSpotList(type); - if (list != NULL) return list->GetSpotWithMinDistance(x, y, distance); + if (list != NULL) return list->GetSpotWithMinMaxDistance(x, y, mindist, maxdist); return NULL; } diff --git a/src/g_shared/a_specialspot.h b/src/g_shared/a_specialspot.h index ffc083978..21d90f73f 100644 --- a/src/g_shared/a_specialspot.h +++ b/src/g_shared/a_specialspot.h @@ -36,7 +36,7 @@ public: bool RemoveSpot(ASpecialSpot *spot); void Serialize(FArchive &arc); ASpecialSpot *GetNextInList(const PClass *type, int skipcounter); - ASpecialSpot *GetSpotWithMinDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t distance); + ASpecialSpot *GetSpotWithMinMaxDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t mindist, fixed_t maxdist); ASpecialSpot *GetRandomSpot(const PClass *type, bool onlyonce = false); }; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a4977eaa6..406d8d759 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -66,6 +66,7 @@ #include "v_font.h" #include "doomstat.h" #include "v_palette.h" +#include "g_shared/a_specialspot.h" static FRandom pr_camissile ("CustomActorfire"); @@ -81,6 +82,7 @@ static FRandom pr_spawndebris ("SpawnDebris"); static FRandom pr_spawnitemex ("SpawnItemEx"); static FRandom pr_burst ("Burst"); static FRandom pr_monsterrefire ("MonsterRefire"); +static FRandom pr_teleport("Teleport"); //========================================================================== @@ -3194,6 +3196,87 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; } +//=========================================================================== +// +// A_Teleport(optional state teleportstate, optional class targettype, +// optional class fogtype, optional int flags, optional fixed mindist, +// optional fixed maxdist) +// +// Attempts to teleport to a targettype at least mindist away and at most +// maxdist away (0 means unlimited). If successful, spawn a fogtype at old +// location and place calling actor in teleportstate. +// +//=========================================================================== +enum T_Flags +{ + TF_TELEFRAG = 1, // Allow telefrag in order to teleport. + TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide) +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_STATE(TeleportState, 0); + ACTION_PARAM_CLASS(TargetType, 1); + ACTION_PARAM_CLASS(FogType, 2); + ACTION_PARAM_INT(Flags, 3); + ACTION_PARAM_FIXED(MinDist, 4); + ACTION_PARAM_FIXED(MaxDist, 5); + + // Randomly choose not to teleport like A_Srcr2Decide. + if (Flags & TF_RANDOMDECIDE) + { + static const int chance[] = + { + 192, 120, 120, 120, 64, 64, 32, 16, 0 + }; + + unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); + + if (chanceindex >= countof(chance)) + { + chanceindex = countof(chance) - 1; + } + + if (pr_teleport() >= chance[chanceindex]) return; + } + + if (TeleportState == NULL) + { + // Default to Teleport. + TeleportState = self->FindState("Teleport"); + // If still nothing, then return. + if (!TeleportState) return; + } + + DSpotState *state = DSpotState::GetSpotState(); + if (state == NULL) return; + + if (!TargetType) TargetType = PClass::FindClass("BossSpot"); + + AActor * spot = state->GetSpotWithMinMaxDistance(TargetType, self->x, self->y, MinDist, MaxDist); + if (spot == NULL) return; + + fixed_t prevX = self->x; + fixed_t prevY = self->y; + fixed_t prevZ = self->z; + if (P_TeleportMove (self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (FogType) + { + Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); + } + + ACTION_JUMP(TeleportState); + + self->z = self->floorz; + self->angle = spot->angle; + self->velx = self->vely = self->velz = 0; + } +} + //=========================================================================== // // A_Turn diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index ec6e0cea0..b6ef92265 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -224,6 +224,7 @@ ACTOR Actor native //: Thinker action native A_CheckCeiling(state label); action native A_PlayerSkinCheck(state label); action native A_BasicAttack(int meleedamage, sound meleesound, class missiletype, float missileheight); + action native A_Teleport(state teleportstate = "", class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, float mindist = 128, float maxdist = 0); action native A_ThrowGrenade(class itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true); action native A_Weave(int xspeed, int yspeed, float xdist, float ydist); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 6c027d82a..f03ad34a6 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -95,6 +95,10 @@ const int CPF_USEAMMO = 1; const int CPF_DAGGER = 2; const int CPF_PULLIN = 4; +// Flags for A_Teleport +const int TF_TELEFRAG = 1; +const int TF_RANDOMDECIDE = 2; + // Activation flags enum {