Skip to content

Commit

Permalink
Begin adding PathNodes.
Browse files Browse the repository at this point in the history
  • Loading branch information
MajorCooke authored and madame-rachelle committed Mar 5, 2024
1 parent 3033faf commit b2cb4b0
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 10 deletions.
104 changes: 104 additions & 0 deletions src/g_level.cpp
Expand Up @@ -98,6 +98,7 @@
#include "s_music.h"
#include "fragglescript/t_script.h"


#include "texturemanager.h"

void STAT_StartNewGame(const char *lev);
Expand Down Expand Up @@ -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);
}
3 changes: 3 additions & 0 deletions src/g_levellocals.h
Expand Up @@ -444,6 +444,9 @@ struct FLevelLocals

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.
Expand Down
1 change: 1 addition & 0 deletions src/gamedata/r_defs.h
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/namedef_custom.h
Expand Up @@ -202,6 +202,7 @@ xx(Cast) // 'damage type' for the cast call
xx(MapSpot)
xx(PatrolPoint)
xx(PatrolSpecial)
xx(PathNode)
xx(Communicator)
xx(PowerScanner)

Expand Down
2 changes: 2 additions & 0 deletions src/p_setup.cpp
Expand Up @@ -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);
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/playsim/actor.h
Expand Up @@ -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 ---
Expand Down Expand Up @@ -1101,6 +1103,8 @@ class AActor final : public DThinker
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)
Expand Down Expand Up @@ -1157,6 +1161,7 @@ class AActor final : public DThinker
TObjPtr<DBoneComponents*> boneComponentData;

// interaction info
TArray<TObjPtr<AActor*> > Path;
FBlockNode *BlockNode; // links in blocks (if needed)
struct sector_t *Sector;
subsector_t * subsector;
Expand Down
60 changes: 52 additions & 8 deletions src/playsim/p_enemy.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -2443,25 +2463,25 @@ 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;
// Switch targets faster, since we're only changing because we can't
// 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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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]);
}

Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/scripting/thingdef_data.cpp
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions src/scripting/vmthunks_actors.cpp
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions wadsrc/static/mapinfo/common.txt
Expand Up @@ -285,6 +285,7 @@ DoomEdNums
14163 = MusicChanger, 63
14164 = MusicChanger, 64
14165 = MusicChanger
14166 = PathNode
32000 = DoomBuilderCamera
}

Expand Down
4 changes: 3 additions & 1 deletion wadsrc/static/zscript/actors/actor.zs
Expand Up @@ -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
Expand Down Expand Up @@ -697,14 +698,15 @@ 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();
native void ClearInterpolation();
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);
Expand Down

0 comments on commit b2cb4b0

Please sign in to comment.