From b461e263bbf2c56f33e727ff387d995f61a03b9c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 22 Apr 2017 12:50:26 +0200 Subject: [PATCH] - added custom boss death actions. These work roughly like MBF's A_LineEffect, but are a bit more thorough about what is allowed and avoid hacking the first player to trigger the action. For the definition it uses the same class names as ZDoom because that is the closest to a naming standard there is. --- prboom2/src/p_enemy.c | 54 +++++++++- prboom2/src/p_map.c | 4 +- prboom2/src/p_setup.c | 2 +- prboom2/src/p_spec.c | 31 +++--- prboom2/src/p_spec.h | 5 +- prboom2/src/p_switch.c | 52 +++++++-- prboom2/src/s_sound.c | 2 +- prboom2/src/umapinfo.c | 233 +++++++++++++++++++++++++++++++++++++++++ prboom2/src/umapinfo.h | 9 ++ 9 files changed, 358 insertions(+), 34 deletions(-) diff --git a/prboom2/src/p_enemy.c b/prboom2/src/p_enemy.c index e4a84228e..f283040e5 100644 --- a/prboom2/src/p_enemy.c +++ b/prboom2/src/p_enemy.c @@ -406,7 +406,7 @@ static dboolean P_Move(mobj_t *actor, dboolean dropoff) /* killough 9/12/98 */ */ for (good = false; numspechit--; ) - if (P_UseSpecialLine(actor, spechit[numspechit], 0)) + if (P_UseSpecialLine(actor, spechit[numspechit], 0, false)) good |= spechit[numspechit] == blockline ? 1 : 2; /* cph - compatibility maze here @@ -2223,6 +2223,54 @@ void A_BossDeath(mobj_t *mo) line_t junk; int i; + // numbossactions == 0 means to use the defaults. + // numbossactions == -1 means to do nothing. + // positive values mean to check the list of boss actions and run all that apply. + if (gamemapinfo && gamemapinfo->numbossactions != 0) + { + if (gamemapinfo->numbossactions < 0) return; + + // make sure there is a player alive for victory + for (i=0; i 0) + break; + + if (i==MAXPLAYERS) + return; // no one left alive, so do not end game + + for (i = 0; i < gamemapinfo->numbossactions; i++) + { + if (gamemapinfo->bossactions[i].type == mo->type) + break; + } + if (i >= gamemapinfo->numbossactions) + return; // no matches found + + // scan the remaining thinkers to see + // if all bosses are dead + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other boss not dead + } + for (i = 0; i < gamemapinfo->numbossactions; i++) + { + if (gamemapinfo->bossactions[i].type == mo->type) + { + junk = *lines; + junk.special = (short)gamemapinfo->bossactions[i].special; + junk.tag = (short)gamemapinfo->bossactions[i].tag; + // use special semantics for line activation to block problem types. + if (!P_UseSpecialLine(mo, &junk, 0, true)) + P_CrossSpecialLine(&junk, 0, mo, true); + } + } + + return; + } + if (gamemode == commercial) { if (gamemap != 7) @@ -2773,8 +2821,8 @@ void A_LineEffect(mobj_t *mo) if (!junk.special) return; junk.tag = (short)mo->state->misc2; - if (!P_UseSpecialLine(mo, &junk, 0)) - P_CrossSpecialLine(&junk, 0, mo); + if (!P_UseSpecialLine(mo, &junk, 0, false)) + P_CrossSpecialLine(&junk, 0, mo, false); mo->state->misc1 = junk.special; mo->player = oldplayer; } diff --git a/prboom2/src/p_map.c b/prboom2/src/p_map.c index a442add98..762cf099c 100644 --- a/prboom2/src/p_map.c +++ b/prboom2/src/p_map.c @@ -956,7 +956,7 @@ dboolean P_TryMove(mobj_t* thing,fixed_t x,fixed_t y, int oldside; if ((oldside = P_PointOnLineSide(oldx, oldy, spechit[numspechit])) != P_PointOnLineSide(thing->x, thing->y, spechit[numspechit])) - P_CrossSpecialLine(spechit[numspechit], oldside, thing); + P_CrossSpecialLine(spechit[numspechit], oldside, thing, false); } return true; @@ -1789,7 +1789,7 @@ dboolean PTR_UseTraverse (intercept_t* in) // return false; // don't use back side - P_UseSpecialLine (usething, in->d.line, side); + P_UseSpecialLine (usething, in->d.line, side, false); //WAS can't use for than one special line in a row //jff 3/21/98 NOW multiple use allowed with enabling line flag diff --git a/prboom2/src/p_setup.c b/prboom2/src/p_setup.c index a14adef67..05fb9e2ac 100644 --- a/prboom2/src/p_setup.c +++ b/prboom2/src/p_setup.c @@ -221,7 +221,7 @@ static dboolean CheckForIdentifier(int lumpnum, const byte *id, size_t length) { dboolean result = false; - if (W_LumpLength(lumpnum) >= length) + if (W_LumpLength(lumpnum) >= (int)length) { const char *data = W_CacheLumpNum(lumpnum); diff --git a/prboom2/src/p_spec.c b/prboom2/src/p_spec.c index 30283644d..6e84b259b 100644 --- a/prboom2/src/p_spec.c +++ b/prboom2/src/p_spec.c @@ -1221,7 +1221,7 @@ dboolean PUREFUNC P_WasSecret(const sector_t *sec) // crossed. Change is qualified by demo_compatibility. // // CPhipps - take a line_t pointer instead of a line number, as in MBF -void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing, dboolean bossaction) { int ok; @@ -1237,7 +1237,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } else { - if (!thing->player) + if (!thing->player && !bossaction) { // Things that should NOT trigger specials... switch(thing->type) @@ -1270,7 +1270,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } else if ((unsigned)line->special >= GenFloorBase) { - if (!thing->player) + if (!thing->player && !bossaction) if ((line->special & FloorChange) || !(line->special & FloorModel)) return; // FloorModel is "Allow Monsters" if FloorChange is 0 if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag @@ -1279,7 +1279,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } else if ((unsigned)line->special >= GenCeilingBase) { - if (!thing->player) + if (!thing->player && !bossaction) if ((line->special & CeilingChange) || !(line->special & CeilingModel)) return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag @@ -1288,7 +1288,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } else if ((unsigned)line->special >= GenDoorBase) { - if (!thing->player) + if (!thing->player && !bossaction) { if (!(line->special & DoorMonster)) return; // monsters disallowed from this door @@ -1301,7 +1301,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } else if ((unsigned)line->special >= GenLockedBase) { - if (!thing->player) + if (!thing->player || bossaction) // boss actions can't handle locked doors return; // monsters disallowed from unlocking doors if (((line->special&TriggerType)==WalkOnce) || ((line->special&TriggerType)==WalkMany)) { //jff 4/1/98 check for being a walk type before reporting door type @@ -1314,7 +1314,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } else if ((unsigned)line->special >= GenLiftBase) { - if (!thing->player) + if (!thing->player && !bossaction) if (!(line->special & LiftMonster)) return; // monsters disallowed if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag @@ -1323,7 +1323,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } else if ((unsigned)line->special >= GenStairsBase) { - if (!thing->player) + if (!thing->player && !bossaction) if (!(line->special & StairMonster)) return; // monsters disallowed if (!comperr(comperr_zerotag) && !line->tag) //e6y //jff 2/27/98 all walk generalized types require tag @@ -1346,18 +1346,16 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) } } - if (!thing->player) + if (!thing->player || bossaction) { ok = 0; switch(line->special) { + // teleporters are blocked for boss actions. case 39: // teleport trigger case 97: // teleport retrigger case 125: // teleport monsteronly trigger case 126: // teleport monsteronly retrigger - case 4: // raise door - case 10: // plat down-wait-up-stay trigger - case 88: // plat down-wait-up-stay retrigger //jff 3/5/98 add ability of monsters etc. to use teleporters case 208: //silent thing teleporters case 207: @@ -1371,6 +1369,11 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) case 267: case 268: case 269: + if (bossaction) return; + + case 4: // raise door + case 10: // plat down-wait-up-stay trigger + case 88: // plat down-wait-up-stay retrigger ok = 1; break; } @@ -1531,7 +1534,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) case 52: // EXIT! // killough 10/98: prevent zombies from exiting levels - if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + if (bossaction || (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie]))) G_ExitLevel (); break; @@ -1617,7 +1620,7 @@ void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) // Secret EXIT // killough 10/98: prevent zombies from exiting levels // CPhipps - change for lxdoom's compatibility handling - if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + if (bossaction || (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie]))) G_SecretExitLevel (); break; diff --git a/prboom2/src/p_spec.h b/prboom2/src/p_spec.h index a36e1c369..ec597c1c5 100644 --- a/prboom2/src/p_spec.h +++ b/prboom2/src/p_spec.h @@ -1095,13 +1095,14 @@ void P_UpdateSpecials dboolean P_UseSpecialLine ( mobj_t* thing, line_t* line, - int side ); + int side, + dboolean noplayercheck); void P_ShootSpecialLine ( mobj_t* thing, line_t* line ); -void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing); +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing, dboolean noplayercheck); void P_PlayerInSpecialSector ( player_t* player ); diff --git a/prboom2/src/p_switch.c b/prboom2/src/p_switch.c index 10cb395eb..68ac5c0fd 100644 --- a/prboom2/src/p_switch.c +++ b/prboom2/src/p_switch.c @@ -262,7 +262,8 @@ dboolean P_UseSpecialLine ( mobj_t* thing, line_t* line, - int side ) + int side, + dboolean bossaction) { // e6y @@ -285,7 +286,7 @@ P_UseSpecialLine } else if ((unsigned)line->special >= GenFloorBase) { - if (!thing->player) + if (!thing->player && !bossaction) if ((line->special & FloorChange) || !(line->special & FloorModel)) return false; // FloorModel is "Allow Monsters" if FloorChange is 0 if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual @@ -294,7 +295,7 @@ P_UseSpecialLine } else if ((unsigned)line->special >= GenCeilingBase) { - if (!thing->player) + if (!thing->player && !bossaction) if ((line->special & CeilingChange) || !(line->special & CeilingModel)) return false; // CeilingModel is "Allow Monsters" if CeilingChange is 0 if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual @@ -303,7 +304,7 @@ P_UseSpecialLine } else if ((unsigned)line->special >= GenDoorBase) { - if (!thing->player) + if (!thing->player && !bossaction) { if (!(line->special & DoorMonster)) return false; // monsters disallowed from this door @@ -316,7 +317,7 @@ P_UseSpecialLine } else if ((unsigned)line->special >= GenLockedBase) { - if (!thing->player) + if (!thing->player || bossaction) return false; // monsters disallowed from unlocking doors if (!P_CanUnlockGenDoor(line,thing->player)) return false; @@ -327,7 +328,7 @@ P_UseSpecialLine } else if ((unsigned)line->special >= GenLiftBase) { - if (!thing->player) + if (!thing->player && !bossaction) if (!(line->special & LiftMonster)) return false; // monsters disallowed if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual @@ -336,7 +337,7 @@ P_UseSpecialLine } else if ((unsigned)line->special >= GenStairsBase) { - if (!thing->player) + if (!thing->player && !bossaction) if (!(line->special & StairMonster)) return false; // monsters disallowed if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual @@ -345,7 +346,7 @@ P_UseSpecialLine } else if ((unsigned)line->special >= GenCrusherBase) { - if (!thing->player) + if (!thing->player && !bossaction) if (!(line->special & CrusherMonster)) return false; // monsters disallowed if (!comperr(comperr_zerotag) && !line->tag && ((line->special&6)!=6)) //e6y //jff 2/27/98 all non-manual @@ -379,7 +380,7 @@ P_UseSpecialLine } // Switches that other things can activate. - if (!thing->player) + if (!thing->player && !bossaction) { // never open secret doors if (line->flags & ML_SECRET) @@ -404,6 +405,35 @@ P_UseSpecialLine } } + if (bossaction) + { + switch(line->special) + { + // 0-tag specials, locked switches and teleporters need to be blocked for boss actions. + case 1: // MANUAL DOOR RAISE + case 32: // MANUAL BLUE + case 33: // MANUAL RED + case 34: // MANUAL YELLOW + case 117: // Blazing door raise + case 118: // Blazing door open + case 133: // BlzOpenDoor BLUE + case 135: // BlzOpenDoor RED + case 137: // BlzOpenDoor YEL + + case 99: // BlzOpenDoor BLUE + case 134: // BlzOpenDoor RED + case 136: // BlzOpenDoor YELLOW + + //jff 3/5/98 add ability to use teleporters for monsters + case 195: // switch teleporters + case 174: + case 210: // silent switch teleporters + case 209: + return false; + break; + } + } + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types return false; @@ -443,7 +473,7 @@ P_UseSpecialLine /* Exit level * killough 10/98: prevent zombies from exiting levels */ - if (thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + if (!bossaction && thing->player && thing->player->health <= 0 && !comp[comp_zombie]) { S_StartSound(thing, sfx_noway); return false; @@ -523,7 +553,7 @@ P_UseSpecialLine /* Secret EXIT * killough 10/98: prevent zombies from exiting levels */ - if (thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + if (!bossaction && thing->player && thing->player->health <= 0 && !comp[comp_zombie]) { S_StartSound(thing, sfx_noway); return false; diff --git a/prboom2/src/s_sound.c b/prboom2/src/s_sound.c index 3774d1e76..04f3ba9a5 100644 --- a/prboom2/src/s_sound.c +++ b/prboom2/src/s_sound.c @@ -192,7 +192,7 @@ void S_Start(void) if (gamemapinfo && gamemapinfo->music[0]) { - int muslump = W_CheckNumForName(gamemapinfo->music, ns_global); + int muslump = W_CheckNumForName(gamemapinfo->music); if (muslump >= 0) { S_ChangeMusInfoMusic(muslump, true); diff --git a/prboom2/src/umapinfo.c b/prboom2/src/umapinfo.c index a6e0ab4bc..8a92f3d1c 100644 --- a/prboom2/src/umapinfo.c +++ b/prboom2/src/umapinfo.c @@ -52,6 +52,163 @@ struct ParseState }; +//========================================================================== +// +// The Doom actors in their original order +// Names are the same as in ZDoom. +// +//========================================================================== + +static const char * const ActorNames[] = +{ + "DoomPlayer", + "ZombieMan", + "ShotgunGuy", + "Archvile", + "ArchvileFire", + "Revenant", + "RevenantTracer", + "RevenantTracerSmoke", + "Fatso", + "FatShot", + "ChaingunGuy", + "DoomImp", + "Demon", + "Spectre", + "Cacodemon", + "BaronOfHell", + "BaronBall", + "HellKnight", + "LostSoul", + "SpiderMastermind", + "Arachnotron", + "Cyberdemon", + "PainElemental", + "WolfensteinSS", + "CommanderKeen", + "BossBrain", + "BossEye", + "BossTarget", + "SpawnShot", + "SpawnFire", + "ExplosiveBarrel", + "DoomImpBall", + "CacodemonBall", + "Rocket", + "PlasmaBall", + "BFGBall", + "ArachnotronPlasma", + "BulletPuff", + "Blood", + "TeleportFog", + "ItemFog", + "TeleportDest", + "BFGExtra", + "GreenArmor", + "BlueArmor", + "HealthBonus", + "ArmorBonus", + "BlueCard", + "RedCard", + "YellowCard", + "YellowSkull", + "RedSkull", + "BlueSkull", + "Stimpack", + "Medikit", + "Soulsphere", + "InvulnerabilitySphere", + "Berserk", + "BlurSphere", + "RadSuit", + "Allmap", + "Infrared", + "Megasphere", + "Clip", + "ClipBox", + "RocketAmmo", + "RocketBox", + "Cell", + "CellPack", + "Shell", + "ShellBox", + "Backpack", + "BFG9000", + "Chaingun", + "Chainsaw", + "RocketLauncher", + "PlasmaRifle", + "Shotgun", + "SuperShotgun", + "TechLamp", + "TechLamp2", + "Column", + "TallGreenColumn", + "ShortGreenColumn", + "TallRedColumn", + "ShortRedColumn", + "SkullColumn", + "HeartColumn", + "EvilEye", + "FloatingSkull", + "TorchTree", + "BlueTorch", + "GreenTorch", + "RedTorch", + "ShortBlueTorch", + "ShortGreenTorch", + "ShortRedTorch", + "Slalagtite", + "TechPillar", + "CandleStick", + "Candelabra", + "BloodyTwitch", + "Meat2", + "Meat3", + "Meat4", + "Meat5", + "NonsolidMeat2", + "NonsolidMeat4", + "NonsolidMeat3", + "NonsolidMeat5", + "NonsolidTwitch", + "DeadCacodemon", + "DeadMarine", + "DeadZombieMan", + "DeadDemon", + "DeadLostSoul", + "DeadDoomImp", + "DeadShotgunGuy", + "GibbedMarine", + "GibbedMarineExtra", + "HeadsOnAStick", + "Gibs", + "HeadOnAStick", + "HeadCandles", + "DeadStick", + "LiveStick", + "BigTree", + "BurningBarrel", + "HangNoGuts", + "HangBNoBrain", + "HangTLookingDown", + "HangTSkull", + "HangTLookingUp", + "HangTNoBrain", + "ColonGibs", + "SmallBloodPool", + "BrainStem", + //Boom/MBF additions + "PointPusher", + "PointPuller", + "MBFHelperDog", + "PlasmaBall1", + "PlasmaBall2", + "EvilSceptre", + "UnholyBible", + NULL +}; + // ----------------------------------------------- // @@ -87,6 +244,7 @@ static void FreeMap(struct MapEntry *mape) if (mape->intertext) free(mape->intertext); if (mape->intertextsecret) free(mape->intertextsecret); if (mape->properties) free(mape->properties); + if (mape->bossactions) free(mape->bossactions); mape->propertycount = 0; mape->mapname = NULL; mape->properties = NULL; @@ -402,6 +560,7 @@ static long ParseInt(struct ParseState *state, int allowbool) state->ErrorFunction("Syntax error in line %u: numeric constant followed by invalid characters", state->line); return 0; } + state->position = newpos; return value; } @@ -471,6 +630,36 @@ static int ParseAssign(struct ParseState *state) return 1; } +// ----------------------------------------------- +// +// Parses an assignment operator +// +// ----------------------------------------------- + +static int ParseComma(struct ParseState *state) +{ + if (SkipWhitespace(state, false)) + { + state->error = 1; + state->ErrorFunction("',' expected in line %u", state->line); + return 0; + } + if (*state->position != ',') + { + state->error = 1; + state->ErrorFunction("',' expected in line %u", state->line); + return 0; + } + state->position++; + if (SkipWhitespace(state, false)) + { + state->error = 1; + state->ErrorFunction("Unexpected end of file %u", state->line); + return 0; + } + return 1; +} + // ----------------------------------------------- // // Parses a map property of the form @@ -638,6 +827,50 @@ static int ParseStandardProperty(struct ParseState *state, struct MapEntry *mape if (!lname) return 0; M_AddEpisode(mape->mapname, lname); } + else if (!stricmp(pname, "bossaction")) + { + char * classname = ParseIdentifier(state, true); + int classnum, special, tag; + if (!stricmp(classname, "clear")) + { + // mark level free of boss actions + classnum = special = tag = -1; + if (mape->bossactions) free(mape->bossactions); + mape->bossactions = NULL; + mape->numbossactions = -1; + } + else + { + int i; + for (i = 0; ActorNames[i]; i++) + { + if (!stricmp(classname, ActorNames[i])) break; + } + if (ActorNames[i] == NULL) + { + state->error = 1; + state->ErrorFunction("Unknown thing type %s in line %d", classname, state->line); + return 0; + } + + ParseComma(state); + special = ParseInt(state, false); + ParseComma(state); + tag = ParseInt(state, false); + // allow no 0-tag specials here, unless a level exit. + if (tag != 0 || special == 11 || special == 51 || special == 52 || special == 124) + { + if (mape->numbossactions == -1) mape->numbossactions = 1; + else mape->numbossactions++; + mape->bossactions = (struct BossAction *)realloc(mape->bossactions, sizeof(struct BossAction) * mape->numbossactions); + mape->bossactions[mape->numbossactions - 1].type = i; + mape->bossactions[mape->numbossactions - 1].special = special; + mape->bossactions[mape->numbossactions - 1].tag = tag; + } + + } + free(classname); + } else { state->position = savedpos; diff --git a/prboom2/src/umapinfo.h b/prboom2/src/umapinfo.h index 4d8987cfc..71fd0c468 100644 --- a/prboom2/src/umapinfo.h +++ b/prboom2/src/umapinfo.h @@ -44,6 +44,13 @@ struct MapProperty struct MapPropertyValue *values; }; +struct BossAction +{ + int type; + int special; + int tag; +}; + struct MapEntry { char *mapname; @@ -62,9 +69,11 @@ struct MapEntry char intermusic[9]; int partime; int nointermission; + int numbossactions; unsigned int propertycount; struct MapProperty *properties; + struct BossAction *bossactions; }; struct MapList