diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 8e10a4fdd..ccccd9e1d 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,11 @@ +May 22, 2008 (Changes by Graf Zahl) +- Reworked a few options that previously depended on LEVEL_HEXENFORMAT + (actors being forced to the ground by instantly moving sectors, strife + railing handling and shooting lines with a non-zero but unassigned tag.) + With UDMF such semantics have to be handled diffently. +- finalized UDMF 1.0 implementation. +- Added Martin Howe's latest morph update. + May 21, 2008 - Fixed: When R_DrawTiltedPlane() calculates the p vector, it can overflow if the view is near the bounds of the fixed point coordinate system. This diff --git a/src/g_game.cpp b/src/g_game.cpp index 5db3eb0c3..a5d8179f4 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1123,7 +1123,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, bool resetinventory if (p->morphTics) { // Undo morph - P_UndoPlayerMorph (p, true); + P_UndoPlayerMorph (p, p, true); } // Clears the entire inventory and gives back the defaults for starting a game diff --git a/src/g_heretic/a_hereticartifacts.cpp b/src/g_heretic/a_hereticartifacts.cpp index ee2422fee..9afdcf8bb 100644 --- a/src/g_heretic/a_hereticartifacts.cpp +++ b/src/g_heretic/a_hereticartifacts.cpp @@ -34,7 +34,7 @@ bool AArtiTomeOfPower::Use (bool pickup) { if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYTOMEOFPOWER)) { // Attempt to undo chicken - if (!P_UndoPlayerMorph (Owner->player)) + if (!P_UndoPlayerMorph (Owner->player, Owner->player)) { // Failed if (!(Owner->player->MorphStyle & MORPH_FAILNOTELEFRAG)) { diff --git a/src/g_level.h b/src/g_level.h index 3340659aa..cce5ab220 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -118,6 +118,8 @@ #define LEVEL_FORCETEAMPLAYOFF UCONST64(0x8000000000000) #define LEVEL_CONV_SINGLE_UNFREEZE UCONST64(0x10000000000000) +#define LEVEL_RAILINGHACK UCONST64(0x20000000000000) // but UDMF requires them to be separate to have more control +#define LEVEL_DUMMYSWITCHES UCONST64(0x40000000000000) struct acsdefered_s; diff --git a/src/g_raven/a_artitele.cpp b/src/g_raven/a_artitele.cpp index 679f08e06..d75b1d58c 100644 --- a/src/g_raven/a_artitele.cpp +++ b/src/g_raven/a_artitele.cpp @@ -62,7 +62,7 @@ bool AArtiTeleport::Use (bool pickup) bool canlaugh = true; if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYCHAOSDEVICE)) { // Teleporting away will undo any morph effects (pig) - if (!P_UndoPlayerMorph (Owner->player) && (Owner->player->MorphStyle & MORPH_FAILNOLAUGH)) + if (!P_UndoPlayerMorph (Owner->player, Owner->player) && (Owner->player->MorphStyle & MORPH_FAILNOLAUGH)) { canlaugh = false; } diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 4643bad3b..0b41d39d8 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -1740,7 +1740,7 @@ void APowerMorph::Serialize (FArchive &arc) { Super::Serialize (arc); arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash; - arc << player; + arc << Player; } //=========================================================================== @@ -1761,7 +1761,7 @@ void APowerMorph::InitEffect( ) { Owner = realplayer->mo; // Replace the new owner in our owner; safe because we are not attached to anything yet ItemFlags |= IF_CREATECOPYMOVED; // Let the caller know the "real" owner has changed (to the morphed actor) - player = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field) + Player = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field) } else // morph failed - give the caller an opportunity to fail the pickup completely { @@ -1778,23 +1778,45 @@ void APowerMorph::InitEffect( ) void APowerMorph::EndEffect( ) { - if (Owner != NULL && player != NULL) + // Abort if owner already destroyed + if (Owner == NULL) { - int savedMorphTics = player->morphTics; - P_UndoPlayerMorph (player); - if (player->morphTics /*failed*/) - { - // Transfer retry timeout - // to the powerup's timer. - EffectTics = player->morphTics; - // Reload negative morph tics; - // use actual value; it may - // be in use for animation. - player->morphTics = savedMorphTics; - } - else // unmorph succeeded - { - player = NULL; - } + assert(Player == NULL); + return; } + + // Abort if owner already unmorphed + if (Player == NULL) + { + return; + } + + // Abort if owner is dead; their Die() method will + // take care of any required unmorphing on death. + if (Player->health <= 0) + { + return; + } + + // Unmorph if possible + int savedMorphTics = Player->morphTics; + P_UndoPlayerMorph (Player, Player); + + // Abort if unmorph failed; in that case, + // set the usual retry timer and return. + if (Player->morphTics) + { + // Transfer retry timeout + // to the powerup's timer. + EffectTics = Player->morphTics; + // Reload negative morph tics; + // use actual value; it may + // be in use for animation. + Player->morphTics = savedMorphTics; + // Try again some time later + return; + } + + // Unmorph suceeded + Player = NULL; } diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 21df07952..e4ade50f5 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -267,7 +267,7 @@ protected: void InitEffect (); void EndEffect (); // Variables - player_s *player; + player_s *Player; }; #endif //__A_ARTIFACTS_H__ diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 1e54e0cfd..80e31d6f3 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -63,6 +63,13 @@ bool P_MorphPlayer (player_t *activator, player_t *p, const PClass *spawntype, i morphed = static_cast(Spawn (spawntype, actor->x, actor->y, actor->z, NO_REPLACE)); DObject::StaticPointerSubstitution (actor, morphed); + if ((actor->tid != 0) && (style & MORPH_NEWTIDBEHAVIOUR)) + { + morphed->tid = actor->tid; + morphed->AddToHash (); + actor->RemoveFromHash (); + actor->tid = 0; + } morphed->angle = actor->angle; morphed->target = actor->target; morphed->tracer = actor; @@ -159,7 +166,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, const PClass *spawntype, i // //---------------------------------------------------------------------------- -bool P_UndoPlayerMorph (player_t *player, bool force) +bool P_UndoPlayerMorph (player_s *activator, player_t *player, bool force) { AWeapon *beastweap; APlayerPawn *mo; @@ -177,11 +184,19 @@ bool P_UndoPlayerMorph (player_t *player, bool force) { return false; } + + if ((pmo->flags2 & MF2_INVULNERABLE) && ((player != activator) || (!(player->MorphStyle & MORPH_WHENINVULNERABLE)))) + { // Immune when invulnerable unless this is something we initiated. + // If the WORLD is the initiator, the same player should be given + // as the activator; WORLD initiated actions should always succeed. + return false; + } + mo = barrier_cast(pmo->tracer); mo->SetOrigin (pmo->x, pmo->y, pmo->z); mo->flags |= MF_SOLID; pmo->flags &= ~MF_SOLID; - if (!force && P_TestMobjLocation (mo) == false) + if (!force && !P_TestMobjLocation (mo)) { // Didn't fit mo->flags &= ~MF_SOLID; pmo->flags |= MF_SOLID; @@ -192,6 +207,11 @@ bool P_UndoPlayerMorph (player_t *player, bool force) mo->ObtainInventory (pmo); DObject::StaticPointerSubstitution (pmo, mo); + if ((pmo->tid != 0) && (player->MorphStyle & MORPH_NEWTIDBEHAVIOUR)) + { + mo->tid = pmo->tid; + mo->AddToHash (); + } mo->angle = pmo->angle; mo->player = player; mo->reactiontime = 18; @@ -211,6 +231,7 @@ bool P_UndoPlayerMorph (player_t *player, bool force) const PClass *exit_flash = player->MorphExitFlash; bool correctweapon = !!(player->MorphStyle & MORPH_LOSEACTUALWEAPON); + bool undobydeathsaves = !!(player->MorphStyle & MORPH_UNDOBYDEATHSAVES); player->morphTics = 0; player->MorphedPlayerClass = 0; @@ -222,7 +243,16 @@ bool P_UndoPlayerMorph (player_t *player, bool force) { level2->Destroy (); } - player->health = mo->health = mo->GetDefault()->health; + + if ((player->health > 0) || undobydeathsaves) + { + player->health = mo->health = mo->GetDefault()->health; + } + else // killed when morphed so stay dead + { + mo->health = player->health; + } + player->mo = mo; if (player->camera == pmo) { @@ -363,18 +393,17 @@ bool P_MorphMonster (AActor *actor, const PClass *spawntype, int duration, int s //---------------------------------------------------------------------------- // -// FUNC P_UpdateMorphedMonster +// FUNC P_UndoMonsterMorph // // Returns true if the monster unmorphs. // //---------------------------------------------------------------------------- -bool P_UpdateMorphedMonster (AMorphedMonster *beast) +bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force) { AActor *actor; - if (beast->UnmorphTime == 0 || - beast->UnmorphTime > level.time || + if (beast->UnmorphTime == 0 || beast->UnmorphedMe == NULL || beast->flags3 & MF3_STAYMORPHED) { @@ -384,7 +413,7 @@ bool P_UpdateMorphedMonster (AMorphedMonster *beast) actor->SetOrigin (beast->x, beast->y, beast->z); actor->flags |= MF_SOLID; beast->flags &= ~MF_SOLID; - if (P_TestMobjLocation (actor) == false) + if (!force && !P_TestMobjLocation (actor)) { // Didn't fit actor->flags &= ~MF_SOLID; beast->flags |= MF_SOLID; @@ -417,6 +446,82 @@ bool P_UpdateMorphedMonster (AMorphedMonster *beast) return true; } +//---------------------------------------------------------------------------- +// +// FUNC P_UpdateMorphedMonster +// +// Returns true if the monster unmorphs. +// +//---------------------------------------------------------------------------- + +bool P_UpdateMorphedMonster (AMorphedMonster *beast) +{ + if (beast->UnmorphTime > level.time) + { + return false; + } + return P_UndoMonsterMorph (beast); +} + +//---------------------------------------------------------------------------- +// +// FUNC P_MorphedDeath +// +// Unmorphs the actor if possible. +// Returns the unmorphed actor, the style with which they were morphed and the +// health (of the AActor, not the player_s) they last had before unmorphing. +// +//---------------------------------------------------------------------------- + +bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth) +{ + // May be a morphed player + if ((actor->player) && + (actor->player->morphTics) && + (actor->player->MorphStyle & MORPH_UNDOBYDEATH) && + (actor->player->mo) && + (actor->player->mo->tracer)) + { + AActor *realme = actor->player->mo->tracer; + int realstyle = actor->player->MorphStyle; + int realhealth = actor->health; + if (P_UndoPlayerMorph(actor->player, actor->player, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED))) + { + *morphed = realme; + *morphedstyle = realstyle; + *morphedhealth = realhealth; + return true; + } + return false; + } + + // May be a morphed monster + if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) + { + AMorphedMonster *fakeme = static_cast(actor); + if ((fakeme->UnmorphTime) && + (fakeme->MorphStyle & MORPH_UNDOBYDEATH) && + (fakeme->UnmorphedMe)) + { + AActor *realme = fakeme->UnmorphedMe; + int realstyle = fakeme->MorphStyle; + int realhealth = fakeme->health; + if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) + { + *morphed = realme; + *morphedstyle = realstyle; + *morphedhealth = realhealth; + return true; + } + } + fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die() + return false; + } + + // Not a morphed player or monster + return false; +} + // Base class for morphing projectiles -------------------------------------- IMPLEMENT_STATELESS_ACTOR(AMorphProjectile, Any, -1, 0) @@ -479,7 +584,11 @@ void AMorphedMonster::Destroy () void AMorphedMonster::Die (AActor *source, AActor *inflictor) { // Dead things don't unmorph - flags3 |= MF3_STAYMORPHED; +// flags3 |= MF3_STAYMORPHED; + // [MH] + // But they can now, so that line above has been + // moved into P_MorphedDeath() and is now set by + // that function if and only if it is needed. Super::Die (source, inflictor); if (UnmorphedMe != NULL && (UnmorphedMe->flags & MF_UNMORPHED)) { diff --git a/src/g_shared/a_morph.h b/src/g_shared/a_morph.h index 18cb19848..c8afbed5d 100644 --- a/src/g_shared/a_morph.h +++ b/src/g_shared/a_morph.h @@ -2,7 +2,7 @@ #define __A_MORPH__ #define MORPHTICS (40*TICRATE) -#define MAXMORPHHEALTH 30 +#define MAXMORPHHEALTH 30 // Morph style states how morphing affects health and // other effects in the game; only valid for players. @@ -11,25 +11,32 @@ enum { MORPH_OLDEFFECTS = 0x00000000, // Default to old Heretic/HeXen behaviour unless flags given - MORPH_ADDSTAMINA = 0x00000001, // Power instead of curse (add stamina instead of limiting to health) - MORPH_FULLHEALTH = 0x00000002, // New health semantics (!POWER => MaxHealth of animal, POWER => Normal health behaviour) + MORPH_ADDSTAMINA = 0x00000001, // Player has a "power" instead of a "curse" (add stamina instead of limiting to health) + MORPH_FULLHEALTH = 0x00000002, // Player uses new health semantics (!POWER => MaxHealth of animal, POWER => Normal health behaviour) MORPH_UNDOBYTOMEOFPOWER = 0x00000004, // Player unmorphs upon activating a Tome of Power MORPH_UNDOBYCHAOSDEVICE = 0x00000008, // Player unmorphs upon activating a Chaos Device MORPH_FAILNOTELEFRAG = 0x00000010, // Player stays morphed if unmorph by Tome of Power fails MORPH_FAILNOLAUGH = 0x00000020, // Player doesn't laugh if unmorph by Chaos Device fails - MORPH_WHENINVULNERABLE = 0x00000040, // Player can morph when invulnerable but ONLY if doing it to themselves - MORPH_LOSEACTUALWEAPON = 0X00000080 // Player loses specified morph weapon only (not "whichever they have when unmorphing") + MORPH_WHENINVULNERABLE = 0x00000040, // Player can morph (or scripted unmorph) when invulnerable but ONLY if doing it to themselves + MORPH_LOSEACTUALWEAPON = 0x00000080, // Player loses specified morph weapon only (not "whichever they have when unmorphing") + MORPH_NEWTIDBEHAVIOUR = 0x00000100, // Actor TID is by default transferred from the old actor to the new actor + MORPH_UNDOBYDEATH = 0x00000200, // Actor unmorphs when killed and (unless MORPH_UNDOBYDEATHSAVES) stays dead + MORPH_UNDOBYDEATHFORCED = 0x00000400, // Actor (if unmorphed when killed) forces unmorph (not very useful with UNDOBYDEATHSAVES) + MORPH_UNDOBYDEATHSAVES = 0x00000800, // Actor (if unmorphed when killed) regains their health and doesn't die }; struct PClass; class AActor; class player_s; +class AMorphedMonster; bool P_MorphPlayer (player_s *activator, player_s *player, const PClass *morphclass, int duration = 0, int style = 0, const PClass *enter_flash = NULL, const PClass *exit_flash = NULL); -bool P_UndoPlayerMorph (player_s *player, bool force = false); +bool P_UndoPlayerMorph (player_s *activator, player_s *player, bool force = false); bool P_MorphMonster (AActor *actor, const PClass *morphclass, int duration = 0, int style = 0, const PClass *enter_flash = NULL, const PClass *exit_flash = NULL); +bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force = false); bool P_UpdateMorphedMonster (AActor *actor); +bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth); #endif //__A_MORPH__ diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index e3e01b9d9..a7e4f37aa 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -291,7 +291,7 @@ void cht_DoCheat (player_t *player, int cheat) if (player->morphTics > 0) { - P_UndoPlayerMorph(player); + P_UndoPlayerMorph(player, player); } } @@ -438,7 +438,7 @@ const char *cht_Morph (player_t *player, const PClass *morphclass, bool quickund if (player->morphTics) { - if (P_UndoPlayerMorph (player)) + if (P_UndoPlayerMorph (player, player)) { if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style)) { diff --git a/src/namedef.h b/src/namedef.h index 0383a6764..c307c3744 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -249,7 +249,6 @@ xx(Sector) xx(Heightfloor) xx(Heightceiling) xx(Lightlevel) -xx(Tag) xx(Texturefloor) xx(Textureceiling) @@ -305,7 +304,7 @@ xx(Twosided) xx(Dontpegtop) xx(Dontpegbottom) xx(Secret) -xx(Soundblock) +xx(Blocksound) xx(Dontdraw) xx(Mapped) xx(Monsteractivate) @@ -321,6 +320,7 @@ xx(Checkswitchrange) xx(Firstsideonly) xx(Transparent) xx(Passuse) +xx(Repeatspecial) xx(Playercross) xx(Playeruse) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 6ef9873a3..804c98053 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5351,8 +5351,129 @@ int DLevelScript::RunScript () } } break; - } - } + + case PCD_MORPHACTOR: + { + int tag = STACK(7); + FName playerclass_name = FBehavior::StaticLookupString(STACK(6)); + const PClass *playerclass = PClass::FindClass (playerclass_name); + FName monsterclass_name = FBehavior::StaticLookupString(STACK(5)); + const PClass *monsterclass = PClass::FindClass (monsterclass_name); + int duration = STACK(4); + int style = STACK(3); + FName morphflash_name = FBehavior::StaticLookupString(STACK(2)); + const PClass *morphflash = PClass::FindClass (morphflash_name); + FName unmorphflash_name = FBehavior::StaticLookupString(STACK(1)); + const PClass *unmorphflash = PClass::FindClass (unmorphflash_name); + int changes = 0; + + if (tag == 0) + { + if (activator->player) + { + if (P_MorphPlayer(activator->player, activator->player, playerclass, duration, style, morphflash, unmorphflash)) + { + changes++; + } + } + else + { + if (P_MorphMonster(activator, monsterclass, duration, style, morphflash, unmorphflash)) + { + changes++; + } + } + } + else + { + FActorIterator iterator (tag); + AActor *actor; + + while ( (actor = iterator.Next ()) ) + { + if (actor->player) + { + if (P_MorphPlayer(activator->player, actor->player, playerclass, duration, style, morphflash, unmorphflash)) + { + changes++; + } + } + else + { + if (P_MorphMonster(actor, monsterclass, duration, style, morphflash, unmorphflash)) + { + changes++; + } + } + } + } + + STACK(7) = changes; + sp -= 6; + } + break; + + case PCD_UNMORPHACTOR: + { + int tag = STACK(2); + bool force = !!STACK(1); + int changes = 0; + + if (tag == 0) + { + if (activator->player) + { + if (P_UndoPlayerMorph(activator->player, activator->player, force)) + { + changes++; + } + } + else + { + if (activator->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) + { + AMorphedMonster *morphed_actor = barrier_cast(activator); + if (P_UndoMonsterMorph(morphed_actor, force)) + { + changes++; + } + } + } + } + else + { + FActorIterator iterator (tag); + AActor *actor; + + while ( (actor = iterator.Next ()) ) + { + if (actor->player) + { + if (P_UndoPlayerMorph(activator->player, actor->player, force)) + { + changes++; + } + } + else + { + if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) + { + AMorphedMonster *morphed_actor = static_cast(actor); + if (P_UndoMonsterMorph(morphed_actor, force)) + { + changes++; + } + } + } + } + } + + STACK(2) = changes; + sp -= 1; + } + break; + } + } if (state == SCRIPT_DivideBy0) { diff --git a/src/p_acs.h b/src/p_acs.h index 072d2ffbd..0485a5d67 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -551,6 +551,8 @@ public: PCD_THINGCOUNTSECTOR, PCD_THINGCOUNTNAMESECTOR, PCD_CHECKPLAYERCAMERA, // [TN] + PCD_MORPHACTOR, // [MH] + PCD_UNMORPHACTOR, // [MH] PCODE_COMMAND_COUNT }; diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 7a4e22d72..a50f57bb2 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -236,6 +236,7 @@ static void LoadScriptFile (const char *name) lump = Wads.ReopenLumpNum (lumpnum); LoadScriptFile(lump, Wads.LumpLength(lumpnum)); + delete lump; } static void LoadScriptFile(FileReader *lump, int numnodes) @@ -278,7 +279,6 @@ static void LoadScriptFile(FileReader *lump, int numnodes) } StrifeDialogues.Push (node); } - delete lump; } //============================================================================ diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 9da7f3dc2..5c55efd65 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -317,8 +317,40 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker) // EXTERN_CVAR (Int, fraglimit) +static int GibHealth(AActor *actor) +{ + return -abs( + actor->GetClass()->Meta.GetMetaInt ( + AMETA_GibHealth, + gameinfo.gametype == GAME_Doom ? + -actor->GetDefault()->health : + -actor->GetDefault()->health/2)); +} + void AActor::Die (AActor *source, AActor *inflictor) { + // Handle possible unmorph on death + bool wasgibbed = (health < GibHealth(this)); + AActor *realthis = NULL; + int realstyle = 0; + int realhealth = 0; + if (P_MorphedDeath(this, &realthis, &realstyle, &realhealth)) + { + if (!(realstyle & MORPH_UNDOBYDEATHSAVES)) + { + if (wasgibbed) + { + int realgibhealth = GibHealth(realthis); + if (realthis->health >= realgibhealth) + { + realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed) + } + } + realthis->Die(source, inflictor); + } + return; + } + // [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :) effects &= ~FX_RESPAWNINVUL; //flags &= ~MF_INVINCIBLE; @@ -634,8 +666,7 @@ void AActor::Die (AActor *source, AActor *inflictor) { int flags4 = inflictor == NULL ? 0 : inflictor->flags4; - int gibhealth = -abs(GetClass()->Meta.GetMetaInt (AMETA_GibHealth, - gameinfo.gametype == GAME_Doom ? -GetDefault()->health : -GetDefault()->health/2)); + int gibhealth = GibHealth(this); // Don't pass on a damage type this actor cannot handle. // (most importantly, prevent barrels from passing on ice damage.) diff --git a/src/p_local.h b/src/p_local.h index 470a0895d..cd30a60ed 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -564,9 +564,6 @@ enum PO_SPAWNHURT_TYPE }; -#define PO_LINE_START 1 // polyobj line start special -#define PO_LINE_EXPLICIT 5 - extern polyobj_t *polyobjs; // list of all poly-objects on the level extern int po_NumPolyobjs; extern polyspawns_t *polyspawns; // [RH] list of polyobject things to spawn diff --git a/src/p_map.cpp b/src/p_map.cpp index c4ac9d5f3..3e0ced12e 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -608,8 +608,7 @@ bool PIT_CheckLine (line_t *ld, const FBoundingBox &box, FCheckPosition &tm) // better than Strife's handling of rails, which lets you jump into rails // from either side. How long until somebody reports this as a bug and I'm // forced to say, "It's not a bug. It's a feature?" Ugh. - (gameinfo.gametype != GAME_Strife || - level.flags & LEVEL_HEXENFORMAT || + (!(level.flags & LEVEL_RAILINGHACK) || open.bottom == tm.thing->Sector->floorplane.ZatPoint (sx, sy))) { open.bottom += 32*FRACUNIT; @@ -3599,6 +3598,7 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b struct FChangePosition { + sector_t *sector; int moveamt; int crushchange; bool nofit; @@ -3953,7 +3953,7 @@ void PIT_FloorDrop (AActor *thing, FChangePosition *cpos) P_CheckFakeFloorTriggers (thing, oldz); } else if ((thing->flags & MF_NOGRAVITY) || - ((!(level.flags & LEVEL_HEXENFORMAT) || cpos->moveamt < 9*FRACUNIT) + (((cpos->sector->Flags & SECF_FLOORDROP) || cpos->moveamt < 9*FRACUNIT) && thing->z - thing->floorz <= cpos->moveamt)) { thing->z = thing->floorz; @@ -4104,6 +4104,7 @@ bool P_ChangeSector (sector_t *sector, int crunch, int amt, int floorOrCeil, boo cpos.crushchange = crunch; cpos.moveamt = abs (amt); cpos.movemidtex = false; + cpos.sector = sector; // [RH] Use different functions for the four different types of sector // movement. Also update the soundorg's z-coordinate for 3D sound. diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 254495176..fba52444e 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -370,6 +370,7 @@ MapData *P_OpenMapData(const char * mapname) else if (!stricmp(lumpname, "BEHAVIOR")) { index = ML_BEHAVIOR; + map->HasBehavior = true; } else if (!stricmp(lumpname, "ENDMAP")) { @@ -411,14 +412,16 @@ MapData *P_OpenMapData(const char * mapname) (*map->file) >> numentries >> dirofs; map->file->Seek(dirofs, SEEK_SET); - for(DWORD i = 0; i < numentries; i++) + (*map->file) >> map->MapLumps[0].FilePos >> map->MapLumps[0].Size; + map->file->Read(map->MapLumps[0].Name, 8); + + for(DWORD i = 1; i < numentries; i++) { DWORD offset, size; char lumpname[8]; (*map->file) >> offset >> size; map->file->Read(lumpname, 8); - if (i == 1 && !strnicmp(lumpname, "TEXTMAP", 8)) { map->isText = true; @@ -432,28 +435,29 @@ MapData *P_OpenMapData(const char * mapname) { I_Error("Invalid map definition for %s", mapname); } - else if (!stricmp(lumpname, "ZNODES")) + else if (!strnicmp(lumpname, "ZNODES",8)) { index = ML_GLZNODES; } - else if (!stricmp(lumpname, "BLOCKMAP")) + else if (!strnicmp(lumpname, "BLOCKMAP",8)) { // there is no real point in creating a blockmap but let's use it anyway index = ML_BLOCKMAP; } - else if (!stricmp(lumpname, "REJECT")) + else if (!strnicmp(lumpname, "REJECT",8)) { index = ML_REJECT; } - else if (!stricmp(lumpname, "DIALOGUE")) + else if (!strnicmp(lumpname, "DIALOGUE",8)) { index = ML_CONVERSATION; } - else if (!stricmp(lumpname, "BEHAVIOR")) + else if (!strnicmp(lumpname, "BEHAVIOR",8)) { index = ML_BEHAVIOR; + map->HasBehavior = true; } - else if (!stricmp(lumpname, "ENDMAP")) + else if (!strnicmp(lumpname, "ENDMAP",8)) { return map; } @@ -1179,6 +1183,7 @@ void P_LoadSectors (MapData * map) for (i = 0; i < numsectors; i++, ss++, ms++) { ss->e = §ors[0].e[i]; + if (!map->HasBehavior) ss->Flags |= SECF_FLOORDROP; ss->floortexz = LittleShort(ms->floorheight)<floorplane.d = -ss->floortexz; ss->floorplane.c = FRACUNIT; @@ -3266,6 +3271,16 @@ void P_SetupLevel (char *lumpname, int position) } } + if (!map->HasBehavior && !map->isText) + { + // set compatibility flags + if (gameinfo.gametype == GAME_Strife) + { + level.flags |= LEVEL_RAILINGHACK; + } + level.flags |= LEVEL_DUMMYSWITCHES; + } + FBehavior::StaticLoadDefaultModules (); P_LoadStrifeConversations (map, lumpname); diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 3980948c6..2f414c4f3 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -229,11 +229,12 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType) } // some old WADs use this method to create walls that change the texture when shot. else if (activationType == SPAC_Impact && // only for shootable triggers - !(level.flags & LEVEL_HEXENFORMAT) && // only in Doom-format maps + (level.flags & LEVEL_DUMMYSWITCHES) && // this is only a compatibility setting for an old hack! !repeat && // only non-repeatable triggers (specialGeneric_Crusher) && // not for Boom's generalized linedefs special && // not for lines without a special - line->id && // only if there's a tag (which is stored in the id field) + line->args[0] == line->id && // Safety check: exclude edited UDMF linedefs or ones that don't map the tag to args[0] + line->args[0] && // only if there's a tag (which is stored in the first arg) P_FindSectorFromTag (line->args[0], -1) == -1) // only if no sector is tagged to this linedef { P_ChangeSwitchTexture (&sides[line->sidenum[0]], repeat, special); diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 2273d1e71..fe61061e4 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -40,6 +40,67 @@ #include "p_lnspec.h" #include "templates.h" #include "i_system.h" +#include "gi.h" + +// These tables define whichline an +static char HexenLineSpecialOk[]={ + 1,1,1,1,1,1,1,1,1,0, // 0-9 + 1,1,1,1,0,0,0,0,0,0, // 10-19 + 1,1,1,1,1,1,1,1,1,1, // 20-29 + 1,1,1,0,0,1,1,0,0,0, // 30-39 + 1,1,1,1,1,1,1,0,0,0, // 40-49 + 0,0,0,0,0,0,0,0,0,0, // 50-59 + 1,1,1,1,1,1,1,1,1,1, // 60-69 + 1,1,1,1,1,1,0,0,0,0, // 70-79 + 1,1,1,1,0,0,0,0,0,0, // 80-89 + 1,1,1,1,1,1,1,0,0,0, // 90-99 + 1,1,1,1,0,0,0,0,0,1, // 100-109 + 1,1,1,1,1,1,1,0,0,0, // 110-119 + 1,0,0,0,0,0,0,0,0,1, // 120-129 + 1,1,1,1,1,1,1,1,1,0, // 130-139 + 1 + // 140 is the highest valid special in Hexen. + +}; + +static char HexenSectorSpecialOk[256]={ + 1,1,1,1,1,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1,0,0, + 0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1, + 1,1,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1, +}; + + +enum +{ + Dm=1, + Ht=2, + Hx=4, + St=8, + Zd=16, + Zdt=32, + + // will be extended later. Unknown namespaces will always be treated like the base + // namespace for each game +}; + void P_ProcessSideTextures(bool checktranmap, side_t *sd, sector_t *sec, mapsidedef_t *msd, int special, int tag, short *alpha); void P_AdjustLine (line_t *ld); @@ -49,12 +110,17 @@ extern bool ForceNodeBuild; extern TArray MapThingsConverted; extern TArray linemap; + +#define CHECK_N(f) if (!(namespace_bits&(f))) break; + struct UDMFParser { FScanner sc; FName namespc; + int namespace_bits; bool isTranslated; bool isExtended; + bool floordrop; TArray ParsedLines; TArray ParsedSides; @@ -70,60 +136,126 @@ struct UDMFParser fogMap = normMap = NULL; } - void Flag(DWORD &value, int mask, const FString &svalue) + FName ParseKey() { - if (!svalue.CompareNoCase("true")) + sc.MustGetString(); + FName key = sc.String; + sc.MustGetToken('='); + + sc.Number = 0; + sc.Float = 0; + sc.MustGetAnyToken(); + + if (sc.TokenType == '+' || sc.TokenType == '-') { - value |= mask; + bool neg = (sc.TokenType == '-'); + sc.MustGetAnyToken(); + if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst) + { + sc.ScriptMessage("Numeric constant expected"); + } + if (neg) + { + sc.Number = -sc.Number; + sc.Float = -sc.Float; + } } - else + return key; + } + + int CheckInt(const char *key) + { + if (sc.TokenType != TK_IntConst) { - value &= ~mask; + sc.ScriptMessage("Integer value expected for key '%s'", key); } + return sc.Number; + } + + double CheckFloat(const char *key) + { + if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst) + { + sc.ScriptMessage("Floatint point value expected for key '%s'", key); + } + return sc.Float; + } + + fixed_t CheckFixed(const char *key) + { + return FLOAT2FIXED(CheckFloat(key)); + } + + bool CheckBool(const char *key) + { + if (sc.TokenType == TK_True) return true; + if (sc.TokenType == TK_False) return false; + sc.ScriptMessage("Boolean value expected for key '%s'", key); + return false; + } + + const char *CheckString(const char *key) + { + if (sc.TokenType != TK_StringConst) + { + sc.ScriptMessage("String value expected for key '%s'", key); + } + return sc.String; + } + + void Flag(DWORD &value, int mask, const char *key) + { + if (CheckBool(key)) value |= mask; + else value &= ~mask; } void ParseThing(FMapThing *th) { memset(th, 0, sizeof(*th)); - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) { - sc.MustGetString(); - FName key = sc.String; - sc.MustGetStringName("="); - sc.MustGetString(); - FString value = sc.String; - sc.MustGetStringName(";"); + FName key = ParseKey(); switch(key) { - case NAME_TID: - th->thingid = (WORD)strtol(value, NULL, 0); + case NAME_Id: + th->thingid = CheckInt(key); break; + case NAME_X: - th->x = FLOAT2FIXED(strtod(value, NULL)); + th->x = CheckFixed(key); break; + case NAME_Y: - th->y = FLOAT2FIXED(strtod(value, NULL)); + th->y = CheckFixed(key); break; + case NAME_Height: - th->z = FLOAT2FIXED(strtod(value, NULL)); + th->z = CheckFixed(key); break; + case NAME_Angle: - th->angle = (short)strtol(value, NULL, 0); + th->angle = (short)CheckInt(key); break; + case NAME_Type: - th->type = (short)strtol(value, NULL, 0); + th->type = (short)CheckInt(key); break; + case NAME_Special: - th->special = (short)strtol(value, NULL, 0); + CHECK_N(Hx | Zd | Zdt) + th->special = CheckInt(key); break; + case NAME_Arg0: case NAME_Arg1: case NAME_Arg2: case NAME_Arg3: case NAME_Arg4: - th->args[int(key)-int(NAME_Arg0)] = strtol(value, NULL, 0); + CHECK_N(Hx | Zd | Zdt) + th->args[int(key)-int(NAME_Arg0)] = CheckInt(key); break; + case NAME_Skill1: case NAME_Skill2: case NAME_Skill3: @@ -140,11 +272,10 @@ struct UDMFParser case NAME_Skill14: case NAME_Skill15: case NAME_Skill16: - if (!value.CompareNoCase("true")) th->SkillFilter |= (1<<(int(key)-NAME_Skill1)); + if (CheckBool(key)) th->SkillFilter |= (1<<(int(key)-NAME_Skill1)); else th->SkillFilter &= ~(1<<(int(key)-NAME_Skill1)); break; - case NAME_Class0: case NAME_Class1: case NAME_Class2: case NAME_Class3: @@ -161,43 +292,72 @@ struct UDMFParser case NAME_Class14: case NAME_Class15: case NAME_Class16: - if (!value.CompareNoCase("true")) th->ClassFilter |= (1<<(int(key)-NAME_Class1)); + CHECK_N(Hx | Zd | Zdt) + if (CheckBool(key)) th->ClassFilter |= (1<<(int(key)-NAME_Class1)); else th->SkillFilter &= ~(1<<(int(key)-NAME_Class1)); break; case NAME_Ambush: - Flag(th->flags, MTF_AMBUSH, value); break; + Flag(th->flags, MTF_AMBUSH, key); + break; + case NAME_Dormant: - Flag(th->flags, MTF_DORMANT, value); break; + CHECK_N(Hx | Zd | Zdt) + Flag(th->flags, MTF_DORMANT, key); + break; + case NAME_Single: - Flag(th->flags, MTF_SINGLE, value); break; + Flag(th->flags, MTF_SINGLE, key); + break; + case NAME_Coop: - Flag(th->flags, MTF_COOPERATIVE, value); break; + Flag(th->flags, MTF_COOPERATIVE, key); + break; + case NAME_Dm: - Flag(th->flags, MTF_DEATHMATCH, value); break; + Flag(th->flags, MTF_DEATHMATCH, key); + break; + case NAME_Translucent: - Flag(th->flags, MTF_SHADOW, value); break; + CHECK_N(St | Zd | Zdt) + Flag(th->flags, MTF_SHADOW, key); + break; + case NAME_Invisible: - Flag(th->flags, MTF_ALTSHADOW, value); break; - case NAME_Friend: + CHECK_N(St | Zd | Zdt) + Flag(th->flags, MTF_ALTSHADOW, key); + break; + + case NAME_Friend: // This maps to Strife's friendly flag + CHECK_N(Dm | Zd | Zdt) + Flag(th->flags, MTF_FRIENDLY, key); + break; + case NAME_Strifeally: - Flag(th->flags, MTF_FRIENDLY, value); break; + CHECK_N(St | Zd | Zdt) + Flag(th->flags, MTF_FRIENDLY, key); + break; + case NAME_Standing: - Flag(th->flags, MTF_STANDSTILL, value); break; + CHECK_N(St | Zd | Zdt) + Flag(th->flags, MTF_STANDSTILL, key); + break; default: break; } + sc.MustGetToken(';'); } - if (isTranslated) + // Thing specials are only valid in namespaces with Hexen-type specials + // and in ZDoomTranslated - which will use the translator on them. + if (namespc == NAME_ZDoomTranslated) { - if (isExtended) - { - // NOTE: Handling of this is undefined in the UDMF spec - // so it is only done for namespace ZDoomTranslated - maplinedef_t mld; - line_t ld; + maplinedef_t mld; + line_t ld; + if (th->special != 0) // if special is 0, keep the args (e.g. for bridge things) + { + // The trigger type is ignored here. mld.flags = 0; mld.special = th->special; mld.tag = th->args[0]; @@ -205,17 +365,18 @@ struct UDMFParser th->special = ld.special; memcpy(th->args, ld.args, sizeof (ld.args)); } - else // NULL the special - { - th->special = 0; - memset(th->args, 0, sizeof (th->args)); - } + } + else if (isTranslated) + { + th->special = 0; + memset(th->args, 0, sizeof (th->args)); } } void ParseLinedef(line_t *ld) { bool passuse = false; + bool strifetrans = false; memset(ld, 0, sizeof(*ld)); ld->Alpha = FRACUNIT; @@ -224,72 +385,109 @@ struct UDMFParser if (level.flags & LEVEL_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; if (level.flags & LEVEL_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; if (level.flags & LEVEL_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) { - sc.MustGetString(); - FName key = sc.String; - sc.MustGetStringName("="); - sc.MustGetString(); - FString value = sc.String; - sc.MustGetStringName(";"); + FName key = ParseKey(); // This switch contains all keys of the UDMF base spec switch(key) { case NAME_V1: - ld->v1 = (vertex_t*)(intptr_t)strtol(value, NULL, 0); // must be relocated later + ld->v1 = (vertex_t*)(intptr_t)CheckInt(key); // must be relocated later break; + case NAME_V2: - ld->v2 = (vertex_t*)(intptr_t)strtol(value, NULL, 0); // must be relocated later + ld->v2 = (vertex_t*)(intptr_t)CheckInt(key); // must be relocated later break; + case NAME_Special: - ld->special = strtol(value, NULL, 0); + ld->special = CheckInt(key); + if (namespc == NAME_Hexen) + { + if (ld->special < 0 || ld->special > 140 || !HexenLineSpecialOk[ld->special]) + ld->special = 0; // NULL all specials which don't exist in Hexen + } + break; + case NAME_Id: - ld->id = strtol(value, NULL, 0); + ld->id = CheckInt(key); break; + case NAME_Sidefront: - ld->sidenum[0] = strtol(value, NULL, 0); + ld->sidenum[0] = CheckInt(key); break; + case NAME_Sideback: - ld->sidenum[1] = strtol(value, NULL, 0); + ld->sidenum[1] = CheckInt(key); break; + case NAME_Arg0: case NAME_Arg1: case NAME_Arg2: case NAME_Arg3: case NAME_Arg4: - ld->args[int(key)-int(NAME_Arg0)] = strtol(value, NULL, 0); + ld->args[int(key)-int(NAME_Arg0)] = CheckInt(key); break; + case NAME_Blocking: - Flag(ld->flags, ML_BLOCKING, value); break; + Flag(ld->flags, ML_BLOCKING, key); + break; + case NAME_Blockmonsters: - Flag(ld->flags, ML_BLOCKMONSTERS, value); break; + Flag(ld->flags, ML_BLOCKMONSTERS, key); + break; + case NAME_Twosided: - Flag(ld->flags, ML_TWOSIDED, value); break; + Flag(ld->flags, ML_TWOSIDED, key); + break; + case NAME_Dontpegtop: - Flag(ld->flags, ML_DONTPEGTOP, value); break; + Flag(ld->flags, ML_DONTPEGTOP, key); + break; + case NAME_Dontpegbottom: - Flag(ld->flags, ML_DONTPEGBOTTOM, value); break; + Flag(ld->flags, ML_DONTPEGBOTTOM, key); + break; + case NAME_Secret: - Flag(ld->flags, ML_SECRET, value); break; - case NAME_Soundblock: - Flag(ld->flags, ML_SOUNDBLOCK, value); break; + Flag(ld->flags, ML_SECRET, key); + break; + + case NAME_Blocksound: + Flag(ld->flags, ML_SOUNDBLOCK, key); + break; + case NAME_Dontdraw: - Flag(ld->flags, ML_DONTDRAW, value); break; + Flag(ld->flags, ML_DONTDRAW, key); + break; + case NAME_Mapped: - Flag(ld->flags, ML_MAPPED, value); break; - case NAME_Monsteractivate: - Flag(ld->flags, ML_MONSTERSCANACTIVATE, value); break; + Flag(ld->flags, ML_MAPPED, key); + break; + case NAME_Jumpover: - Flag(ld->flags, ML_RAILING, value); break; + CHECK_N(St | Zd | Zdt) + Flag(ld->flags, ML_RAILING, key); + break; + case NAME_Blockfloating: - Flag(ld->flags, ML_BLOCK_FLOATERS, value); break; + CHECK_N(St | Zd | Zdt) + Flag(ld->flags, ML_BLOCK_FLOATERS, key); + break; + case NAME_Transparent: - ld->Alpha = !value.CompareNoCase("true")? FRACUNIT*3/4 : FRACUNIT; break; + CHECK_N(St | Zd | Zdt) + strifetrans = CheckBool(key); + break; + case NAME_Passuse: - passuse = !value.CompareNoCase("true"); break; + CHECK_N(Dm | Zd | Zdt) + passuse = CheckBool(key); + break; + default: break; } @@ -298,48 +496,94 @@ struct UDMFParser if (!isTranslated) switch (key) { case NAME_Playercross: - Flag(ld->activation, SPAC_Cross, value); break; + Flag(ld->activation, SPAC_Cross, key); + break; + case NAME_Playeruse: - Flag(ld->activation, SPAC_Use, value); break; + Flag(ld->activation, SPAC_Use, key); + break; + case NAME_Monstercross: - Flag(ld->activation, SPAC_MCross, value); break; + Flag(ld->activation, SPAC_MCross, key); + break; + case NAME_Impact: - Flag(ld->activation, SPAC_Impact, value); break; + Flag(ld->activation, SPAC_Impact, key); + break; + case NAME_Playerpush: - Flag(ld->activation, SPAC_Push, value); break; + Flag(ld->activation, SPAC_Push, key); + break; + case NAME_Missilecross: - Flag(ld->activation, SPAC_PCross, value); break; + Flag(ld->activation, SPAC_PCross, key); + break; + case NAME_Monsteruse: - Flag(ld->activation, SPAC_MUse, value); break; + Flag(ld->activation, SPAC_MUse, key); + break; + case NAME_Monsterpush: - Flag(ld->activation, SPAC_MPush, value); break; + Flag(ld->activation, SPAC_MPush, key); + break; + + case NAME_Repeatspecial: + Flag(ld->flags, ML_REPEAT_SPECIAL, key); + break; + default: break; } // This switch contains all keys which are ZDoom specific - if (isExtended) switch(key) + if (namespace_bits & (Zd|Zdt)) switch(key) { + case NAME_Anycross: + Flag(ld->activation, SPAC_AnyCross, key); + break; + + case NAME_Monsteractivate: + Flag(ld->flags, ML_MONSTERSCANACTIVATE, key); + break; + case NAME_Blockplayers: - Flag(ld->flags, ML_BLOCK_PLAYERS, value); break; + Flag(ld->flags, ML_BLOCK_PLAYERS, key); + break; + case NAME_Blockeverything: - Flag(ld->flags, ML_BLOCKEVERYTHING, value); break; + Flag(ld->flags, ML_BLOCKEVERYTHING, key); + break; + case NAME_Zoneboundary: - Flag(ld->flags, ML_ZONEBOUNDARY, value); break; + Flag(ld->flags, ML_ZONEBOUNDARY, key); + break; + case NAME_Clipmidtex: - Flag(ld->flags, ML_CLIP_MIDTEX, value); break; + Flag(ld->flags, ML_CLIP_MIDTEX, key); + break; + case NAME_Wrapmidtex: - Flag(ld->flags, ML_WRAP_MIDTEX, value); break; + Flag(ld->flags, ML_WRAP_MIDTEX, key); + break; + case NAME_Midtex3d: - Flag(ld->flags, ML_3DMIDTEX, value); break; + Flag(ld->flags, ML_3DMIDTEX, key); + break; + case NAME_Checkswitchrange: - Flag(ld->flags, ML_CHECKSWITCHRANGE, value); break; + Flag(ld->flags, ML_CHECKSWITCHRANGE, key); + break; + case NAME_Firstsideonly: - Flag(ld->flags, ML_FIRSTSIDEONLY, value); break; + Flag(ld->flags, ML_FIRSTSIDEONLY, key); + break; + default: break; } + sc.MustGetToken(';'); } + if (isTranslated) { int saved = ld->flags; @@ -355,6 +599,10 @@ struct UDMFParser { ld->activation = (ld->activation & ~SPAC_Use) | SPAC_UseThrough; } + if (strifetrans && ld->Alpha == FRACUNIT) + { + ld->Alpha = FRACUNIT * 3/4; + } } void ParseSidedef(side_t *sd, mapsidedef_t *sdt) @@ -365,44 +613,41 @@ struct UDMFParser strncpy(sdt->bottomtexture, "-", 8); strncpy(sdt->toptexture, "-", 8); strncpy(sdt->midtexture, "-", 8); - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) { - sc.MustGetString(); - FName key = sc.String; - sc.MustGetStringName("="); - sc.MustGetString(); - FString value = sc.String; - sc.MustGetStringName(";"); + FName key = ParseKey(); switch(key) { case NAME_Offsetx: - texofs[0] = strtol(value, NULL, 0) << FRACBITS; + texofs[0] = CheckInt(key) << FRACBITS; break; case NAME_Offsety: - texofs[1] = strtol(value, NULL, 0) << FRACBITS; + texofs[1] = CheckInt(key) << FRACBITS; break; case NAME_Texturetop: - strncpy(sdt->toptexture, value, 8); + strncpy(sdt->toptexture, CheckString(key), 8); break; case NAME_Texturebottom: - strncpy(sdt->bottomtexture, value, 8); + strncpy(sdt->bottomtexture, CheckString(key), 8); break; case NAME_Texturemiddle: - strncpy(sdt->midtexture, value, 8); + strncpy(sdt->midtexture, CheckString(key), 8); break; case NAME_Sector: - sd->sector = (sector_t*)(intptr_t)strtol(value, NULL, 0); + sd->sector = (sector_t*)(intptr_t)CheckInt(key); break; default: break; } + sc.MustGetToken(';'); } // initialization of these is delayed to allow separate offsets and add them with the global ones. sd->AddTextureXOffset(side_t::top, texofs[0]); @@ -416,7 +661,7 @@ struct UDMFParser void ParseSector(sector_t *sec) { memset(sec, 0, sizeof(*sec)); - sec->lightlevel = 255; + sec->lightlevel = 160; sec->floor_xscale = FRACUNIT; // [RH] floor and ceiling scaling sec->floor_yscale = FRACUNIT; sec->ceiling_xscale = FRACUNIT; @@ -428,6 +673,7 @@ struct UDMFParser sec->nextsec = -1; //jff 2/26/98 add fields to support locking out sec->prevsec = -1; // stair retriggering until build completes sec->heightsec = NULL; // sector used to get floor and ceiling height + if (floordrop) sec->Flags = SECF_FLOORDROP; // killough 3/7/98: end changes sec->gravity = 1.f; // [RH] Default sector gravity of 1.0 @@ -437,49 +683,50 @@ struct UDMFParser sec->friction = ORIG_FRICTION; sec->movefactor = ORIG_FRICTION_FACTOR; - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) { - sc.MustGetString(); - FName key = sc.String; - sc.MustGetStringName("="); - sc.MustGetString(); - FString value = sc.String; - sc.MustGetStringName(";"); + FName key = ParseKey(); switch(key) { case NAME_Heightfloor: - sec->floortexz = strtol(value, NULL, 0) << FRACBITS; + sec->floortexz = CheckInt(key) << FRACBITS; break; case NAME_Heightceiling: - sec->ceilingtexz = strtol(value, NULL, 0) << FRACBITS; + sec->ceilingtexz = CheckInt(key) << FRACBITS; break; case NAME_Texturefloor: - sec->floorpic = TexMan.GetTexture (value, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); + sec->floorpic = TexMan.GetTexture (CheckString(key), FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); break; case NAME_Textureceiling: - sec->ceilingpic = TexMan.GetTexture (value, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); + sec->ceilingpic = TexMan.GetTexture (CheckString(key), FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); break; case NAME_Lightlevel: - sec->lightlevel = (BYTE)clamp(strtol(value, NULL, 0), 0, 255); + sec->lightlevel = (BYTE)clamp(CheckInt(key), 0, 255); break; case NAME_Special: - sec->special = (short)strtol(value, NULL, 0); + sec->special = (short)CheckInt(key); if (isTranslated) sec->special = P_TranslateSectorSpecial(sec->special); + else if (namespc == NAME_Hexen) + { + if (sec->special < 0 || sec->special > 255 || !HexenSectorSpecialOk[sec->special]) + sec->special = 0; // NULL all unknown specials + } break; - case NAME_Tag: - sec->tag = (short)strtol(value, NULL, 0); + case NAME_Id: + sec->tag = (short)CheckInt(key); break; default: break; } + sc.MustGetToken(';'); } sec->floorplane.d = -sec->floortexz; @@ -603,7 +850,6 @@ struct UDMFParser char *buffer = new char[map->Size(ML_TEXTMAP)]; isTranslated = true; - isExtended = false; map->Read(ML_TEXTMAP, buffer); sc.OpenMem(Wads.GetLumpFullName(map->lumpnum), buffer, map->Size(ML_TEXTMAP)); @@ -613,34 +859,58 @@ struct UDMFParser sc.MustGetStringName("="); sc.MustGetString(); namespc = sc.String; - if (namespc == NAME_ZDoom) + switch(namespc) { + case NAME_ZDoom: + namespace_bits = Zd; isTranslated = false; - isExtended = true; - } - else if (namespc == NAME_Hexen) - { + break; + case NAME_ZDoomTranslated: + namespace_bits = Zdt; + break; + case NAME_Hexen: + namespace_bits = Hx; isTranslated = false; - } - else if (namespc == NAME_ZDoomTranslated) - { - isExtended = true; - } - else if (namespc == NAME_Doom) - { + break; + case NAME_Doom: + namespace_bits = Dm; P_LoadTranslator("xlat/doom_base.txt"); - } - else if (namespc == NAME_Heretic) - { + level.flags |= LEVEL_DUMMYSWITCHES; + floordrop = true; + break; + case NAME_Heretic: + namespace_bits = Ht; P_LoadTranslator("xlat/heretic_base.txt"); - } - else if (namespc == NAME_Strife) - { + level.flags |= LEVEL_DUMMYSWITCHES; + floordrop = true; + break; + case NAME_Strife: + namespace_bits = St; P_LoadTranslator("xlat/strife_base.txt"); - } - else - { - Printf("Unknown namespace %s\n", sc.String); + level.flags |= LEVEL_DUMMYSWITCHES|LEVEL_RAILINGHACK; + floordrop = true; + break; + default: + Printf("Unknown namespace %s. Using defaults for %s\n", sc.String, GameNames[gameinfo.gametype]); + switch (gameinfo.gametype) + { + case GAME_Doom: + namespace_bits = Dm; + P_LoadTranslator("xlat/doom_base.txt"); + break; + case GAME_Heretic: + namespace_bits = Ht; + P_LoadTranslator("xlat/heretic_base.txt"); + break; + case GAME_Strife: + namespace_bits = St; + P_LoadTranslator("xlat/strife_base.txt"); + break; + case GAME_Hexen: + namespace_bits = Hx; + isTranslated = false; + break; + } } sc.MustGetStringName(";"); } @@ -693,8 +963,8 @@ struct UDMFParser // Create the real sectors numsectors = ParsedSectors.Size(); sectors = new sector_t[numsectors]; - sectors[0].e = new extsector_t[numsectors]; memcpy(sectors, &ParsedSectors[0], numsectors * sizeof(*sectors)); + sectors[0].e = new extsector_t[numsectors]; for(int i = 0; i < numsectors; i++) { sectors[i].e = §ors[0].e[i]; diff --git a/src/p_user.cpp b/src/p_user.cpp index 634092d51..3c1d48b87 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2202,7 +2202,7 @@ void P_PlayerThink (player_t *player) } if (!--player->morphTics) { // Attempt to undo the chicken/pig - P_UndoPlayerMorph (player); + P_UndoPlayerMorph (player, player); } } // Cycle psprites diff --git a/src/po_man.cpp b/src/po_man.cpp index 8c4181823..4ac6a12c8 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -23,6 +23,7 @@ #include "s_sndseq.h" #include "a_sharedglobal.h" #include "r_main.h" +#include "p_lnspec.h" // MACROS ------------------------------------------------------------------ @@ -1172,8 +1173,8 @@ static void InitSegLists () if (segs[i].linedef != NULL) { SegListHead[segs[i].v1 - vertexes] = i; - if ((segs[i].linedef->special == PO_LINE_START || - segs[i].linedef->special == PO_LINE_EXPLICIT)) + if ((segs[i].linedef->special == Polyobj_StartLine || + segs[i].linedef->special == Polyobj_ExplicitLine)) { KnownPolySegs.Push (i); } @@ -1265,7 +1266,7 @@ static void SpawnPolyobj (int index, int tag, int type) continue; } - if (segs[i].linedef->special == PO_LINE_START && + if (segs[i].linedef->special == Polyobj_StartLine && segs[i].linedef->args[0] == tag) { if (polyobjs[index].segs) @@ -1306,7 +1307,7 @@ static void SpawnPolyobj (int index, int tag, int type) i = KnownPolySegs[ii]; if (i >= 0 && - segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->special == Polyobj_ExplicitLine && segs[i].linedef->args[0] == tag) { if (!segs[i].linedef->args[1]) @@ -1327,7 +1328,7 @@ static void SpawnPolyobj (int index, int tag, int type) { i = KnownPolySegs[ii]; if (i >= 0 && - segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->special == Polyobj_ExplicitLine && segs[i].linedef->args[0] == tag && segs[i].linedef->args[1] == j) { segs[i].linedef->special = 0; @@ -1344,7 +1345,7 @@ static void SpawnPolyobj (int index, int tag, int type) { i = KnownPolySegs[ii]; if (i >= 0 && - segs[i].linedef->special == PO_LINE_EXPLICIT && + segs[i].linedef->special == Polyobj_ExplicitLine && segs[i].linedef->args[0] == tag) { I_Error ("SpawnPolyobj: Missing explicit line %d for poly %d\n", diff --git a/src/r_defs.h b/src/r_defs.h index 7e61c1c33..01df40b65 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -256,6 +256,7 @@ enum { SECF_SILENT = 1, // actors in sector make no noise SECF_NOFALLINGDAMAGE= 2, // No falling damage in this sector + SECF_FLOORDROP = 4, // all actors standing on this floor will remain on it when it lowers very fast. }; struct FDynamicColormap; diff --git a/src/sc_man.cpp b/src/sc_man.cpp index 7c590abc0..590cc26b3 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -982,6 +982,32 @@ void STACK_ARGS FScanner::ScriptError (const char *message, ...) AlreadyGot? AlreadyGotLine : Line, composed.GetChars()); } +//========================================================================== +// +// FScanner::ScriptError +// +//========================================================================== + +void STACK_ARGS FScanner::ScriptMessage (const char *message, ...) +{ + FString composed; + + if (message == NULL) + { + composed = "Bad syntax."; + } + else + { + va_list arglist; + va_start (arglist, message); + composed.VFormat (message, arglist); + va_end (arglist); + } + + Printf ("Script error, \"%s\" line %d:\n%s\n", ScriptName.GetChars(), + AlreadyGot? AlreadyGotLine : Line, composed.GetChars()); +} + //========================================================================== // // FScanner :: CheckOpen diff --git a/src/sc_man.h b/src/sc_man.h index cbfc00096..a7ed75f02 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -58,6 +58,7 @@ public: int MustMatchString(const char **strings); void ScriptError(const char *message, ...); + void ScriptMessage(const char *message, ...); // Members ------------------------------------------------------ char *String; diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 255e71dc6..16cc6ecc5 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -147,7 +147,7 @@ int FTextureManager::CheckForTexture (const char *name, int usetype, BITFIELD fl if ((flags & TEXMAN_TryAny) && usetype != FTexture::TEX_Any) { // Never return the index of NULL textures. - if (firsttype == FTexture::TEX_Null) return 0; + if (firstfound != -1 && firsttype == FTexture::TEX_Null) return 0; return firstfound; } diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index f3c5c2fc7..3bd5e2071 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -756,11 +756,13 @@ static int ParseMorphStyle (FScanner &sc) { static const char * morphstyles[]={ "MRF_ADDSTAMINA", "MRF_FULLHEALTH", "MRF_UNDOBYTOMEOFPOWER", "MRF_UNDOBYCHAOSDEVICE", - "MRF_FAILNOTELEFRAG", "MRF_FAILNOLAUGH", "MRF_WHENINVULNERABLE", "MRF_LOSEACTUALWEAPON", NULL}; - + "MRF_FAILNOTELEFRAG", "MRF_FAILNOLAUGH", "MRF_WHENINVULNERABLE", "MRF_LOSEACTUALWEAPON", + "MRF_NEWTIDBEHAVIOUR", "MRF_UNDOBYDEATH", "MRF_UNDOBYDEATHFORCED", "MRF_UNDOBYDEATHSAVES", NULL}; + static const int morphstyle_values[]={ MORPH_ADDSTAMINA, MORPH_FULLHEALTH, MORPH_UNDOBYTOMEOFPOWER, MORPH_UNDOBYCHAOSDEVICE, - MORPH_FAILNOTELEFRAG, MORPH_FAILNOLAUGH, MORPH_WHENINVULNERABLE, MORPH_LOSEACTUALWEAPON}; + MORPH_FAILNOTELEFRAG, MORPH_FAILNOLAUGH, MORPH_WHENINVULNERABLE, MORPH_LOSEACTUALWEAPON, + MORPH_NEWTIDBEHAVIOUR, MORPH_UNDOBYDEATH, MORPH_UNDOBYDEATHFORCED, MORPH_UNDOBYDEATHSAVES}; // May be given flags by number... if (sc.CheckNumber()) diff --git a/src/xlat/parse_xlat.cpp b/src/xlat/parse_xlat.cpp index 904b1727c..c1c71a28b 100644 --- a/src/xlat/parse_xlat.cpp +++ b/src/xlat/parse_xlat.cpp @@ -115,13 +115,13 @@ struct XlatParseContext : public FParseContext { "arg2", "arg3", "arg4", "arg5", "bitmask", "clear", "define", "enum", "flags", "include", "lineid", - "nobitmask", "sector", "tag", "maxlinespecial" + "maxlinespecial", "nobitmask", "sector", "tag" }; static const short types[] = { XLAT_ARG2, XLAT_ARG3, XLAT_ARG4, XLAT_ARG5, XLAT_BITMASK, XLAT_CLEAR, XLAT_DEFINE, XLAT_ENUM, XLAT_FLAGS, XLAT_INCLUDE, XLAT_TAG, - XLAT_NOBITMASK, XLAT_SECTOR, XLAT_TAG, XLAT_MAXLINESPECIAL + XLAT_MAXLINESPECIAL, XLAT_NOBITMASK, XLAT_SECTOR, XLAT_TAG }; int min = 0, max = countof(tokens) - 1;