mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-03-30 22:51:01 +00:00
Reverted the path node system
Code reviews were unfavorable so better nix it before it finds wider use.
This commit is contained in:
parent
c9e678b60e
commit
520b960ca5
21 changed files with 30 additions and 613 deletions
|
@ -215,9 +215,6 @@ class TObjPtr
|
|||
mutable DObject *o;
|
||||
};
|
||||
public:
|
||||
TObjPtr() = default;
|
||||
|
||||
TObjPtr(T t) : pp(t) {}
|
||||
|
||||
constexpr TObjPtr<T>& operator=(T q) noexcept
|
||||
{
|
||||
|
|
|
@ -576,35 +576,6 @@ public:
|
|||
return f;
|
||||
}
|
||||
|
||||
bool SortedDelete(const T& obj)
|
||||
{
|
||||
auto f = SortedFind(obj, true);
|
||||
if (f == Size())
|
||||
{
|
||||
Delete(f);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
bool SortedDelete(const T& obj, Func lt)
|
||||
{
|
||||
auto f = SortedFind(obj, lt, true);
|
||||
if (f == Size())
|
||||
{
|
||||
Delete(f);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Pop ()
|
||||
{
|
||||
if (Count > 0)
|
||||
|
|
186
src/g_level.cpp
186
src/g_level.cpp
|
@ -98,7 +98,6 @@
|
|||
#include "s_music.h"
|
||||
#include "fragglescript/t_script.h"
|
||||
|
||||
|
||||
#include "texturemanager.h"
|
||||
|
||||
void STAT_StartNewGame(const char *lev);
|
||||
|
@ -2467,188 +2466,3 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, GetEpisodeName)
|
|||
ACTION_RETURN_STRING(GStrings.localize(STAT_EpisodeName().GetChars()));
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Pathfinding
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// Code by RicardoLuis0, modified by Major Cooke
|
||||
static TArray<TObjPtr<AActor*>>& GetPathNodeNeighbors(AActor * self)
|
||||
{
|
||||
static PClass * nodeCls = PClass::FindClass(NAME_PathNode);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if(!nodeCls->IsAncestorOf(self->GetClass()))
|
||||
{
|
||||
ThrowAbortException(X_BAD_SELF, "Invalid class passed to GetNeighbors (must be PathNode)");
|
||||
}
|
||||
#endif
|
||||
|
||||
static PField *var = dyn_cast<PField>(nodeCls->FindSymbol("neighbors", true));
|
||||
|
||||
assert(var);
|
||||
assert(var->Type->isDynArray());
|
||||
assert(static_cast<PDynArray*>(var->Type)->ElementType == nodeCls->VMType);
|
||||
|
||||
return *reinterpret_cast<TArray<TObjPtr<AActor*>>*>(reinterpret_cast<uintptr_t>(self) + var->Offset);
|
||||
}
|
||||
|
||||
static void ReconstructPath(TMap<AActor*, AActor*> &cameFrom, AActor* current, TArray<TObjPtr<AActor*>> &path)
|
||||
{
|
||||
path.Clear();
|
||||
path.Push(current);
|
||||
AActor ** tmp = cameFrom.CheckKey(current);
|
||||
|
||||
if(tmp) do
|
||||
{
|
||||
path.Insert(0, *tmp);
|
||||
}
|
||||
while(tmp = cameFrom.CheckKey(*tmp));
|
||||
}
|
||||
|
||||
static AActor* FindClosestNode(AActor* from)
|
||||
{
|
||||
static const PClass * nodeCls = PClass::FindClass(NAME_PathNode);
|
||||
|
||||
AActor * closest = nullptr;
|
||||
double closestDist = DBL_MAX;
|
||||
|
||||
for (int i = 0; i < from->Level->PathNodes.Size(); i++)
|
||||
{
|
||||
AActor* node = from->Level->PathNodes[i];
|
||||
if(node && !(node->flags & MF_AMBUSH) && nodeCls->IsAncestorOf(node->GetClass()))
|
||||
{
|
||||
double dst = node->Distance3DSquared(from);
|
||||
const bool mrange = (dst < closestDist &&
|
||||
(node->friendlyseeblocks <= 0 || dst < double(node->friendlyseeblocks * node->friendlyseeblocks)));
|
||||
|
||||
if (mrange && !from->CallExcludeNode(node) && P_CheckSight(node, from))
|
||||
{
|
||||
closestDist = dst;
|
||||
closest = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
static V GetOr(TMap<K, V> map, const K &key, V alt)
|
||||
{
|
||||
V *k = map.CheckKey(key);
|
||||
return k ? *k : alt;
|
||||
}
|
||||
|
||||
static bool FindPathAStar(AActor *chaser, AActor* startnode, AActor* goalnode, TArray<TObjPtr<AActor*>> &path)
|
||||
{
|
||||
TArray<AActor*> openSet;
|
||||
TMap<AActor*, AActor*> cameFrom;
|
||||
TMap<AActor*, double> gScore;
|
||||
TMap<AActor*, double> fScore;
|
||||
|
||||
openSet.Push(startnode);
|
||||
gScore.Insert(startnode, 0);
|
||||
fScore.Insert(startnode, startnode->Distance3DSquared(goalnode));
|
||||
|
||||
auto lt_fScore = [&fScore](AActor* lhs, AActor* rhs)
|
||||
{
|
||||
return GetOr(fScore, lhs, DBL_MAX) < GetOr(fScore, rhs, DBL_MAX);
|
||||
};
|
||||
|
||||
while(openSet.Size() > 0)
|
||||
{
|
||||
AActor * current = openSet[0];
|
||||
openSet.Delete(0);
|
||||
if(current == goalnode)
|
||||
{
|
||||
ReconstructPath(cameFrom, current, path);
|
||||
return true;
|
||||
}
|
||||
|
||||
double current_gScore = GetOr(gScore, current, DBL_MAX);
|
||||
|
||||
for(AActor * neighbor : GetPathNodeNeighbors(current))
|
||||
{
|
||||
double tentative_gScore = current_gScore + current->Distance3DSquared(neighbor);
|
||||
|
||||
double neighbor_gScore = GetOr(gScore, neighbor, DBL_MAX);
|
||||
|
||||
if (tentative_gScore < neighbor_gScore && !chaser->CallExcludeNode(neighbor))
|
||||
{
|
||||
openSet.SortedDelete(neighbor, lt_fScore);
|
||||
cameFrom.Insert(neighbor, current);
|
||||
gScore.Insert(neighbor, tentative_gScore);
|
||||
fScore.Insert(neighbor, tentative_gScore + neighbor->Distance3DSquared(goalnode));
|
||||
openSet.SortedInsert(neighbor, lt_fScore);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FLevelLocals::FindPath(AActor* chaser, AActor* target, AActor* startNode, AActor* goalNode)
|
||||
{
|
||||
if (!chaser || !target || PathNodes.Size() < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static PClass* nodeCls = PClass::FindClass(NAME_PathNode);
|
||||
assert(startNode == nullptr || nodeCls->IsAncestorOf(startNode->GetClass()));
|
||||
assert(goalNode == nullptr || nodeCls->IsAncestorOf(goalNode->GetClass()));
|
||||
|
||||
if(startNode == nullptr) startNode = FindClosestNode(chaser);
|
||||
if(goalNode == nullptr) goalNode = FindClosestNode(target);
|
||||
|
||||
// Incomplete graph.
|
||||
if (!startNode || !goalNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startNode == goalNode)
|
||||
{
|
||||
chaser->ClearPath();
|
||||
chaser->Path.Push(MakeObjPtr<AActor*>(startNode));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FindPathAStar(chaser, startNode, goalNode, chaser->Path))
|
||||
{
|
||||
if (chaser->goal && nodeCls->IsAncestorOf(chaser->goal->GetClass()))
|
||||
{
|
||||
chaser->goal = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FLevelLocals, FindPath)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
|
||||
PARAM_OBJECT(chaser, AActor);
|
||||
PARAM_OBJECT(target, AActor);
|
||||
PARAM_OBJECT(startnode, AActor);
|
||||
PARAM_OBJECT(goalnode, AActor);
|
||||
return self->FindPath(chaser, target, startnode, goalnode);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FLevelLocals, HandlePathNode)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
|
||||
PARAM_OBJECT(node, AActor);
|
||||
PARAM_BOOL(add);
|
||||
if (node)
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
if (self->PathNodes.Find(node) >= self->PathNodes.Size())
|
||||
self->PathNodes.Push(node);
|
||||
}
|
||||
else self->PathNodes.Delete(self->PathNodes.Find(node));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -444,9 +444,6 @@ public:
|
|||
|
||||
void SetMusic();
|
||||
|
||||
bool FindPath(AActor *chaser, AActor *target, AActor *startnode = nullptr, AActor *goalnode = nullptr);
|
||||
|
||||
TArray<AActor *> PathNodes;
|
||||
TArray<vertex_t> vertexes;
|
||||
TArray<sector_t> sectors;
|
||||
TArray<extsector_t> extsectors; // container for non-trivial sector information. sector_t must be trivially copyable for *_fakeflat to work as intended.
|
||||
|
|
|
@ -1819,7 +1819,6 @@ MapFlagHandlers[] =
|
|||
{ "disableskyboxao", MITYPE_CLRFLAG3, LEVEL3_SKYBOXAO, 0 },
|
||||
{ "avoidmelee", MITYPE_SETFLAG3, LEVEL3_AVOIDMELEE, 0 },
|
||||
{ "attenuatelights", MITYPE_SETFLAG3, LEVEL3_ATTENUATE, 0 },
|
||||
{ "pathing", MITYPE_SETFLAG3, LEVEL3_PATHING, 0 },
|
||||
{ "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes
|
||||
{ "nopassover", MITYPE_COMPATFLAG, COMPATF_NO_PASSMOBJ, 0 },
|
||||
{ "passover", MITYPE_CLRCOMPATFLAG, COMPATF_NO_PASSMOBJ, 0 },
|
||||
|
|
|
@ -270,7 +270,6 @@ enum ELevelFlags : unsigned int
|
|||
LEVEL3_AVOIDMELEE = 0x00020000, // global flag needed for proper MBF support.
|
||||
LEVEL3_NOJUMPDOWN = 0x00040000, // only for MBF21. Inverse of MBF's dog_jumping flag.
|
||||
LEVEL3_LIGHTCREATED = 0x00080000, // a light had been created in the last frame
|
||||
LEVEL3_PATHING = 0x00100000, // enable pathfinding by default
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -503,7 +503,6 @@ enum
|
|||
SECMF_OVERLAPPING = 512, // floor and ceiling overlap and require special renderer action.
|
||||
SECMF_NOSKYWALLS = 1024, // Do not draw "sky walls"
|
||||
SECMF_LIFT = 2048, // For MBF monster AI
|
||||
SECMF_NOPATHING = 4096, // monsters cannot path find in these areas, saves on time and resources
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
|
@ -202,7 +202,6 @@ xx(Cast) // 'damage type' for the cast call
|
|||
xx(MapSpot)
|
||||
xx(PatrolPoint)
|
||||
xx(PatrolSpecial)
|
||||
xx(PathNode)
|
||||
xx(Communicator)
|
||||
xx(PowerScanner)
|
||||
|
||||
|
|
|
@ -326,7 +326,6 @@ void FLevelLocals::ClearLevelData(bool fullgc)
|
|||
}
|
||||
ClearPortals();
|
||||
|
||||
PathNodes.Clear();
|
||||
tagManager.Clear();
|
||||
ClearTIDHashes();
|
||||
if (SpotState) SpotState->Destroy();
|
||||
|
|
|
@ -442,10 +442,7 @@ enum ActorFlag9
|
|||
MF9_DOSHADOWBLOCK = 0x00000002, // [inkoalawetrust] Should the monster look for SHADOWBLOCK actors ?
|
||||
MF9_SHADOWBLOCK = 0x00000004, // [inkoalawetrust] Actors in the line of fire with this flag trigger the MF_SHADOW aiming penalty.
|
||||
MF9_SHADOWAIMVERT = 0x00000008, // [inkoalawetrust] Monster aim is also offset vertically when aiming at shadow actors.
|
||||
MF9_DECOUPLEDANIMATIONS = 0x00000010, // [RL0] Decouple model animations from states
|
||||
MF9_PATHING = 0x00000020, // [MC] Enables monsters to do pathfinding, such as A*.
|
||||
MF9_KEEPPATH = 0x00000040, // [MC] Forces monsters to keep to the path when target's in sight.
|
||||
MF9_NOPATHING = 0x00000080, // [MC] override the mapinfo "pathfinding"
|
||||
MF9_DECOUPLEDANIMATIONS = 0x00000010, // [RL0] Decouple model animations from states
|
||||
};
|
||||
|
||||
// --- mobj.renderflags ---
|
||||
|
@ -1104,11 +1101,6 @@ public:
|
|||
void AttachLight(unsigned int count, const FLightDefaults *lightdef);
|
||||
void SetDynamicLights();
|
||||
|
||||
void ClearPath();
|
||||
bool CanPathfind();
|
||||
bool CallExcludeNode(AActor *node);
|
||||
void CallReachedNode(AActor *node);
|
||||
|
||||
// info for drawing
|
||||
// NOTE: The first member variable *must* be snext.
|
||||
AActor *snext, **sprev; // links in sector (if needed)
|
||||
|
@ -1165,7 +1157,6 @@ public:
|
|||
TObjPtr<DBoneComponents*> boneComponentData;
|
||||
|
||||
// interaction info
|
||||
TArray<TObjPtr<AActor*> > Path;
|
||||
FBlockNode *BlockNode; // links in blocks (if needed)
|
||||
struct sector_t *Sector;
|
||||
subsector_t * subsector;
|
||||
|
|
|
@ -318,14 +318,13 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, CheckMeleeRange, P_CheckMeleeRange)
|
|||
//
|
||||
//=============================================================================
|
||||
|
||||
static int DoCheckMissileRange (AActor *actor, int &sight)
|
||||
static int P_CheckMissileRange (AActor *actor)
|
||||
{
|
||||
double dist;
|
||||
|
||||
if ((actor->Sector->Flags & SECF_NOATTACK)) return false;
|
||||
|
||||
sight = P_CheckSight(actor, actor->target, SF_SEEPASTBLOCKEVERYTHING);
|
||||
if (!sight)
|
||||
if (!P_CheckSight (actor, actor->target, SF_SEEPASTBLOCKEVERYTHING))
|
||||
return false;
|
||||
|
||||
if (actor->flags & MF_JUSTHIT)
|
||||
|
@ -381,12 +380,6 @@ static int DoCheckMissileRange (AActor *actor, int &sight)
|
|||
return pr_checkmissilerange() >= min(int(dist), mmc);
|
||||
}
|
||||
|
||||
static int P_CheckMissileRange(AActor* actor)
|
||||
{
|
||||
int n = -1;
|
||||
return DoCheckMissileRange(actor, n);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, CheckMissileRange, P_CheckMissileRange)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
@ -951,13 +944,13 @@ void P_DoNewChaseDir (AActor *actor, double deltax, double deltay)
|
|||
//
|
||||
//=============================================================================
|
||||
|
||||
void DoNewChaseDir(AActor * actor, bool node)
|
||||
void P_NewChaseDir(AActor * actor)
|
||||
{
|
||||
DVector2 delta;
|
||||
|
||||
actor->strafecount = 0;
|
||||
|
||||
if ((node || actor->flags5&MF5_CHASEGOAL || actor->goal == actor->target) && actor->goal != nullptr)
|
||||
if ((actor->flags5&MF5_CHASEGOAL || actor->goal == actor->target) && actor->goal!=NULL)
|
||||
{
|
||||
delta = actor->Vec2To(actor->goal);
|
||||
}
|
||||
|
@ -1096,11 +1089,6 @@ void DoNewChaseDir(AActor * actor, bool node)
|
|||
|
||||
}
|
||||
|
||||
void P_NewChaseDir(AActor* actor)
|
||||
{
|
||||
DoNewChaseDir(actor, false);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// P_RandomChaseDir
|
||||
|
@ -2211,85 +2199,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_ClearLastHeard)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ClearPath
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void AActor::ClearPath()
|
||||
{
|
||||
Path.Clear();
|
||||
if (goal)
|
||||
{
|
||||
static PClass* nodeCls = PClass::FindClass(NAME_PathNode);
|
||||
if (nodeCls->IsAncestorOf(goal->GetClass()))
|
||||
goal = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, ClearPath)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->ClearPath();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AActor::CanPathfind()
|
||||
{
|
||||
if (Level->PathNodes.Size() > 0 &&
|
||||
(!(flags9 & MF9_NOPATHING) && !(Sector->MoreFlags & SECMF_NOPATHING)) &&
|
||||
(flags9 & MF9_PATHING || Level->flags3 & LEVEL3_PATHING))
|
||||
{
|
||||
if ((flags6 & MF6_NOFEAR))
|
||||
return true;
|
||||
|
||||
// Can't pathfind while feared.
|
||||
if (flags4 & MF4_FRIGHTENED)
|
||||
return false;
|
||||
|
||||
if (target)
|
||||
{
|
||||
if (target->flags8 & MF8_FRIGHTENING)
|
||||
return false;
|
||||
|
||||
if (target->player && target->player->cheats & CF_FRIGHTENING)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, CanPathfind)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
return self->CanPathfind();
|
||||
}
|
||||
|
||||
void AActor::CallReachedNode(AActor *node)
|
||||
{
|
||||
IFVIRTUAL(AActor, ReachedNode)
|
||||
{
|
||||
VMValue params[2] = { this, node };
|
||||
VMCall(func, params, 2, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Called while scoring the path.
|
||||
bool AActor::CallExcludeNode(AActor* node)
|
||||
{
|
||||
IFVIRTUAL(AActor, ExcludeNode)
|
||||
{
|
||||
VMValue params[2] = { (DObject*)this, node };
|
||||
int retval = 0;
|
||||
VMReturn ret(&retval);
|
||||
VMCall(func, params, 2, &ret, 1);
|
||||
return !!retval;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// A_Wander
|
||||
|
@ -2424,7 +2333,6 @@ nosee:
|
|||
|
||||
void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missilestate, bool playactive, bool nightmarefast, bool dontmove, int flags)
|
||||
{
|
||||
int sight = -1;
|
||||
if (actor->flags5 & MF5_INCONVERSATION)
|
||||
return;
|
||||
|
||||
|
@ -2503,7 +2411,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
|
||||
// [RH] Friendly monsters will consider chasing whoever hurts a player if they
|
||||
// don't already have a target.
|
||||
if (actor->flags & MF_FRIENDLY && actor->target == nullptr)
|
||||
if (actor->flags & MF_FRIENDLY && actor->target == NULL)
|
||||
{
|
||||
player_t *player;
|
||||
|
||||
|
@ -2562,7 +2470,6 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
}
|
||||
else
|
||||
{
|
||||
actor->ClearPath();
|
||||
actor->SetIdle();
|
||||
actor->flags7 &= ~MF7_INCHASE;
|
||||
return;
|
||||
|
@ -2586,13 +2493,9 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
actor->flags7 &= ~MF7_INCHASE;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// [RH] Don't attack if just moving toward goal
|
||||
static const PClass* nodeCls = PClass::FindClass(NAME_PathNode);
|
||||
bool pnode = (actor->goal && nodeCls->IsAncestorOf(actor->goal->GetClass()));
|
||||
|
||||
if (!pnode &&
|
||||
(actor->target == actor->goal || (actor->flags5&MF5_CHASEGOAL && actor->goal)))
|
||||
if (actor->target == actor->goal || (actor->flags5&MF5_CHASEGOAL && actor->goal != NULL))
|
||||
{
|
||||
AActor * savedtarget = actor->target;
|
||||
actor->target = actor->goal;
|
||||
|
@ -2610,7 +2513,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
// as the goal.
|
||||
while ( (spec = specit.Next()) )
|
||||
{
|
||||
P_ExecuteSpecial(actor->Level, spec->special, nullptr, actor, false, spec->args[0],
|
||||
P_ExecuteSpecial(actor->Level, spec->special, NULL, actor, false, spec->args[0],
|
||||
spec->args[1], spec->args[2], spec->args[3], spec->args[4]);
|
||||
}
|
||||
|
||||
|
@ -2633,7 +2536,6 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
if (newgoal != NULL && delay != 0)
|
||||
{
|
||||
actor->flags4 |= MF4_INCOMBAT;
|
||||
actor->ClearPath(); // [MC] Just to be safe.
|
||||
actor->SetIdle();
|
||||
}
|
||||
actor->flags7 &= ~MF7_INCHASE;
|
||||
|
@ -2691,9 +2593,17 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
}
|
||||
|
||||
// check for missile attack
|
||||
if (missilestate && (actor->isFast() || actor->movecount < 1) && DoCheckMissileRange(actor, sight))
|
||||
if (missilestate)
|
||||
{
|
||||
actor->SetState(missilestate);
|
||||
if (!actor->isFast() && actor->movecount)
|
||||
{
|
||||
goto nomissile;
|
||||
}
|
||||
|
||||
if (!P_CheckMissileRange (actor))
|
||||
goto nomissile;
|
||||
|
||||
actor->SetState (missilestate);
|
||||
actor->flags |= MF_JUSTATTACKED;
|
||||
actor->flags4 |= MF4_INCOMBAT;
|
||||
actor->flags7 &= ~MF7_INCHASE;
|
||||
|
@ -2723,53 +2633,10 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
if (gotNew && actor->target != oldtarget)
|
||||
{
|
||||
actor->flags7 &= ~MF7_INCHASE;
|
||||
actor->ClearPath();
|
||||
return; // got a new target
|
||||
}
|
||||
}
|
||||
|
||||
if (!dontmove && actor->target && actor->CanPathfind())
|
||||
{
|
||||
// Try to get sight checks from missile states if they have any, saving time.
|
||||
if (!(actor->flags9 & MF9_KEEPPATH))
|
||||
{
|
||||
if (sight < 0)
|
||||
sight = P_CheckSight(actor, actor->target, SF_SEEPASTBLOCKEVERYTHING);
|
||||
}
|
||||
else sight = 0;
|
||||
|
||||
// Out of sight, so keep pathfinding
|
||||
if (sight == 0)
|
||||
{
|
||||
if (pnode && !(actor->goal->flags & MF_AMBUSH))
|
||||
{
|
||||
AActor* temp = actor->target;
|
||||
actor->target = actor->goal;
|
||||
bool reached = false;
|
||||
// 2D checks for floaters, 3D otherwise
|
||||
if (actor->flags & MF_FLOAT)
|
||||
{
|
||||
bool vrange = !!(actor->flags5 & MF5_NOVERTICALMELEERANGE);
|
||||
actor->flags5 |= MF5_NOVERTICALMELEERANGE;
|
||||
reached = P_CheckMeleeRange(actor);
|
||||
if (!vrange) actor->flags5 &= ~(MF5_NOVERTICALMELEERANGE);
|
||||
}
|
||||
else reached = (P_CheckMeleeRange(actor));
|
||||
actor->target = temp;
|
||||
|
||||
if (reached)
|
||||
actor->CallReachedNode(actor->goal);
|
||||
|
||||
}
|
||||
if (!actor->goal)
|
||||
{
|
||||
if (actor->Path.Size() > 0 || actor->Level->FindPath(actor, actor->target))
|
||||
actor->goal = actor->Path[0];
|
||||
}
|
||||
}
|
||||
else actor->ClearPath();
|
||||
}
|
||||
|
||||
//
|
||||
// chase towards player
|
||||
//
|
||||
|
@ -2788,7 +2655,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
// chase towards player
|
||||
if ((--actor->movecount < 0 && !(flags & CHF_NORANDOMTURN)) || (!P_SmartMove(actor) && !(flags & CHF_STOPIFBLOCKED)))
|
||||
{
|
||||
DoNewChaseDir(actor, pnode);
|
||||
P_NewChaseDir(actor);
|
||||
}
|
||||
// if the move was illegal, reset it
|
||||
// (copied from A_SerpentChase - it applies to everything with CANTLEAVEFLOORPIC!)
|
||||
|
@ -2804,7 +2671,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
}
|
||||
}
|
||||
if (!(flags & CHF_STOPIFBLOCKED))
|
||||
DoNewChaseDir(actor, pnode);
|
||||
P_NewChaseDir(actor);
|
||||
}
|
||||
}
|
||||
else if (dontmove && actor->movecount > 0) actor->movecount--;
|
||||
|
|
|
@ -592,7 +592,7 @@ bool P_TeleportMove(AActor* thing, const DVector3 &pos, bool telefrag, bool modi
|
|||
thing->CheckSectorTransition(oldsec);
|
||||
}
|
||||
}
|
||||
thing->ClearPath();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2536,8 +2536,8 @@ bool P_TryMove(AActor *thing, const DVector2 &pos,
|
|||
|
||||
|
||||
// Check for crossed portals
|
||||
bool portalcrossed, nodecall;
|
||||
nodecall = portalcrossed = false;
|
||||
bool portalcrossed;
|
||||
portalcrossed = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -2582,7 +2582,6 @@ bool P_TryMove(AActor *thing, const DVector2 &pos,
|
|||
thing->Prev += port->mDisplacement;
|
||||
thing->LinkToWorld(&ctx);
|
||||
P_FindFloorCeiling(thing);
|
||||
thing->ClearPath();
|
||||
portalcrossed = true;
|
||||
tm.portalstep = false;
|
||||
tm.pos += port->mDisplacement;
|
||||
|
@ -2612,8 +2611,7 @@ bool P_TryMove(AActor *thing, const DVector2 &pos,
|
|||
P_TranslatePortalVXVY(ld, thing->Vel.X, thing->Vel.Y);
|
||||
P_TranslatePortalAngle(ld, thing->Angles.Yaw);
|
||||
thing->LinkToWorld(&ctx);
|
||||
P_FindFloorCeiling(thing);
|
||||
thing->ClearPath();
|
||||
P_FindFloorCeiling(thing);
|
||||
thing->ClearInterpolation();
|
||||
portalcrossed = true;
|
||||
tm.portalstep = false;
|
||||
|
@ -2771,7 +2769,7 @@ bool P_TryMove(AActor *thing, const DVector2 &pos,
|
|||
thing->Sector = thing->Level->PointInSector(thing->Pos());
|
||||
thing->PrevPortalGroup = thing->Sector->PortalGroup;
|
||||
thing->LinkToWorld(&ctx);
|
||||
thing->ClearPath();
|
||||
|
||||
P_FindFloorCeiling(thing);
|
||||
}
|
||||
|
||||
|
|
|
@ -1124,7 +1124,7 @@ FSpriteModelFrame * FindModelFrame(const AActor * thing, int sprite, int frame,
|
|||
|
||||
if(thing->flags9 & MF9_DECOUPLEDANIMATIONS)
|
||||
{
|
||||
return BaseSpriteModelFrames.CheckKey((thing->modelData != nullptr && thing->modelData->modelDef != nullptr) ? thing->modelData->modelDef : thing->GetClass());
|
||||
return &BaseSpriteModelFrames[(thing->modelData != nullptr && thing->modelData->modelDef != nullptr) ? thing->modelData->modelDef : thing->GetClass()];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -353,9 +353,6 @@ static FFlagDef ActorFlagDefs[]=
|
|||
DEFINE_FLAG(MF9, SHADOWBLOCK, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, SHADOWAIMVERT, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, DECOUPLEDANIMATIONS, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, PATHING, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, KEEPPATH, AActor, flags9),
|
||||
DEFINE_FLAG(MF9, NOPATHING, AActor, flags9),
|
||||
|
||||
// Effect flags
|
||||
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
|
||||
|
|
|
@ -2786,7 +2786,6 @@ DEFINE_FIELD_X(LevelInfo, level_info_t, RedirectMapName)
|
|||
DEFINE_FIELD_X(LevelInfo, level_info_t, teamdamage)
|
||||
|
||||
DEFINE_GLOBAL_NAMED(currentVMLevel, level)
|
||||
DEFINE_FIELD(FLevelLocals, PathNodes)
|
||||
DEFINE_FIELD(FLevelLocals, sectors)
|
||||
DEFINE_FIELD(FLevelLocals, lines)
|
||||
DEFINE_FIELD(FLevelLocals, sides)
|
||||
|
@ -2854,7 +2853,6 @@ DEFINE_FIELD_BIT(FLevelLocals, flags2, infinite_flight, LEVEL2_INFINITE_FLIGHT)
|
|||
DEFINE_FIELD_BIT(FLevelLocals, flags2, no_dlg_freeze, LEVEL2_CONV_SINGLE_UNFREEZE)
|
||||
DEFINE_FIELD_BIT(FLevelLocals, flags2, keepfullinventory, LEVEL2_KEEPFULLINVENTORY)
|
||||
DEFINE_FIELD_BIT(FLevelLocals, flags3, removeitems, LEVEL3_REMOVEITEMS)
|
||||
DEFINE_FIELD_BIT(FLevelLocals, flags3, pathing, LEVEL3_PATHING)
|
||||
|
||||
DEFINE_FIELD_X(Sector, sector_t, floorplane)
|
||||
DEFINE_FIELD_X(Sector, sector_t, ceilingplane)
|
||||
|
|
|
@ -2125,7 +2125,6 @@ DEFINE_FIELD(AActor, LightLevel)
|
|||
DEFINE_FIELD(AActor, ShadowAimFactor)
|
||||
DEFINE_FIELD(AActor, ShadowPenaltyFactor)
|
||||
DEFINE_FIELD(AActor, AutomapOffsets)
|
||||
DEFINE_FIELD(AActor, Path)
|
||||
DEFINE_FIELD(AActor, LandingSpeed)
|
||||
|
||||
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, thing);
|
||||
|
|
|
@ -44,7 +44,6 @@ DoomEdNums
|
|||
5065 = InvisibleBridge8
|
||||
9001 = MapSpot
|
||||
9013 = MapSpotGravity
|
||||
9022 = PathNode
|
||||
9024 = PatrolPoint
|
||||
9025 = SecurityCamera
|
||||
9026 = Spark
|
||||
|
|
|
@ -261,7 +261,6 @@ class Actor : Thinker native
|
|||
private native int InventoryID; // internal counter.
|
||||
native uint freezetics;
|
||||
native Vector2 AutomapOffsets;
|
||||
native Array<PathNode> Path;
|
||||
native double LandingSpeed;
|
||||
|
||||
meta String Obituary; // Player was killed by this actor
|
||||
|
@ -667,7 +666,7 @@ class Actor : Thinker native
|
|||
// called before and after triggering a teleporter
|
||||
// return false in PreTeleport() to cancel the action early
|
||||
virtual bool PreTeleport( Vector3 destpos, double destangle, int flags ) { return true; }
|
||||
virtual void PostTeleport( Vector3 destpos, double destangle, int flags ) { }
|
||||
virtual void PostTeleport( Vector3 destpos, double destangle, int flags ) {}
|
||||
|
||||
native virtual bool OkayToSwitchTarget(Actor other);
|
||||
native clearscope static class<Actor> GetReplacement(class<Actor> cls);
|
||||
|
@ -701,7 +700,7 @@ class Actor : Thinker native
|
|||
native void SoundAlert(Actor target, bool splash = false, double maxdist = 0);
|
||||
native void ClearBounce();
|
||||
native TerrainDef GetFloorTerrain();
|
||||
native bool CheckLocalView(int consoleplayer = -1 /* parameter is not used anymore but needed for backward compatibility. */);
|
||||
native bool CheckLocalView(int consoleplayer = -1 /* parameter is not used anymore but needed for backward compatibilityö. */);
|
||||
native bool CheckNoDelay();
|
||||
native bool UpdateWaterLevel (bool splash = true);
|
||||
native bool IsZeroDamage();
|
||||
|
@ -800,86 +799,6 @@ class Actor : Thinker native
|
|||
movecount = random[TryWalk](0, 15);
|
||||
return true;
|
||||
}
|
||||
|
||||
native void ClearPath();
|
||||
native clearscope bool CanPathfind() const;
|
||||
virtual void ReachedNode(Actor mo)
|
||||
{
|
||||
if (!mo)
|
||||
{
|
||||
if (!goal)
|
||||
return;
|
||||
mo = goal;
|
||||
}
|
||||
|
||||
let node = PathNode(mo);
|
||||
if (!node || !target || (!bKEEPPATH && CheckSight(target)))
|
||||
{
|
||||
ClearPath();
|
||||
return;
|
||||
}
|
||||
|
||||
int i = Path.Find(node) + 1;
|
||||
int end = Path.Size();
|
||||
|
||||
for (i; i < end; i++)
|
||||
{
|
||||
PathNode next = Path[i];
|
||||
|
||||
if (!next || next == node)
|
||||
continue;
|
||||
|
||||
// 2D checks for floaters, 3D for ground
|
||||
Actor tar = target;
|
||||
bool vrange = bNOVERTICALMELEERANGE;
|
||||
bNOVERTICALMELEERANGE = bFLOAT;
|
||||
target = next;
|
||||
|
||||
bool inrange = CheckMeleeRange();
|
||||
|
||||
target = tar;
|
||||
bNOVERTICALMELEERANGE = vrange;
|
||||
|
||||
if (inrange)
|
||||
continue;
|
||||
|
||||
// Monsters will never 'reach' AMBUSH flagged nodes. Instead, the engine
|
||||
// indicates they're reached the moment they tele/portal.
|
||||
|
||||
if (node.bAMBUSH && next.bAMBUSH)
|
||||
continue;
|
||||
|
||||
goal = next;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= end)
|
||||
ClearPath();
|
||||
|
||||
}
|
||||
|
||||
// Return true to mark the node as ineligible for constructing a path along.
|
||||
virtual bool ExcludeNode(PathNode node)
|
||||
{
|
||||
if (!node) return true;
|
||||
|
||||
// Scale is the size requirements.
|
||||
// STANDSTILL flag is used to require the actor to be bigger instead of smaller.
|
||||
double r = node.Scale.X;
|
||||
double h = node.Scale.Y;
|
||||
|
||||
if (r <= 0.0 && h <= 0.0)
|
||||
return false;
|
||||
|
||||
// Perfect fit.
|
||||
if (radius == r && height == h)
|
||||
return false;
|
||||
|
||||
if ((r < radius) || (h < height))
|
||||
return !node.bSTANDSTILL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
native bool TryMove(vector2 newpos, int dropoff, bool missilecheck = false, FCheckPosition tm = null);
|
||||
native bool CheckMove(vector2 newpos, int flags = 0, FCheckPosition tm = null);
|
||||
|
|
|
@ -245,123 +245,3 @@ class SpeakerIcon : Unknown
|
|||
Scale 0.125;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================================================================
|
||||
Path Nodes
|
||||
=================================================================
|
||||
Special flags are as follows:
|
||||
|
||||
AMBUSH
|
||||
Node is blind. Things cannot "touch" these nodes with A_Chase.
|
||||
Useful for tele/portals since the engine makes them "touch"
|
||||
upon transitioning. These nodes are fast forwarded over in Actor's
|
||||
ReachedNode() function.
|
||||
|
||||
STANDSTILL
|
||||
Traveller must be bigger instead of smaller.
|
||||
*/
|
||||
class PathNode : Actor
|
||||
{
|
||||
Array<PathNode> neighbors;
|
||||
|
||||
Default
|
||||
{
|
||||
+NOINTERACTION
|
||||
+NOBLOCKMAP
|
||||
+INVISIBLE
|
||||
+DONTSPLASH
|
||||
+NOTONAUTOMAP
|
||||
+NOGRAVITY
|
||||
Radius 16;
|
||||
Height 56;
|
||||
RenderStyle "None";
|
||||
|
||||
// The following properties can be set directly in Ultimate Doom Builder's Custom tab.
|
||||
|
||||
FriendlySeeBlocks 0; // Sight checks limited to this. <= 0 is infinite.
|
||||
XScale 0; // filter height - actors must be this small for this node. Only effective if > 0.
|
||||
YScale 0; // filter radius - ^ but for radius
|
||||
}
|
||||
|
||||
// Args are TIDs. Can be one way to force single directions.
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
for (int i = 0; i < Args.Size(); i++)
|
||||
{
|
||||
if (!Args[i]) continue;
|
||||
|
||||
let it = level.CreateActorIterator(Args[i], "PathNode");
|
||||
PathNode node;
|
||||
do
|
||||
{
|
||||
if (node && node != self)
|
||||
neighbors.Push(node);
|
||||
} while (node = PathNode(it.Next()))
|
||||
|
||||
}
|
||||
level.HandlePathNode(self, true);
|
||||
}
|
||||
|
||||
override void OnDestroy()
|
||||
{
|
||||
level.HandlePathNode(self, false);
|
||||
Super.OnDestroy();
|
||||
}
|
||||
|
||||
// For ACS access with ScriptCall.
|
||||
// 'con' values are:
|
||||
// 0: No connections
|
||||
// 1: Connect tid1 to tid2 one-way
|
||||
// 2: ^ but two-way.
|
||||
static void SetConnectionGlobal(int tid1, int tid2, int con)
|
||||
{
|
||||
if (tid1 == 0 || tid2 == 0)
|
||||
return;
|
||||
|
||||
PathNode node;
|
||||
Array<PathNode> nodes2; // Cache for actors with tid2
|
||||
{
|
||||
let it = Level.CreateActorIterator(tid2, "PathNode");
|
||||
|
||||
do
|
||||
{
|
||||
if (node)
|
||||
nodes2.Push(node);
|
||||
} while (node = PathNode(it.Next()))
|
||||
}
|
||||
// Nothing to set to.
|
||||
if (nodes2.Size() < 1)
|
||||
return;
|
||||
|
||||
let it = Level.CreateActorIterator(tid1, "PathNode");
|
||||
node = null;
|
||||
do
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
foreach (n2 : nodes2)
|
||||
{
|
||||
if (n2)
|
||||
{
|
||||
node.SetConnection(n2, con);
|
||||
n2.SetConnection(node, (con > 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (node = PathNode(it.Next()))
|
||||
}
|
||||
|
||||
void SetConnection(PathNode other, bool on)
|
||||
{
|
||||
if (!other) return;
|
||||
|
||||
if (on)
|
||||
{
|
||||
if (neighbors.Find(other) >= neighbors.Size())
|
||||
neighbors.Push(other);
|
||||
}
|
||||
else neighbors.Delete(neighbors.Find(other));
|
||||
}
|
||||
}
|
|
@ -408,7 +408,7 @@ struct LevelLocals native
|
|||
|
||||
const CLUSTER_HUB = 0x00000001; // Cluster uses hub behavior
|
||||
|
||||
native readonly Array<PathNode> PathNodes;
|
||||
|
||||
native Array<@Sector> Sectors;
|
||||
native Array<@Line> Lines;
|
||||
native Array<@Side> Sides;
|
||||
|
@ -476,7 +476,6 @@ struct LevelLocals native
|
|||
native readonly int compatflags;
|
||||
native readonly int compatflags2;
|
||||
native readonly LevelInfo info;
|
||||
native readonly bool pathing;
|
||||
|
||||
native String GetUDMFString(int type, int index, Name key);
|
||||
native int GetUDMFInt(int type, int index, Name key);
|
||||
|
@ -554,9 +553,6 @@ struct LevelLocals native
|
|||
|
||||
native void SpawnParticle(FSpawnParticleParams p);
|
||||
native VisualThinker SpawnVisualThinker(Class<VisualThinker> type);
|
||||
|
||||
native bool FindPath(Actor chaser, Actor target, PathNode startnode = null, PathNode goalnode = null);
|
||||
native void HandlePathNode(PathNode node, bool add); // This is only here because there's no other way to register the node privately.
|
||||
}
|
||||
|
||||
// a few values of this need to be readable by the play code.
|
||||
|
|
|
@ -435,7 +435,6 @@ struct Sector native play
|
|||
SECMF_UNDERWATERMASK = 32+64,
|
||||
SECMF_DRAWN = 128, // sector has been drawn at least once
|
||||
SECMF_HIDDEN = 256, // Do not draw on textured automap
|
||||
SECMF_NOPATHING = 4096, // monsters cannot path find in these areas, saves on time and resources
|
||||
}
|
||||
native uint16 MoreFlags;
|
||||
|
||||
|
|
Loading…
Reference in a new issue