diff --git a/src/d_main.cpp b/src/d_main.cpp index 842b1e9f4..556664662 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -703,7 +703,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) case 5: // MBF compat mode v = COMPATF_TRACE | COMPATF_SOUNDTARGET | COMPATF_BOOMSCROLL | COMPATF_MISSILECLIP | COMPATF_MUSHROOM | COMPATF_MBFMONSTERMOVE | COMPATF_NOBLOCKFRIENDS | COMPATF_MASKEDMIDTEX; - w = COMPATF2_EXPLODE1; + w = COMPATF2_EXPLODE1 | COMPATF2_AVOID_HAZARDS | COMPATF2_STAYONLIFT; break; case 6: // Boom with some added settings to reenable some 'broken' behavior @@ -716,7 +716,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) v = COMPATF_CORPSEGIBS | COMPATF_NOBLOCKFRIENDS | COMPATF_MBFMONSTERMOVE | COMPATF_INVISIBILITY | COMPATF_NOTOSSDROPS | COMPATF_MUSHROOM | COMPATF_NO_PASSMOBJ | COMPATF_BOOMSCROLL | COMPATF_WALLRUN | COMPATF_TRACE | COMPATF_HITSCAN | COMPATF_MISSILECLIP | COMPATF_MASKEDMIDTEX | COMPATF_SOUNDTARGET; - w = COMPATF2_POINTONLINE | COMPATF2_EXPLODE1 | COMPATF2_EXPLODE2; + w = COMPATF2_POINTONLINE | COMPATF2_EXPLODE1 | COMPATF2_EXPLODE2 | COMPATF2_AVOID_HAZARDS | COMPATF2_STAYONLIFT; break; } compatflags = v; @@ -766,6 +766,8 @@ CVAR (Flag, compat_checkswitchrange, compatflags2, COMPATF2_CHECKSWITCHRANGE); CVAR (Flag, compat_explode1, compatflags2, COMPATF2_EXPLODE1); CVAR (Flag, compat_explode2, compatflags2, COMPATF2_EXPLODE2); CVAR (Flag, compat_railing, compatflags2, COMPATF2_RAILING); +CVAR (Flag, compat_avoidhazard, compatflags2, COMPATF2_AVOID_HAZARDS); +CVAR (Flag, compat_stayonlift, compatflags2, COMPATF2_STAYONLIFT); CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) diff --git a/src/doomdef.h b/src/doomdef.h index 1adf81d7d..05e7ebb95 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -222,6 +222,9 @@ enum : unsigned int COMPATF2_EXPLODE2 = 1 << 9, // Use original explosion code throughout. COMPATF2_RAILING = 1 << 10, // Bugged Strife railings. COMPATF2_SCRIPTWAIT = 1 << 11, // Use old scriptwait implementation where it doesn't wait on a non-running script. + COMPATF2_AVOID_HAZARDS = 1 << 12, // another MBF thing. + COMPATF2_STAYONLIFT = 1 << 13, // yet another MBF thing. + }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index 3b8c5ca3b..3b823c59e 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -1663,7 +1663,6 @@ 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 }, @@ -1705,6 +1704,8 @@ MapFlagHandlers[] = { "compat_explode2", MITYPE_COMPATFLAG, 0, COMPATF2_EXPLODE2 }, { "compat_railing", MITYPE_COMPATFLAG, 0, COMPATF2_RAILING }, { "compat_scriptwait", MITYPE_COMPATFLAG, 0, COMPATF2_SCRIPTWAIT }, + { "compat_avoidhazards", MITYPE_COMPATFLAG, 0, COMPATF2_AVOID_HAZARDS }, + { "compat_stayonlift", MITYPE_COMPATFLAG, 0, COMPATF2_STAYONLIFT }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/src/gamedata/g_mapinfo.h b/src/gamedata/g_mapinfo.h index ecb6ba0e6..9f7472854 100644 --- a/src/gamedata/g_mapinfo.h +++ b/src/gamedata/g_mapinfo.h @@ -260,7 +260,6 @@ 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/gamedata/r_defs.h b/src/gamedata/r_defs.h index fa4a30227..d7fb686cf 100644 --- a/src/gamedata/r_defs.h +++ b/src/gamedata/r_defs.h @@ -485,6 +485,7 @@ enum SECMF_HIDDEN = 256, // Do not draw on textured automap 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 }; enum diff --git a/src/maploader/specials.cpp b/src/maploader/specials.cpp index 2024954f7..280eba279 100644 --- a/src/maploader/specials.cpp +++ b/src/maploader/specials.cpp @@ -787,6 +787,27 @@ void MapLoader::SpawnSpecials () SpawnLinePortal(&line); break; + // partial support for MBF's stay-on-lift feature. + // Unlike MBF we cannot scan all lines for a proper special each time because it'd take too long. + // So instead, set the info here, but only for repeatable lifts to keep things simple. + // This also cannot consider lifts triggered by scripts etc. + case Generic_Lift: + if (line.args[3] != 1) continue; + case Plat_DownWaitUpStay: + case Plat_DownWaitUpStayLip: + case Plat_UpWaitDownStay: + case Plat_UpNearestWaitDownStay: + if (line.flags & ML_REPEAT_SPECIAL) + { + auto it = Level->GetSectorTagIterator(line.args[0], &line); + int secno; + while ((secno = it.Next()) != -1) + { + Level->sectors[secno].MoreFlags |= SECMF_LIFT; + } + } + break; + // [RH] ZDoom Static_Init settings case Static_Init: switch (line.args[1]) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 61b65b593..e604cbfeb 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -422,6 +422,7 @@ enum ActorFlag8 MF8_MAP07BOSS1 = 0x00400000, // MBF21 boss death. MF8_MAP07BOSS2 = 0x00800000, // MBF21 boss death. MF8_AVOIDHAZARDS = 0x01000000, // MBF AI enhancement. + MF8_STAYONLIFT = 0x02000000, // MBF AI enhancement. }; // --- mobj.renderflags --- diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index 1432fa67d..b877bcd12 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -69,6 +69,7 @@ static FRandom pr_slook ("SlooK"); static FRandom pr_dropoff ("Dropoff"); static FRandom pr_defect ("Defect"); static FRandom pr_avoidcrush("AvoidCrush"); +static FRandom pr_stayonlift("StayOnLift"); static FRandom pr_skiptarget("SkipTarget"); static FRandom pr_enemystrafe("EnemyStrafe"); @@ -409,6 +410,33 @@ int P_HitFriend(AActor * self) return false; } +/* + * P_IsOnLift + * + * killough 9/9/98: + * + * Returns true if the object is on a lift. Used for AI, + * since it may indicate the need for crowded conditions, + * or that a monster should stay on the lift for a while + * while it goes up or down. + */ + +static bool P_IsOnLift(const AActor* actor) +{ + sector_t* sec = actor->Sector; + + // Short-circuit: it's on a lift which is active. + DSectorEffect* e = sec->floordata; + if (e && e->IsKindOf(RUNTIME_CLASS(DPlat))) + return true; + + // Check to see if it's in a sector which can be activated as a lift. + // This is a bit more restrictive than MBF as it only considers repeatable lifts moving from A->B->A and stop. + // Other types of movement are not easy to detect with the more complex map setup + // and also do not really make sense in this context unless they are actually active + return !!(sec->MoreFlags & SECMF_LIFT); +} + /* * P_IsUnderDamage * @@ -437,6 +465,20 @@ static int P_IsUnderDamage(AActor* actor) return dir; } +// +// P_CheckTags +// Checks if 2 sectors share the same primary activation tag +// + +bool P_CheckTags(sector_t* sec1, sector_t* sec2) +{ + auto Level = sec1->Level; + if (!Level->SectorHasTags(sec1) || !Level->SectorHasTags(sec2)) return sec1 == sec2; + if (Level->GetFirstSectorTag(sec1) == Level->GetFirstSectorTag(sec2)) return true; + // todo: check secondary tags as well. + return false; +} + // // P_Move // Move in the current direction, @@ -686,15 +728,12 @@ 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); + bool monster_avoid_hazards = (actor->Level->i_compatflags2 & COMPATF2_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 + on_lift = ((actor->flags8 & MF8_STAYONLIFT) || (actor->Level->i_compatflags2 & COMPATF2_STAYONLIFT)) + && target && target->health > 0 && P_IsOnLift(actor) + && P_CheckTags(target->Sector, actor->Sector); under_damage = monster_avoid_hazards && P_IsUnderDamage(actor) != 0;//e6y @@ -703,11 +742,9 @@ int P_SmartMove(AActor* actor) // killough 9/9/98: avoid crushing ceilings or other damaging areas if ( -#if 0 - (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift + (on_lift && 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))