mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-22 12:11:25 +00:00
Begin adding PathNodes.
This commit is contained in:
parent
3033fafaa7
commit
b2cb4b0a6d
14 changed files with 286 additions and 10 deletions
104
src/g_level.cpp
104
src/g_level.cpp
|
@ -98,6 +98,7 @@
|
|||
#include "s_music.h"
|
||||
#include "fragglescript/t_script.h"
|
||||
|
||||
|
||||
#include "texturemanager.h"
|
||||
|
||||
void STAT_StartNewGame(const char *lev);
|
||||
|
@ -2466,3 +2467,106 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, GetEpisodeName)
|
|||
ACTION_RETURN_STRING(GStrings.localize(STAT_EpisodeName().GetChars()));
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Pathfinding
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// Code by RicardoLuis0
|
||||
TArray<AActor*> * GetNeighbors(AActor * self)
|
||||
{
|
||||
PClass * cls = PClass::FindClass("PathNode");
|
||||
if(!cls->IsAncestorOf(self->GetClass()))
|
||||
{
|
||||
ThrowAbortException(X_BAD_SELF, "Invalid class passed to GetNeighbors (must be PathNodeInfo)");
|
||||
}
|
||||
|
||||
PField *var = dyn_cast<PField>(cls->FindSymbol("neighbors", true));
|
||||
|
||||
assert(var);
|
||||
assert(var->Type->isDynArray());
|
||||
assert(static_cast<PDynArray*>(var->Type)->ElementType == cls);
|
||||
|
||||
return reinterpret_cast<TArray<AActor*>*>(reinterpret_cast<uintptr_t>(self) + var->Offset);
|
||||
}
|
||||
|
||||
int AS_BinarySearch(TMap<AActor*, double>* fScore, bool exact = false)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AS_ReconstructPath(TMap<AActor*, AActor*>* cameFrom, AActor* chaser)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FLevelLocals::AStar(AActor* chaser, AActor* target, AActor* startnode, AActor* goalnode)
|
||||
{
|
||||
if (!chaser || !target || PathNodes.Size() < 1)
|
||||
return false;
|
||||
|
||||
// If supplying nodes, skip the search.
|
||||
const bool getstart = (!startnode || !startnode->IsKindOf(NAME_PathNode));
|
||||
const bool getgoal = (!goalnode || !goalnode->IsKindOf(NAME_PathNode));
|
||||
|
||||
if (getstart || getgoal)
|
||||
{
|
||||
double dist[2];
|
||||
dist[0] = dist[1] = 100000000.0;
|
||||
|
||||
|
||||
for (int i = 0; i < PathNodes.Size(); i++)
|
||||
{
|
||||
AActor *node = PathNodes[i];
|
||||
if (!node) continue;
|
||||
|
||||
double dis;
|
||||
if (getstart)
|
||||
{
|
||||
dis = node->Distance2DSquared(chaser);
|
||||
if (dis < dist[0] && P_CheckSight(node, chaser)) // TO DO: Make 3D aware, so 3D floors can work better.
|
||||
{
|
||||
startnode = node;
|
||||
dist[0] = dis;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dis = node->Distance2DSquared(target);
|
||||
if (dis < dist[1] && P_CheckSight(node, target))
|
||||
{
|
||||
goalnode = node;
|
||||
dist[1] = dis;
|
||||
}
|
||||
}
|
||||
|
||||
// Incomplete graph.
|
||||
if (!startnode || !goalnode)
|
||||
return false;
|
||||
|
||||
if (startnode == goalnode)
|
||||
{
|
||||
chaser->ClearPath();
|
||||
chaser->Path.Push(MakeObjPtr<AActor*>(startnode));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Begin A* here.
|
||||
TArray<AActor*> openSet;
|
||||
TMap<AActor*, AActor*> cameFrom;
|
||||
TMap<AActor*, double> gScore;
|
||||
TMap<AActor*, double> fScore;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FLevelLocals, AStar)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
|
||||
PARAM_OBJECT(chaser, AActor);
|
||||
PARAM_OBJECT(target, AActor);
|
||||
PARAM_OBJECT(startnode, AActor);
|
||||
PARAM_OBJECT(goalnode, AActor);
|
||||
return self->AStar(chaser, target, startnode, goalnode);
|
||||
}
|
||||
|
|
|
@ -444,6 +444,9 @@ public:
|
|||
|
||||
void SetMusic();
|
||||
|
||||
bool AStar(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.
|
||||
|
|
|
@ -503,6 +503,7 @@ 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,6 +202,7 @@ xx(Cast) // 'damage type' for the cast call
|
|||
xx(MapSpot)
|
||||
xx(PatrolPoint)
|
||||
xx(PatrolSpecial)
|
||||
xx(PathNode)
|
||||
xx(Communicator)
|
||||
xx(PowerScanner)
|
||||
|
||||
|
|
|
@ -611,6 +611,8 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
|
|||
while ((ac = it.Next()))
|
||||
{
|
||||
ac->SetDynamicLights();
|
||||
if (ac->IsKindOf(NAME_PathNode))
|
||||
Level->PathNodes.Push(ac);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -442,7 +442,9 @@ 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_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.
|
||||
};
|
||||
|
||||
// --- mobj.renderflags ---
|
||||
|
@ -1101,6 +1103,8 @@ public:
|
|||
void AttachLight(unsigned int count, const FLightDefaults *lightdef);
|
||||
void SetDynamicLights();
|
||||
|
||||
void ClearPath();
|
||||
|
||||
// info for drawing
|
||||
// NOTE: The first member variable *must* be snext.
|
||||
AActor *snext, **sprev; // links in sector (if needed)
|
||||
|
@ -1157,6 +1161,7 @@ public:
|
|||
TObjPtr<DBoneComponents*> boneComponentData;
|
||||
|
||||
// interaction info
|
||||
TArray<TObjPtr<AActor*> > Path;
|
||||
FBlockNode *BlockNode; // links in blocks (if needed)
|
||||
struct sector_t *Sector;
|
||||
subsector_t * subsector;
|
||||
|
|
|
@ -2199,6 +2199,26 @@ DEFINE_ACTION_FUNCTION(AActor, A_ClearLastHeard)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ClearPath
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void AActor::ClearPath()
|
||||
{
|
||||
Path.Clear();
|
||||
if (goal && goal->IsKindOf(NAME_PathNode))
|
||||
goal = nullptr;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, ClearPath)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->ClearPath();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// A_Wander
|
||||
|
@ -2411,7 +2431,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 == NULL)
|
||||
if (actor->flags & MF_FRIENDLY && actor->target == nullptr)
|
||||
{
|
||||
player_t *player;
|
||||
|
||||
|
@ -2443,7 +2463,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
}
|
||||
if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
|
||||
{ // look for a new target
|
||||
if (actor->target != NULL && (actor->target->flags2 & MF2_NONSHOOTABLE))
|
||||
if (actor->target != nullptr && (actor->target->flags2 & MF2_NONSHOOTABLE))
|
||||
{
|
||||
// Target is only temporarily unshootable, so remember it.
|
||||
actor->lastenemy = actor->target;
|
||||
|
@ -2451,17 +2471,17 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
// hurt our old one temporarily.
|
||||
actor->threshold = 0;
|
||||
}
|
||||
if (P_LookForPlayers (actor, !(flags & CHF_DONTLOOKALLAROUND), NULL) && actor->target != actor->goal)
|
||||
if (P_LookForPlayers (actor, !(flags & CHF_DONTLOOKALLAROUND), nullptr) && actor->target != actor->goal)
|
||||
{ // got a new target
|
||||
actor->flags7 &= ~MF7_INCHASE;
|
||||
return;
|
||||
}
|
||||
if (actor->target == NULL)
|
||||
if (actor->target == nullptr)
|
||||
{
|
||||
if (flags & CHF_DONTIDLE || actor->flags & MF_FRIENDLY)
|
||||
{
|
||||
//A_Look(actor);
|
||||
if (actor->target == NULL)
|
||||
if (actor->target == nullptr)
|
||||
{
|
||||
if (!dontmove) A_Wander(actor);
|
||||
actor->flags7 &= ~MF7_INCHASE;
|
||||
|
@ -2470,6 +2490,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
}
|
||||
else
|
||||
{
|
||||
actor->ClearPath();
|
||||
actor->SetIdle();
|
||||
actor->flags7 &= ~MF7_INCHASE;
|
||||
return;
|
||||
|
@ -2493,9 +2514,31 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
|
|||
actor->flags7 &= ~MF7_INCHASE;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (actor->target && actor->flags9 & MF9_PATHING)
|
||||
{
|
||||
if (actor->goal && actor->goal->IsKindOf(NAME_PathNode))
|
||||
{
|
||||
AActor* temp = actor->target;
|
||||
actor->target = actor->goal;
|
||||
bool result = P_CheckMeleeRange(actor);
|
||||
actor->target = temp;
|
||||
|
||||
if (result) // TO DO
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (!actor->goal)
|
||||
{
|
||||
if (actor->Path.Size() < 1 && actor->Level->AStar(actor, actor->target))
|
||||
actor->goal = actor->Path[0];
|
||||
|
||||
}
|
||||
}
|
||||
// [RH] Don't attack if just moving toward goal
|
||||
if (actor->target == actor->goal || (actor->flags5&MF5_CHASEGOAL && actor->goal != NULL))
|
||||
else if (actor->target == actor->goal || (actor->flags5&MF5_CHASEGOAL && actor->goal != nullptr))
|
||||
{
|
||||
AActor * savedtarget = actor->target;
|
||||
actor->target = actor->goal;
|
||||
|
@ -2513,7 +2556,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, NULL, actor, false, spec->args[0],
|
||||
P_ExecuteSpecial(actor->Level, spec->special, nullptr, actor, false, spec->args[0],
|
||||
spec->args[1], spec->args[2], spec->args[3], spec->args[4]);
|
||||
}
|
||||
|
||||
|
@ -2536,6 +2579,7 @@ 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;
|
||||
|
|
|
@ -353,6 +353,8 @@ 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),
|
||||
|
||||
// Effect flags
|
||||
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
|
||||
|
|
|
@ -2125,6 +2125,7 @@ DEFINE_FIELD(AActor, LightLevel)
|
|||
DEFINE_FIELD(AActor, ShadowAimFactor)
|
||||
DEFINE_FIELD(AActor, ShadowPenaltyFactor)
|
||||
DEFINE_FIELD(AActor, AutomapOffsets)
|
||||
DEFINE_FIELD(AActor, Path)
|
||||
|
||||
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, thing);
|
||||
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, pos);
|
||||
|
|
|
@ -285,6 +285,7 @@ DoomEdNums
|
|||
14163 = MusicChanger, 63
|
||||
14164 = MusicChanger, 64
|
||||
14165 = MusicChanger
|
||||
14166 = PathNode
|
||||
32000 = DoomBuilderCamera
|
||||
}
|
||||
|
||||
|
|
|
@ -261,6 +261,7 @@ class Actor : Thinker native
|
|||
private native int InventoryID; // internal counter.
|
||||
native uint freezetics;
|
||||
native Vector2 AutomapOffsets;
|
||||
native Array<Actor> Path; // Cannot be cast to PathNode, unfortunately.
|
||||
|
||||
meta String Obituary; // Player was killed by this actor
|
||||
meta String HitObituary; // Player was killed by this actor in melee
|
||||
|
@ -697,7 +698,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();
|
||||
|
@ -705,6 +706,7 @@ class Actor : Thinker native
|
|||
native void ClearFOVInterpolation();
|
||||
native clearscope Vector3 PosRelative(sector sec) const;
|
||||
native void RailAttack(FRailParams p);
|
||||
native void ClearPath();
|
||||
|
||||
native void HandleSpawnFlags();
|
||||
native void ExplodeMissile(line lin = null, Actor target = null, bool onsky = false);
|
||||
|
|
|
@ -245,3 +245,110 @@ class SpeakerIcon : Unknown
|
|||
Scale 0.125;
|
||||
}
|
||||
}
|
||||
|
||||
//===============================================================
|
||||
// Path Nodes
|
||||
//===============================================================
|
||||
|
||||
class PathNode : Actor
|
||||
{
|
||||
// For non-connected paths. Stamina will be used to set this. Necessary for tele/portals.
|
||||
private int group;
|
||||
|
||||
Array<Actor> neighbors;
|
||||
|
||||
Default
|
||||
{
|
||||
//$Arg0 "TID 1"
|
||||
//$Arg1 "TID 2"
|
||||
//$Arg2 "TID 3"
|
||||
//$Arg3 "TID 4"
|
||||
//$Arg4 "TID 5"
|
||||
//$Arg0Type 14
|
||||
//$Arg1Type 14
|
||||
//$Arg2Type 14
|
||||
//$Arg3Type 14
|
||||
//$Arg4Type 14
|
||||
+NOBLOCKMAP
|
||||
+INVISIBLE
|
||||
+DONTSPLASH
|
||||
+NOTONAUTOMAP
|
||||
+NOGRAVITY // TO DO: Look into 3D variant for traversing up and down 3D floors and floating monsters.
|
||||
RenderStyle "None";
|
||||
MeleeRange 2048; // Sight checks limited to this. 0 = infinite.
|
||||
}
|
||||
|
||||
// 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()))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
|
@ -553,6 +553,8 @@ struct LevelLocals native
|
|||
|
||||
native void SpawnParticle(FSpawnParticleParams p);
|
||||
native VisualThinker SpawnVisualThinker(Class<VisualThinker> type);
|
||||
|
||||
native bool AStar(Actor chaser, Actor target, Actor startnode = null, Actor goalnode = null);
|
||||
}
|
||||
|
||||
// a few values of this need to be readable by the play code.
|
||||
|
|
|
@ -435,6 +435,7 @@ 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