From 66c663e9d8f9ece26655f044b335bcd1a28c8aa7 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Thu, 20 Apr 2006 14:21:27 +0000
Subject: [PATCH] - Fixed: Hexen's ammo display in the status bar cannot be
 refreshed   partially because the background patch has to be drawn always to 
  overwrite the old display. - Fixed: Giving a health item to a non-player
 caused a crash. - Added a compatibility option to limit deh.MaxHealth to the
 health bonus.   Originally this value wasn't used for health packs. Doing
 this was a bug   in Boom but since there's quite a few maps out there which
 require   Boom's altered behavior it has to be compatibility optioned. -
 Fixed: The health bonus's max health must be defined by deh.MaxHealth,   not
 deh.MaxSoulsphere. To achieve this deh.MaxHealth's handling had to   be
 altered because it has to default to 100. - Fixed: ZDBSP created incorrect
 side references with compressed sidedefs   and both sidedefs of a linedef
 being the same. This only affects the   external tool because the internal
 node builder is run after uncompressing   the sidedefs. - Added Jim's latest
 makefile.linux. - Added a consistency check to the PNAMES loader because one
 crash log   indicated that it crashed due to a corrupt PNAMES lump. - Brought
 back the sector based sound target handling as a compatibility   option. This
 radical change just broke far too many maps that depend   on the original
 behavior. Strife's special AI functions are excluded   though because they
 work better with the new method.

SVN r56 (trunk)
---
 Makefile.linux                 | 43 +++++++++++++++++++---------------
 docs/rh-log.txt                | 22 +++++++++++++++++
 src/b_think.cpp                |  2 +-
 src/d_dehacked.cpp             |  8 +++++--
 src/d_main.cpp                 |  2 ++
 src/doomdef.h                  |  2 ++
 src/g_hexen/hexen_sbar.cpp     | 41 ++++++++++----------------------
 src/g_shared/a_pickups.cpp     | 42 +++++++++++++++++++++------------
 src/g_strife/a_merchants.cpp   |  1 +
 src/g_strife/a_oracle.cpp      |  2 +-
 src/g_strife/a_strifestuff.cpp |  6 -----
 src/m_menu.cpp                 | 15 ++++++++----
 src/m_options.cpp              |  2 ++
 src/p_enemy.cpp                |  3 ++-
 src/p_local.h                  |  1 -
 src/p_saveg.cpp                |  1 +
 src/r_data.cpp                 | 17 ++++++++++++++
 src/r_defs.h                   |  1 +
 18 files changed, 133 insertions(+), 78 deletions(-)

diff --git a/Makefile.linux b/Makefile.linux
index 3ac87287d..3280acc9e 100644
--- a/Makefile.linux
+++ b/Makefile.linux
@@ -19,10 +19,10 @@ DEBUGOBJ ?= debugobj
 CPPSRCS = $(wildcard $(addsuffix *.cpp,$(SRCDIRS)))
 CSRCS = $(wildcard $(addsuffix *.c,$(SRCDIRS)))
 ifndef NOASM
-ASRCS = $(wildcard src/*.nas)
-CFLAGS += -DUSEASM=1
+  ASRCS = $(wildcard src/*.nas)
+  CFLAGS += -DUSEASM=1
 else
-CFLAGS += -DNOASM
+  CFLAGS += -DNOASM
 endif
 SRCS = $(CSRCS) $(CPPSRCS) $(ASRCS)
 CPPOBJFILES = $(notdir $(patsubst %.cpp,%.o,$(CPPSRCS)))
@@ -54,27 +54,30 @@ RESTART?=1
 # rule pattern for dependencies
 define DEPBUILD_PATTERN
 _dep_: _src_
-   $(CXX) _src_ -MM $(CXXFLAGS) -MT "$$(patsubst %.d,%.o,_dep_) _dep_" -MF _dep_
+	$(CXX) _src_ -MM $(CXXFLAGS) -MT "$$(patsubst %.d,%.o,_dep_) _dep_" -MF _dep_
 endef
 
 # rule pattern for assembly files
 define ASMBUILD_PATTERN
 _obj_: _src_
-   $(NASM) -o _obj_ $(NASMFLAGS) _src_
+	$(NASM) -o _obj_ $(NASMFLAGS) _src_
 endef
 
 define CBUILD_PATTERN
 _obj_: _src_
-   $(CC) -c $(CFLAGS) -o _obj_ -c _src_
+	$(CC) -c $(CFLAGS) -o _obj_ -c _src_
 endef
 
 all: $(ZDOOMBIN) zdoom.wad
 
-$(ZDOOMBIN): $(OBJDIR) deps $(OBJS)
-   $(CXX) $(LDFLAGS) $(OBJDIR)/autostart.o \
-   $(filter-out %/autostart.o %/autozend.o,$(OBJS)) $(OBJDIR)/autozend.o -o $(ZDOOMBIN)
+$(ZDOOMBIN): $(OBJDIR) $(if $(RESTART),deps) $(OBJS)
+ifndef RESTART
+	$(CXX) $(LDFLAGS) $(OBJDIR)/autostart.o \
+	$(filter-out %/autostart.o %/autozend.o,$(OBJS)) \
+	$(OBJDIR)/autozend.o -o $(ZDOOMBIN)
+endif
 
-#include any of the dep files that already exist
+# include any of the dep files that already exist
 $(foreach dep,$(DEPS),$(if $(wildcard $(dep)),$(eval include $(dep))))
 
 # textually substitute in the _dep_ and the _src_ it depends on to create rules
@@ -92,30 +95,32 @@ $(foreach src,$(CSRCS), $(eval $(subst _src_,$(src),$(subst \
 _obj_,$(OBJDIR)/$(patsubst %.c,%.o,$(notdir $$$(src))),$(CBUILD_PATTERN)))))
 
 $(OBJDIR)/%.o:
-   $(CXX) -c $(CXXFLAGS) -o $@ -c $<
+	$(CXX) -c $(CXXFLAGS) -o $@ -c $<
 
 # start a new instance of make after dependency files have been made
 deps: $(DEPS)
-   $(if $(RESTART),@make -f $(firstword $(MAKEFILE_LIST)) RESTART=)
+ifdef RESTART
+	@make -f $(firstword $(MAKEFILE_LIST)) RESTART=
+endif
 
 $(OBJDIR):
-   mkdir $(OBJDIR)
+	mkdir $(OBJDIR)
 
 zdoom.wad:
-   make -C wadsrc/ -f Makefile
+	make -C wadsrc/ -f Makefile
 
 .PHONY : clean cleandeps cleanobjs distclean deps
 
 clean: cleanobjs
-   rm -f $(ZDOOMDEBUG) $(ZDOOM) $(ZDOOM).map
+	rm -f $(ZDOOMDEBUG) $(ZDOOM) $(ZDOOM).map
 
 # I could use a recursive delete instead, but that could be dangerous...
 distclean: clean cleandeps
-   rmdir $(RELEASEOBJ) $(DEBUGOBJ)
+	rmdir $(RELEASEOBJ) $(DEBUGOBJ)
 
 cleandeps:
-   rm -f $(RELEASEOBJ)/*.d $(DEBUGOBJ)/*.d
+	rm -f $(RELEASEOBJ)/*.d $(DEBUGOBJ)/*.d
 
 cleanobjs:
-   rm -f $(RELEASEOBJ)/*.o $(DEBUGOBJ)/*.o 
-   
\ No newline at end of file
+	rm -f $(RELEASEOBJ)/*.o $(DEBUGOBJ)/*.o 
+   
diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index 787305967..1cc456792 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,4 +1,26 @@
 April 19, 2006 (Changes by Graf Zahl)
+- Fixed: Hexen's ammo display in the status bar cannot be refreshed 
+  partially because the background patch has to be drawn always to 
+  overwrite the old display.
+- Fixed: Giving a health item to a non-player caused a crash.
+- Added a compatibility option to limit deh.MaxHealth to the health bonus.
+  Originally this value wasn't used for health packs. Doing this was a bug
+  in Boom but since there's quite a few maps out there which require
+  Boom's altered behavior it has to be compatibility optioned.
+- Fixed: The health bonus's max health must be defined by deh.MaxHealth, 
+  not deh.MaxSoulsphere. To achieve this deh.MaxHealth's handling had to
+  be altered because it has to default to 100.
+- Fixed: ZDBSP created incorrect side references with compressed sidedefs
+  and both sidedefs of a linedef being the same. This only affects the
+  external tool because the internal node builder is run after uncompressing
+  the sidedefs.
+- Added Jim's latest makefile.linux.
+- Added a consistency check to the PNAMES loader because one crash log
+  indicated that it crashed due to a corrupt PNAMES lump.
+- Brought back the sector based sound target handling as a compatibility
+  option. This radical change just broke far too many maps that depend
+  on the original behavior. Strife's special AI functions are excluded 
+  though because they work better with the new method.
 - Fixed: Hexen had no default sound sequence for doors and passed a NULL
   pointer to SN_StartSequence in DoorSound.
 
diff --git a/src/b_think.cpp b/src/b_think.cpp
index f59ba922f..a43606fc9 100644
--- a/src/b_think.cpp
+++ b/src/b_think.cpp
@@ -363,7 +363,7 @@ void DCajunMaster::WhatToGet (AActor *actor, AActor *item)
 	}
 	else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && actor->health >= deh.MaxSoulsphere)
 		return;
-	else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= MAXHEALTH)
+	else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/)
 		return;
 
 	if ((b->dest == NULL ||
diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp
index 08b86a3ef..04cbcadc3 100644
--- a/src/d_dehacked.cpp
+++ b/src/d_dehacked.cpp
@@ -129,7 +129,7 @@ DehInfo deh =
 {
 	100,	// .StartHealth
 	 50,	// .StartBullets
-	100,	// .MaxHealth
+	 -1,	// .MaxHealth
 	200,	// .MaxArmor
 	  1,	// .GreenAC
 	  2,	// .BlueAC
@@ -1640,7 +1640,7 @@ static int PatchMisc (int dummy)
 
 	AHealth *health;
 	health = static_cast<AHealth *> (GetDefaultByName ("HealthBonus"));
-	health->MaxAmount = deh.MaxSoulsphere;
+	if (deh.MaxHealth != -1) health->MaxAmount = deh.MaxHealth;
 
 	// 0xDD means "enable infighting"
 	if (infighting == 0xDD)
@@ -2531,6 +2531,10 @@ void FinishDehPatch ()
 
 		DPrintf ("%s replaces %s\n", subclass->Name, type->Name);
 	}
+
+	// Since deh.MaxHealth was used incorrectly this can only be set
+	// after finishing with the DEH stuff.
+	if (deh.MaxHealth == -1) deh.MaxHealth = 100;
 }
 
 void HandleNoSector()
diff --git a/src/d_main.cpp b/src/d_main.cpp
index fec5f05ba..28a52f36a 100644
--- a/src/d_main.cpp
+++ b/src/d_main.cpp
@@ -390,6 +390,8 @@ CVAR (Flag, compat_notossdrops,	compatflags, COMPATF_NOTOSSDROPS);
 CVAR (Flag, compat_useblocking, compatflags, COMPATF_USEBLOCKING);
 CVAR (Flag, compat_nodoorlight,	compatflags, COMPATF_NODOORLIGHT);
 CVAR (Flag, compat_ravenscroll,	compatflags, COMPATF_RAVENSCROLL);
+CVAR (Flag, compat_soundtarget,	compatflags, COMPATF_SOUNDTARGET);
+CVAR (Flag, compat_dehhealth,	compatflags, COMPATF_DEHHEALTH);
 
 //==========================================================================
 //
diff --git a/src/doomdef.h b/src/doomdef.h
index 2f454cb7f..731a36ae5 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -262,6 +262,8 @@ enum
 	COMPATF_USEBLOCKING		= 1 << 8,	// Any special line can block a use line
 	COMPATF_NODOORLIGHT		= 1 << 9,	// Don't do the BOOM local door light effect
 	COMPATF_RAVENSCROLL		= 1 << 10,	// Raven's scrollers use their original carrying speed
+	COMPATF_SOUNDTARGET		= 1 << 11,	// Use sector based sound target code.
+	COMPATF_DEHHEALTH		= 1 << 12,	// Limit deh.MaxHealth to the health bonus (as in Doom2.exe)
 };
 
 // phares 3/20/98:
diff --git a/src/g_hexen/hexen_sbar.cpp b/src/g_hexen/hexen_sbar.cpp
index f4811e854..bd569d552 100644
--- a/src/g_hexen/hexen_sbar.cpp
+++ b/src/g_hexen/hexen_sbar.cpp
@@ -197,8 +197,6 @@ public:
 		Mana1Refresh = 0;
 		Mana2Refresh = 0;
 		AmmoRefresh = 0;
-		Ammo1Refresh = 0;
-		Ammo2Refresh = 0;
 	}
 
 	~FHexenStatusBar ()
@@ -509,51 +507,40 @@ private:
 
 	void DrawMainAltAmmo (AAmmo *ammo1, AAmmo *ammo2, int ammocount1, int ammocount2)
 	{
-		if (AmmoRefresh)
-		{
-			Ammo1Refresh = MAX(AmmoRefresh, Ammo1Refresh);
-			Ammo2Refresh = MAX(AmmoRefresh, Ammo2Refresh);
-			AmmoRefresh--;
-			DrawImage (Images[imgAMMOBACK], 77, 2);
-		}
 		if (ammo1 != oldammo1 || ammocount1 != oldammocount1)
 		{
-			Ammo1Refresh = screen->GetPageCount ();
+			AmmoRefresh = screen->GetPageCount ();
 			oldammo1 = ammo1;
 			oldammocount1 = ammocount1;
 		}
 		if (ammo2 != oldammo2 || ammocount2 != oldammocount2)
 		{
-			Ammo2Refresh = screen->GetPageCount ();
+			AmmoRefresh = screen->GetPageCount ();
 			oldammo2 = ammo2;
 			oldammocount2 = ammocount2;
 		}
-		if (ammo2 != NULL)
-		{ // Draw both ammos
-			if (Ammo1Refresh)
-			{
-				Ammo1Refresh--;
+
+		if (AmmoRefresh)
+		{
+			AmmoRefresh--;
+			DrawImage (Images[imgAMMOBACK], 77, 2);
+			if (ammo2 != NULL)
+			{ // Draw both ammos
+				AmmoRefresh--;
 				screen->DrawTexture (TexMan[ammo1->Icon], 89+ST_X, 10+ST_Y,
 					DTA_CenterOffset, true,
 					DTA_320x200, true,
 					TAG_DONE);
 				DrSmallNumber (ammo1->Amount, 86, 20);
-			}
-			if (Ammo2Refresh)
-			{
-				Ammo2Refresh--;
+
 				screen->DrawTexture (TexMan[ammo2->Icon], 113+ST_X, 10+ST_Y,
 					DTA_CenterOffset, true,
 					DTA_320x200, true,
 					TAG_DONE);
 				DrSmallNumber (ammo2->Amount, 110, 20);
 			}
-		}
-		else
-		{ // Draw one ammo
-			if (Ammo1Refresh)
-			{
-				Ammo1Refresh--;
+			else
+			{ // Draw one ammo
 				screen->DrawTexture (TexMan[ammo1->Icon], 100+ST_X, 10+ST_Y,
 					DTA_CenterOffset, true,
 					DTA_320x200, true,
@@ -1150,8 +1137,6 @@ private:
 	char Mana1Refresh;
 	char Mana2Refresh;
 	char AmmoRefresh;
-	char Ammo1Refresh;
-	char Ammo2Refresh;
 
 	FManaBar ManaVial1Pic;
 	FManaBar ManaVial2Pic;
diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp
index dad016903..3197b69d8 100644
--- a/src/g_shared/a_pickups.cpp
+++ b/src/g_shared/a_pickups.cpp
@@ -183,7 +183,7 @@ bool P_GiveBody (AActor *actor, int num)
 
 	if (player != NULL)
 	{
-		max = MAXHEALTH + player->stamina;
+		max = ((compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth) + player->stamina;
 		if (player->morphTics)
 		{
 			max = MAXMORPHHEALTH;
@@ -1728,31 +1728,43 @@ bool AHealth::TryPickup (AActor *other)
 	player_t *player = other->player;
 	int max = MaxAmount;
 	
-	if (max == 0)
+	if (player != NULL)
 	{
-		max = MAXHEALTH + (player != NULL ? player->stamina : 0);
-		if (player->morphTics)
+		if (max == 0)
 		{
-			max = MAXMORPHHEALTH;
+			max = ((compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth) + player->stamina;
+			if (player->morphTics)
+			{
+				max = MAXMORPHHEALTH;
+			}
 		}
+		if (player->health >= max)
+		{
+			// You should be able to pick up the Doom health bonus even if
+			// you are already full on health.
+			if (ItemFlags & IF_ALWAYSPICKUP)
+			{
+				GoAwayAndDie ();
+				return true;
+			}
+			return false;
+		}
+		player->health += Amount;
+		if (player->health > max)
+		{
+			player->health = max;
+		}
+		player->mo->health = player->health;
 	}
-	if (player->health >= max)
+	else
 	{
-		// You should be able to pick up the Doom health bonus even if
-		// you are already full on health.
-		if (ItemFlags & IF_ALWAYSPICKUP)
+		if (P_GiveBody(other, Amount) || ItemFlags & IF_ALWAYSPICKUP)
 		{
 			GoAwayAndDie ();
 			return true;
 		}
 		return false;
 	}
-	player->health += Amount;
-	if (player->health > max)
-	{
-		player->health = max;
-	}
-	player->mo->health = player->health;
 	GoAwayAndDie ();
 	return true;
 }
diff --git a/src/g_strife/a_merchants.cpp b/src/g_strife/a_merchants.cpp
index b56b8a1e6..4fa29d4b3 100644
--- a/src/g_strife/a_merchants.cpp
+++ b/src/g_strife/a_merchants.cpp
@@ -209,6 +209,7 @@ void A_ClearSoundTarget (AActor *self)
 {
 	AActor *actor;
 
+	self->Sector->SoundTarget = NULL;
 	for (actor = self->Sector->thinglist; actor != NULL; actor = actor->snext)
 	{
 		actor->LastHeard = NULL;
diff --git a/src/g_strife/a_oracle.cpp b/src/g_strife/a_oracle.cpp
index 806d24cb7..9ed0b9f29 100644
--- a/src/g_strife/a_oracle.cpp
+++ b/src/g_strife/a_oracle.cpp
@@ -64,7 +64,7 @@ void A_WakeOracleSpectre (AActor *self)
 
 	if (spectre != NULL)
 	{
-		spectre->LastHeard = self->LastHeard;
+		spectre->Sector->SoundTarget = spectre->LastHeard = self->LastHeard;
 		spectre->target = self->target;
 		spectre->SetState (spectre->SeeState);
 	}
diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp
index 3db072dd9..ade4469f7 100644
--- a/src/g_strife/a_strifestuff.cpp
+++ b/src/g_strife/a_strifestuff.cpp
@@ -686,12 +686,6 @@ void A_KlaxonBlare (AActor *self)
 	}
 	if (self->reactiontime == 2)
 	{
-		/*
-		for (AActor *actor = self->Sector->thinglist; actor != NULL; actor = actor->snext)
-		{
-			actor->LastHeard = NULL;
-		}
-		*/
 		// [RH] Unalert monsters near the alarm and not just those in the same sector as it.
 		P_NoiseAlert (NULL, self, false);
 	}
diff --git a/src/m_menu.cpp b/src/m_menu.cpp
index 8a48aab7a..aa63cff6a 100644
--- a/src/m_menu.cpp
+++ b/src/m_menu.cpp
@@ -181,6 +181,7 @@ static char		underscore[2];
 static int		MenuPClass;
 
 static FSaveGameNode *quickSaveSlot;	// NULL = no quicksave slot picked!
+static FSaveGameNode *lastSaveSlot;	// Used for highlighting the most recently used slot in the menu
 static int 		messageToPrint;			// 1 = message to be printed
 static const char *messageString;		// ...and here is the message string!
 static EMenuState messageLastMenuActive;
@@ -796,9 +797,10 @@ void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave)
 		SelSaveGame = node;
 	}
 
-	if (quickSaveSlot == NULL && okForQuicksave)
+	if (okForQuicksave)
 	{
-		quickSaveSlot = node;
+		if (quickSaveSlot == NULL) quickSaveSlot = node;
+		lastSaveSlot = node;
 	}
 }
 
@@ -1244,13 +1246,13 @@ void M_SaveGame (int choice)
 	M_ReadSaveStrings();
 	SaveGames.AddHead (&NewSaveNode);
 	TopSaveGame = static_cast<FSaveGameNode *>(SaveGames.Head);
-	if (quickSaveSlot == NULL)
+	if (lastSaveSlot == NULL)
 	{
 		SelSaveGame = &NewSaveNode;
 	}
 	else
 	{
-		SelSaveGame = quickSaveSlot;
+		SelSaveGame = lastSaveSlot;
 	}
 	M_ExtractSaveData (SelSaveGame);
 }
@@ -2808,6 +2810,10 @@ static void M_DeleteSaveResponse (int choice)
 		{
 			quickSaveSlot = NULL;
 		}
+		if (lastSaveSlot == SelSaveGame)
+		{
+			lastSaveSlot = NULL;
+		}
 		SelSaveGame->Remove ();
 		delete SelSaveGame;
 		SelSaveGame = next;
@@ -3120,6 +3126,7 @@ void M_Init (void)
 	messageString = NULL;
 	messageLastMenuActive = menuactive;
 	quickSaveSlot = NULL;
+	lastSaveSlot = NULL;
 	strcpy (NewSaveNode.Title, "<New Save Game>");
 
 	underscore[0] = (gameinfo.gametype & (GAME_Doom|GAME_Strife)) ? '_' : '[';
diff --git a/src/m_options.cpp b/src/m_options.cpp
index 9df06dd6c..137895113 100644
--- a/src/m_options.cpp
+++ b/src/m_options.cpp
@@ -954,6 +954,8 @@ static menuitem_t CompatibilityItems[] = {
 	{ bitflag,  "All special lines can block <use>",		{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_USEBLOCKING} },
 	{ bitflag,	"Disable BOOM door light effect",			{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NODOORLIGHT} },
 	{ bitflag,	"Raven scrollers use original speed",		{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_RAVENSCROLL} },
+	{ bitflag,	"Use original sound target handling",		{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SOUNDTARGET} },
+	{ bitflag,	"DEH health settings like Doom2.exe",		{&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_DEHHEALTH} },
 	{ discrete, "Interpolate monster movement",	{&nomonsterinterpolation},		{2.0}, {0.0},	{0.0}, {NoYes} },
 };
 
diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp
index 0195da305..7f7e1c341 100644
--- a/src/p_enemy.cpp
+++ b/src/p_enemy.cpp
@@ -125,6 +125,7 @@ void P_RecursiveSound (sector_t *sec, AActor *soundtarget, bool splash, int soun
 	
 	sec->validcount = validcount;
 	sec->soundtraversed = soundblocks+1;
+	sec->SoundTarget = soundtarget;
 
 	// [RH] Set this in the actors in the sector instead of the sector itself.
 	for (actor = sec->thinglist; actor != NULL; actor = actor->snext)
@@ -1385,7 +1386,7 @@ void A_Look (AActor *actor)
 	}
 	else
 	{
-		targ = actor->LastHeard;
+		targ = (compatflags & COMPATF_SOUNDTARGET)? actor->Sector->SoundTarget : actor->LastHeard;
 
 		// [RH] If the soundtarget is dead, don't chase it
 		if (targ != NULL && targ->health <= 0)
diff --git a/src/p_local.h b/src/p_local.h
index 9e3bc024c..e9f0d263d 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -33,7 +33,6 @@
 
 #define STEEPSLOPE		46341	// [RH] Minimum floorplane.c value for walking
 
-#define MAXHEALTH		(deh.MaxHealth)		//100
 #define MAXMORPHHEALTH	30
 
 #define BONUSADD		6
diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp
index f567e7da1..71257d1c3 100644
--- a/src/p_saveg.cpp
+++ b/src/p_saveg.cpp
@@ -118,6 +118,7 @@ void P_SerializeWorld (FArchive &arc)
 			<< sec->gravity
 			<< sec->damage
 			<< sec->mod
+			<< sec->SoundTarget
 			<< sec->SecActTarget
 			<< sec->FloorLight
 			<< sec->CeilingLight
diff --git a/src/r_data.cpp b/src/r_data.cpp
index a27d26b97..9224ae221 100644
--- a/src/r_data.cpp
+++ b/src/r_data.cpp
@@ -636,6 +636,23 @@ void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int p
 
 		pnames >> numpatches;
 
+		// Check whether the amount of names reported is correct.
+		if (numpatches < 0)
+		{
+			I_Error("Corrupt PNAMES lump found (negative amount of entries reported)");
+			return;
+		}
+
+		// Check whether the amount of names reported is correct.
+		int lumplength = Wads.LumpLength(patcheslump);
+		if (numpatches > (lumplength-4)/8)
+		{
+			Printf("PNAMES lump is shorter than required (%ld entries reported but only %d bytes (%ld entries) long\n",
+				numpatches, lumplength, (lumplength-4)/8);
+			// Truncate but continue reading. Who knows how many such lumps exist?
+			numpatches = (lumplength-4)/8;
+		}
+
 		// Catalog the patches these textures use so we know which
 		// textures they represent.
 		patchlookup = (FPatchLookup *)alloca (numpatches * sizeof(*patchlookup));
diff --git a/src/r_defs.h b/src/r_defs.h
index cb6af375d..3ecb0dc51 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -302,6 +302,7 @@ struct sector_t
 	short		floorpic, ceilingpic;
 	BYTE		lightlevel;
 
+	AActor *	SoundTarget;
 	byte 		soundtraversed;	// 0 = untraversed, 1,2 = sndlines -1
 
 	short		special;