This commit is contained in:
nashmuhandes 2024-03-16 04:37:28 +08:00
commit a72057945b
24 changed files with 65 additions and 579 deletions

View file

@ -98,7 +98,6 @@
#include "s_music.h"
#include "fragglescript/t_script.h"
#include "texturemanager.h"
void STAT_StartNewGame(const char *lev);
@ -2473,188 +2472,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;
}

View file

@ -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.

View file

@ -1823,7 +1823,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 },
{ "nousersave", MITYPE_SETVKDFLAG, VKDLEVELFLAG_NOUSERSAVE, 0 },
{ "noautomap", MITYPE_SETVKDFLAG, VKDLEVELFLAG_NOAUTOMAP, 0 },
{ "noautosaveonenter", MITYPE_SETVKDFLAG, VKDLEVELFLAG_NOAUTOSAVEONENTER, 0 },

View file

@ -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
// VKDoom custom flags
VKDLEVELFLAG_NOUSERSAVE = 0x00000001,

View file

@ -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

View file

@ -202,7 +202,6 @@ xx(Cast) // 'damage type' for the cast call
xx(MapSpot)
xx(PatrolPoint)
xx(PatrolSpecial)
xx(PathNode)
xx(Communicator)
xx(PowerScanner)

View file

@ -326,7 +326,6 @@ void FLevelLocals::ClearLevelData(bool fullgc)
}
ClearPortals();
PathNodes.Clear();
tagManager.Clear();
ClearTIDHashes();
if (SpotState) SpotState->Destroy();

View file

@ -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;
@ -1369,6 +1360,10 @@ public:
int SpawnTime;
uint32_t SpawnOrder;
// landing speed from a jump with normal gravity (squats the player's view)
// (note: this is put into AActor instead of the PlayerPawn because non-players also use the value)
double LandingSpeed;
double SourceRadius = 5.0; // Light source radius
// ThingIDs

View file

@ -5165,7 +5165,7 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
if(animStart == FErr_NotFound)
{
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE;
Printf("Could not find animation %s", animName.GetChars());
Printf("Could not find animation %s\n", animName.GetChars());
return;
}
int animEnd = mdl->FindLastFrame(animName);
@ -5180,13 +5180,13 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
if(startFrame >= len)
{
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE;
Printf("frame %d is past the end of animation %s", startFrame, animName.GetChars());
Printf("frame %d is past the end of animation %s\n", startFrame, animName.GetChars());
return;
}
else if(loopFrame >= len)
{
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE;
Printf("frame %d is past the end of animation %s", startFrame, animName.GetChars());
Printf("frame %d is past the end of animation %s\n", startFrame, animName.GetChars());
return;
}
@ -5330,7 +5330,7 @@ void ChangeModelNative(
if (n_modeldef != NAME_None && (modeldef = PClass::FindActor(n_modeldef.GetChars())) == nullptr)
{
Printf("Attempt to pass invalid modeldef name %s in %s.", n_modeldef.GetChars(), self->GetCharacterName());
Printf("Attempt to pass invalid modeldef name %s in %s.\n", n_modeldef.GetChars(), self->GetCharacterName());
return;
}

View file

@ -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,81 +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))
{
if (!target)
return true;
if (!target->flags8 & MF8_FRIGHTENING)
return (!target->player || !(target->player->cheats & CF_FRIGHTENING));
}
}
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
@ -2420,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;
@ -2499,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;
@ -2558,7 +2470,6 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi
}
else
{
actor->ClearPath();
actor->SetIdle();
actor->flags7 &= ~MF7_INCHASE;
return;
@ -2582,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;
@ -2606,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]);
}
@ -2629,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;
@ -2687,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;
@ -2719,43 +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;
if (sight == 0)
{
if (pnode && !(actor->goal->flags & MF_AMBUSH))
{
AActor* temp = actor->target;
actor->target = actor->goal;
bool 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
//
@ -2774,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!)
@ -2790,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--;

View file

@ -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);
}

View file

@ -391,7 +391,8 @@ void AActor::Serialize(FSerializer &arc)
A("lightlevel", LightLevel)
A("userlights", UserLights)
A("WorldOffset", WorldOffset)
("modelData", modelData);
("modelData", modelData)
A("LandingSpeed", LandingSpeed);
SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain);
SerializeArgs(arc, "args", args, def->args, special);
@ -2584,11 +2585,9 @@ static void P_ZMovement (AActor *mo, double oldfloorz)
mo->SetZ(mo->floorz);
if (mo->Vel.Z < 0)
{
const double minvel = -8; // landing speed from a jump with normal gravity
// Spawn splashes, etc.
P_HitFloor (mo);
if (mo->DamageType == NAME_Ice && mo->Vel.Z < minvel)
if (mo->DamageType == NAME_Ice && mo->Vel.Z < mo->LandingSpeed)
{
mo->tics = 1;
mo->Vel.Zero();
@ -2601,11 +2600,11 @@ static void P_ZMovement (AActor *mo, double oldfloorz)
}
if (mo->player)
{
if (mo->player->jumpTics < 0 || mo->Vel.Z < minvel)
if (mo->player->jumpTics < 0 || mo->Vel.Z < mo->LandingSpeed)
{ // delay any jumping for a short while
mo->player->jumpTics = 7;
}
if (mo->Vel.Z < minvel && !(mo->flags & MF_NOGRAVITY))
if (mo->Vel.Z < mo->LandingSpeed && !(mo->flags & MF_NOGRAVITY))
{
// Squat down.
// Decrease viewheight for a moment after hitting the ground (hard),

View file

@ -1141,7 +1141,7 @@ FSpriteModelFrame * FindModelFrame(const AActor * thing, int sprite, int frame,
if(thing->flags9 & MF9_DECOUPLEDANIMATIONS)
{
return &BaseSpriteModelFrames[(thing->modelData != nullptr && thing->modelData->modelDef != nullptr) ? thing->modelData->modelDef : thing->GetClass()];
return BaseSpriteModelFrames.CheckKey((thing->modelData != nullptr && thing->modelData->modelDef != nullptr) ? thing->modelData->modelDef : thing->GetClass());
}
else
{

View file

@ -466,18 +466,13 @@ bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp)
const bool useOffsets = (actor != nullptr) && !(actor->renderflags & RF_ROLLCENTER);
FVector2 offset = FVector2( offx, offy );
// Account for +ROLLCENTER flag. Takes the embedded image offsets and adds them in with SpriteOffsets.
if (drawRollSpriteActor && useOffsets)
{
offset.X += center.X - x;
offset.Y += center.Z - z;
}
float xx = -center.X + x;
float yy = -center.Y + y;
float zz = -center.Z + z;
// [Nash] check for special sprite drawing modes
if (drawWithXYBillboard || isWallSprite)
{
// Compute center of sprite
mat.MakeIdentity();
mat.Translate(center.X, center.Z, center.Y); // move to sprite center
@ -512,16 +507,21 @@ bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp)
mat.Rotate(0, 1, 0, 0);
if (drawRollSpriteActor)
{
if (useOffsets) mat.Translate(xx, zz, yy);
mat.Rotate(yawvecX, 0, yawvecY, rollDegrees);
if (useOffsets) mat.Translate(-xx, -zz, -yy);
}
}
else if (doRoll)
{
if (useOffsets) mat.Translate(xx, zz, yy);
if (drawWithXYBillboard)
{
mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees());
}
mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees);
if (useOffsets) mat.Translate(-xx, -zz, -yy);
}
else if (drawWithXYBillboard)
{
@ -554,7 +554,9 @@ bool HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp)
float rollDegrees = Angles.Roll.Degrees();
mat.Translate(center.X, center.Z, center.Y);
if (useOffsets) mat.Translate(xx, zz, yy);
mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees);
if (useOffsets) mat.Translate(-xx, -zz, -yy);
mat.Translate(-center.X, -center.Z, -center.Y);
}

View file

@ -598,9 +598,9 @@ void R_InterpolateView(FRenderViewpoint& viewPoint, const player_t* const player
}
else
{
viewPoint.Angles.Pitch = iView->Old.Angles.Pitch * inverseTicFrac + iView->New.Angles.Pitch * ticFrac;
viewPoint.Angles.Yaw = prevYaw * inverseTicFrac + curYaw * ticFrac;
viewPoint.Angles.Roll = iView->Old.Angles.Roll * inverseTicFrac + iView->New.Angles.Roll * ticFrac;
viewPoint.Angles.Pitch = iView->Old.Angles.Pitch + deltaangle(iView->Old.Angles.Pitch, iView->New.Angles.Pitch) * ticFrac;
viewPoint.Angles.Yaw = prevYaw + deltaangle(prevYaw, curYaw) * ticFrac;
viewPoint.Angles.Roll = iView->Old.Angles.Roll + deltaangle(iView->Old.Angles.Roll, iView->New.Angles.Roll) * ticFrac;
}
// Now that the base position and angles are set, add offsets.
@ -624,9 +624,9 @@ void R_InterpolateView(FRenderViewpoint& viewPoint, const player_t* const player
{
if (player == nullptr || (player->cheats & CF_INTERPVIEWANGLES))
{
viewPoint.Angles.Yaw += iView->Old.ViewAngles.Yaw * inverseTicFrac + iView->New.ViewAngles.Yaw * ticFrac;
viewPoint.Angles.Pitch += iView->Old.ViewAngles.Pitch * inverseTicFrac + iView->New.ViewAngles.Pitch * ticFrac;
viewPoint.Angles.Roll += iView->Old.ViewAngles.Roll * inverseTicFrac + iView->New.ViewAngles.Roll * ticFrac;
viewPoint.Angles.Yaw += iView->Old.ViewAngles.Yaw + deltaangle(iView->Old.ViewAngles.Yaw, iView->New.ViewAngles.Yaw) * ticFrac;
viewPoint.Angles.Pitch += iView->Old.ViewAngles.Pitch + deltaangle(iView->Old.ViewAngles.Pitch, iView->New.ViewAngles.Pitch) * ticFrac;
viewPoint.Angles.Roll += iView->Old.ViewAngles.Roll + deltaangle(iView->Old.ViewAngles.Roll, iView->New.ViewAngles.Roll) * ticFrac;
}
else
{

View file

@ -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),

View file

@ -2787,7 +2787,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)
@ -2855,7 +2854,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_BIT(FLevelLocals, vkdflags, nousersave, VKDLEVELFLAG_NOUSERSAVE)
DEFINE_FIELD_BIT(FLevelLocals, vkdflags, noautomap, VKDLEVELFLAG_NOAUTOMAP)

View file

@ -2125,7 +2125,7 @@ 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);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, pos);

View file

@ -115,6 +115,6 @@ mapbind joy2 am_clearmarks
mapbind pov1right +am_panright
mapbind pov1left +am_panleft
mapbind pov1up +am_panup
mapbind pov2down +am_pandown
mapbind pov1down +am_pandown
mapbind joy5 +am_zoomout
mapbind joy6 +am_zoomin

View file

@ -44,7 +44,6 @@ DoomEdNums
5065 = InvisibleBridge8
9001 = MapSpot
9013 = MapSpotGravity
9022 = PathNode
9024 = PatrolPoint
9025 = SecurityCamera
9026 = Spark

View file

@ -261,7 +261,7 @@ 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
meta String HitObituary; // Player was killed by this actor in melee
@ -367,6 +367,7 @@ class Actor : Thinker native
property ShadowAimFactor: ShadowAimFactor;
property ShadowPenaltyFactor: ShadowPenaltyFactor;
property AutomapOffsets : AutomapOffsets;
property LandingSpeed: LandingSpeed;
// need some definition work first
//FRenderStyle RenderStyle;
@ -455,6 +456,7 @@ class Actor : Thinker native
RenderHidden 0;
RenderRequired 0;
FriendlySeeBlocks 10; // 10 (blocks) * 128 (one map unit block)
LandingSpeed -8; // landing speed from a jump with normal gravity (squats the player's view)
}
// Functions
@ -664,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);
@ -698,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();
@ -797,72 +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;
// 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);

View file

@ -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));
}
}

View file

@ -409,7 +409,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;
@ -477,7 +477,6 @@ struct LevelLocals native
native readonly int compatflags;
native readonly int compatflags2;
native readonly LevelInfo info;
native readonly bool pathing;
native bool nousersave;
native bool noautomap;
@ -558,9 +557,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.

View file

@ -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;