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