diff --git a/src/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index cbfdcb1f98..3b8c5ca3b4 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -1663,6 +1663,7 @@ MapFlagHandlers[] = { "enableskyboxao", MITYPE_SETFLAG3, LEVEL3_SKYBOXAO, 0 }, { "disableskyboxao", MITYPE_CLRFLAG3, LEVEL3_SKYBOXAO, 0 }, { "avoidmelee", MITYPE_SETFLAG3, LEVEL3_AVOIDMELEE, 0 }, + { "avoidhazards", MITYPE_SETFLAG3, LEVEL3_AVOID_HAZARDS, 0 }, { "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes { "compat_shorttex", MITYPE_COMPATFLAG, COMPATF_SHORTTEX, 0 }, { "compat_stairs", MITYPE_COMPATFLAG, COMPATF_STAIRINDEX, 0 }, diff --git a/src/gamedata/g_mapinfo.h b/src/gamedata/g_mapinfo.h index 9f74728542..ecb6ba0e6f 100644 --- a/src/gamedata/g_mapinfo.h +++ b/src/gamedata/g_mapinfo.h @@ -260,6 +260,7 @@ enum ELevelFlags : unsigned int LEVEL3_NOSHADOWMAP = 0x00010000, // disables shadowmaps for a given level. LEVEL3_AVOIDMELEE = 0x00020000, // global flag needed for proper MBF support. LEVEL3_NOJUMPDOWN = 0x00040000, // only for MBF21. Inverse of MBF's dog_jumping flag. + LEVEL3_AVOID_HAZARDS = 0x00080000, // another MBF thing. }; diff --git a/src/playsim/actor.h b/src/playsim/actor.h index d2a5d75195..bfc1cf5664 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -421,6 +421,7 @@ enum ActorFlag8 MF8_E4M6BOSS = 0x00200000, // MBF21 boss death. MF8_MAP07BOSS1 = 0x00400000, // MBF21 boss death. MF8_MAP07BOSS2 = 0x00800000, // MBF21 boss death. + MF8_AVOIDHAZARDS = 0x01000000, // MBF AI enhancement. }; // --- mobj.renderflags --- diff --git a/src/playsim/mapthinkers/a_ceiling.h b/src/playsim/mapthinkers/a_ceiling.h index d1db8b70a8..2954fb1185 100644 --- a/src/playsim/mapthinkers/a_ceiling.h +++ b/src/playsim/mapthinkers/a_ceiling.h @@ -54,6 +54,9 @@ public: void Serialize(FSerializer &arc); void Tick (); + int getCrush() const { return m_Crush; } + int getDirection() const { return m_Direction; } + protected: ECeiling m_Type; double m_BottomHeight; diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index baa97f83ce..6b692e2294 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -49,6 +49,7 @@ #include "g_levellocals.h" #include "vm.h" #include "actorinlines.h" +#include "a_ceiling.h" #include "gi.h" @@ -67,6 +68,7 @@ static FRandom pr_look3 ("IGotHooky"); static FRandom pr_slook ("SlooK"); static FRandom pr_dropoff ("Dropoff"); static FRandom pr_defect ("Defect"); +static FRandom pr_avoidcrush("AvoidCrush"); static FRandom pr_skiptarget("SkipTarget"); static FRandom pr_enemystrafe("EnemyStrafe"); @@ -414,13 +416,41 @@ int P_HitFriend(AActor * self) return false; } +/* + * P_IsUnderDamage + * + * killough 9/9/98: + * + * Returns nonzero if the object is under damage based on + * their current position. Returns 1 if the damage is moderate, + * -1 if it is serious. Used for AI. + */ + +static int P_IsUnderDamage(AActor* actor) +{ + msecnode_t* seclist; + int dir = 0; + for (seclist = actor->touching_sectorlist; seclist; seclist = seclist->m_tnext) + { + DSectorEffect* e = seclist->m_sector->ceilingdata; + if (e && e->IsKindOf(RUNTIME_CLASS(DCeiling))) + { + auto cl = (DCeiling*)e; + if (cl->getCrush() > 0) // unlike MBF we need to consider non-crushing ceiling movers here. + dir |= cl->getDirection(); + } + // Q: consider crushing 3D floors too? + } + return dir; +} + // // P_Move // Move in the current direction, // returns false if the move is blocked. // -int P_Move (AActor *actor) +static int P_Move (AActor *actor) { double tryx, tryy, deltax, deltay, origx, origy; @@ -653,6 +683,47 @@ int P_Move (AActor *actor) return true; } +// +// P_SmartMove +// +// killough 9/12/98: Same as P_Move, except smarter +// + +int P_SmartMove(AActor* actor) +{ + AActor* target = actor->target; + int on_lift = false, dropoff = false, under_damage; + bool monster_avoid_hazards = (actor->Level->flags3 & LEVEL3_AVOID_HAZARDS) || (actor->flags8 & MF8_AVOIDHAZARDS); + +#if 0 + /* killough 9/12/98: Stay on a lift if target is on one */ + on_lift = !comp[comp_staylift] + && target && target->health > 0 + && target->subsector->sector->tag == actor->subsector->sector->tag && + P_IsOnLift(actor); +#endif + + under_damage = monster_avoid_hazards && P_IsUnderDamage(actor) != 0;//e6y + + if (!P_Move(actor)) + return false; + + // killough 9/9/98: avoid crushing ceilings or other damaging areas + if ( +#if 0 + (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift + !P_IsOnLift(actor)) + || +#endif + (monster_avoid_hazards && !under_damage && //e6y // Get away from damage + (under_damage = P_IsUnderDamage(actor)) && + (under_damage < 0 || pr_avoidcrush() < 200)) + ) + actor->movedir = DI_NODIR; // avoid the area (most of the time anyway) + + return true; +} + //============================================================================= // // TryWalk @@ -669,7 +740,7 @@ int P_Move (AActor *actor) bool P_TryWalk (AActor *actor) { - if (!P_Move (actor)) + if (!P_SmartMove (actor)) { return false; } @@ -2123,7 +2194,7 @@ void A_Wander(AActor *self, int flags) } } - if ((--self->movecount < 0 && !(flags & CHF_NORANDOMTURN)) || (!P_Move(self) && !(flags & CHF_STOPIFBLOCKED))) + if ((--self->movecount < 0 && !(flags & CHF_NORANDOMTURN)) || (!P_SmartMove(self) && !(flags & CHF_STOPIFBLOCKED))) { P_RandomChaseDir(self); self->movecount += 5; @@ -2525,7 +2596,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi FTextureID oldFloor = actor->floorpic; // chase towards player - if ((--actor->movecount < 0 && !(flags & CHF_NORANDOMTURN)) || (!P_Move(actor) && !(flags & CHF_STOPIFBLOCKED))) + if ((--actor->movecount < 0 && !(flags & CHF_NORANDOMTURN)) || (!P_SmartMove(actor) && !(flags & CHF_STOPIFBLOCKED))) { P_NewChaseDir(actor); } diff --git a/src/playsim/p_enemy.h b/src/playsim/p_enemy.h index c1b425f096..282760e5e6 100644 --- a/src/playsim/p_enemy.h +++ b/src/playsim/p_enemy.h @@ -50,7 +50,7 @@ int P_HitFriend (AActor *self); void P_NoiseAlert (AActor *emmiter, AActor *target, bool splash=false, double maxdist=0); bool P_CheckMeleeRange2 (AActor *actor); -int P_Move (AActor *actor); +int P_SmartMove (AActor *actor); bool P_TryWalk (AActor *actor); void P_NewChaseDir (AActor *actor); void P_RandomChaseDir(AActor *actor);; diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index bd2579244e..e3c1f7ed8b 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -336,6 +336,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF8, E4M6BOSS, AActor, flags8), DEFINE_FLAG(MF8, MAP07BOSS1, AActor, flags8), DEFINE_FLAG(MF8, MAP07BOSS2, AActor, flags8), + DEFINE_FLAG(MF8, AVOIDHAZARDS, AActor, flags8), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index 378496b0c0..2f5c26e9d4 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -1443,10 +1443,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, HitFriend, P_HitFriend) ACTION_RETURN_BOOL(P_HitFriend(self)); } -DEFINE_ACTION_FUNCTION_NATIVE(AActor, MonsterMove, P_Move) +DEFINE_ACTION_FUNCTION_NATIVE(AActor, MonsterMove, P_SmartMove) { PARAM_SELF_PROLOGUE(AActor); - ACTION_RETURN_BOOL(P_Move(self)); + ACTION_RETURN_BOOL(P_SmartMove(self)); } DEFINE_ACTION_FUNCTION_NATIVE(AActor, NewChaseDir, P_NewChaseDir)