From 3638c658d60ca55ef99fc26e074ecdc5e04de3f9 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Sat, 23 Aug 2008 08:48:19 +0000
Subject: [PATCH] - Added and fixed Boss death submission for random spawner. -
 Added functions to FActorInfo that can set the damage factors and   pain
 chances to reduce the chance of new errors when working with   these
 features. - Fixed: The handling of the deprecated FIRERESIST flag didn't
 work.   There were 3 problems:   * Actor defaults have no class information
 so HandleDeprecatedFlags     needs to be passed a pointer to the ActorInfo.  
 * The DamageFactors list is only created when needed so the code needs to    
 check if it already exists.   * damage factors are stored as fixed_t but this
 set a float. - Added a traditional Strife color set for the automap.

SVN r1183 (trunk)
---
 docs/rh-log.txt                      |  16 +++
 src/am_map.cpp                       | 146 +++++++++++++++++----------
 src/g_shared/a_randomspawner.cpp     |  41 ++++++--
 src/info.cpp                         |  37 +++++++
 src/info.h                           |   2 +
 src/m_options.cpp                    |   9 +-
 src/thingdef/thingdef_properties.cpp |  24 ++---
 7 files changed, 193 insertions(+), 82 deletions(-)

diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index ee22a3f801..9f1caebaf1 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,3 +1,16 @@
+August 22, 2008  (Changes by Graf Zahl)
+- Added and fixed Boss death submission for random spawner.
+- Added functions to FActorInfo that can set the damage factors and
+  pain chances to reduce the chance of new errors when working with
+  these features.
+- Fixed: The handling of the deprecated FIRERESIST flag didn't work.
+  There were 3 problems:
+  * Actor defaults have no class information so HandleDeprecatedFlags
+    needs to be passed a pointer to the ActorInfo.
+  * The DamageFactors list is only created when needed so the code needs to
+    check if it already exists.
+  * damage factors are stored as fixed_t but this set a float.
+
 August 22, 2008
 - Fixed: Timidity::Renderer::reset_voices() must completely zero the
   voices. Because this wasn't done, note_on() could try to access
@@ -5,6 +18,9 @@ August 22, 2008
   random memory. There may be other places where it's a problem, but
   this is where I noticed it, by chance.
 
+August 21, 2008  (Changes by Graf Zahl)
+- Added a traditional Strife color set for the automap.
+
 August 21, 2008  (Changes by Graf Zahl)
 - Fixed: The conversion of the strings in wbstartstruct_t to FStrings
   caused crashes when reloading the hub data.
diff --git a/src/am_map.cpp b/src/am_map.cpp
index f3aaf19b47..2b392461f0 100644
--- a/src/am_map.cpp
+++ b/src/am_map.cpp
@@ -91,6 +91,15 @@ static BYTE DoomPaletteVals[11*3] =
 	0x80,0x80,0x80, 0x6c,0x6c,0x6c
 };
 
+static AMColor StrifeColors[11];
+static BYTE StrifePaletteVals[11*3] =
+{
+	0x00,0x00,0x00,  239, 239,   0, 0x10,0x10,0x10,
+	 199, 195, 195,  119, 115, 115,   55,  59,  91,
+	 119, 115, 115, 0xfc,0x00,0x00, 0x4c,0x4c,0x4c,
+	0x80,0x80,0x80, 0x6c,0x6c,0x6c
+};
+
 #define MAPBITS 12
 #define MapDiv SafeDivScale12
 #define MapMul MulScale12
@@ -122,7 +131,7 @@ CVAR (Bool,  am_showmonsters,		true,		CVAR_ARCHIVE);
 CVAR (Bool,  am_showitems,			false,		CVAR_ARCHIVE);
 CVAR (Bool,  am_showtime,			true,		CVAR_ARCHIVE);
 CVAR (Bool,  am_showtotaltime,		false,		CVAR_ARCHIVE);
-CVAR (Bool,  am_usecustomcolors,	true,		CVAR_ARCHIVE);
+CVAR (Int,   am_colorset,			0,			CVAR_ARCHIVE);
 CVAR (Color, am_backcolor,			0x6c5440,	CVAR_ARCHIVE);
 CVAR (Color, am_yourcolor,			0xfce8d8,	CVAR_ARCHIVE);
 CVAR (Color, am_wallcolor,			0x2c1808,	CVAR_ARCHIVE);
@@ -648,6 +657,7 @@ static void AM_initColors (bool overlayed)
 		for (i = j = 0; i < 11; i++, j += 3)
 		{
 			DoomColors[i].FromRGB(DoomPaletteVals[j], DoomPaletteVals[j+1], DoomPaletteVals[j+2]);
+			StrifeColors[i].FromRGB(StrifePaletteVals[j], StrifePaletteVals[j+1], StrifePaletteVals[j+2]);
 		}
 	}
 
@@ -667,62 +677,88 @@ static void AM_initColors (bool overlayed)
 		InterTeleportColor.FromCVar (am_ovtelecolor);
 		IntraTeleportColor = InterTeleportColor;
 	}
-	else if (am_usecustomcolors)
+	else switch(am_colorset)
 	{
-		/* Use the custom colors in the am_* cvars */
-		Background.FromCVar (am_backcolor);
-		YourColor.FromCVar (am_yourcolor);
-		SecretWallColor.FromCVar (am_secretwallcolor);
-		WallColor.FromCVar (am_wallcolor);
-		TSWallColor.FromCVar (am_tswallcolor);
-		FDWallColor.FromCVar (am_fdwallcolor);
-		CDWallColor.FromCVar (am_cdwallcolor);
-		ThingColor_Item.FromCVar (am_thingcolor_item);
-		ThingColor_Friend.FromCVar (am_thingcolor_friend);
-		ThingColor_Monster.FromCVar (am_thingcolor_monster);
-		ThingColor.FromCVar (am_thingcolor);
-		GridColor.FromCVar (am_gridcolor);
-		XHairColor.FromCVar (am_xhaircolor);
-		NotSeenColor.FromCVar (am_notseencolor);
-		LockedColor.FromCVar (am_lockedcolor);
-		InterTeleportColor.FromCVar (am_interlevelcolor);
-		IntraTeleportColor.FromCVar (am_intralevelcolor);
-		SecretSectorColor.FromCVar (am_secretsectorcolor);
+		default:
+		{
+			/* Use the custom colors in the am_* cvars */
+			Background.FromCVar (am_backcolor);
+			YourColor.FromCVar (am_yourcolor);
+			SecretWallColor.FromCVar (am_secretwallcolor);
+			WallColor.FromCVar (am_wallcolor);
+			TSWallColor.FromCVar (am_tswallcolor);
+			FDWallColor.FromCVar (am_fdwallcolor);
+			CDWallColor.FromCVar (am_cdwallcolor);
+			ThingColor_Item.FromCVar (am_thingcolor_item);
+			ThingColor_Friend.FromCVar (am_thingcolor_friend);
+			ThingColor_Monster.FromCVar (am_thingcolor_monster);
+			ThingColor.FromCVar (am_thingcolor);
+			GridColor.FromCVar (am_gridcolor);
+			XHairColor.FromCVar (am_xhaircolor);
+			NotSeenColor.FromCVar (am_notseencolor);
+			LockedColor.FromCVar (am_lockedcolor);
+			InterTeleportColor.FromCVar (am_interlevelcolor);
+			IntraTeleportColor.FromCVar (am_intralevelcolor);
+			SecretSectorColor.FromCVar (am_secretsectorcolor);
 
-		DWORD ba = am_backcolor;
+			DWORD ba = am_backcolor;
 
-		int r = RPART(ba) - 16;
-		int g = GPART(ba) - 16;
-		int b = BPART(ba) - 16;
+			int r = RPART(ba) - 16;
+			int g = GPART(ba) - 16;
+			int b = BPART(ba) - 16;
 
-		if (r < 0)
-			r += 32;
-		if (g < 0)
-			g += 32;
-		if (b < 0)
-			b += 32;
+			if (r < 0)
+				r += 32;
+			if (g < 0)
+				g += 32;
+			if (b < 0)
+				b += 32;
 
-		AlmostBackground.FromRGB(r, g, b);
-	}
-	else
-	{ // Use colors corresponding to the original Doom's
-		Background = DoomColors[0];
-		YourColor = DoomColors[1];
-		AlmostBackground = DoomColors[2];
-		SecretSectorColor = 		
-			SecretWallColor =
-			WallColor = DoomColors[3];
-		TSWallColor = DoomColors[4];
-		FDWallColor = DoomColors[5];
-		LockedColor =
-			CDWallColor = DoomColors[6];
-		ThingColor_Item = 
-			ThingColor_Friend = 
-			ThingColor_Monster =
-			ThingColor = DoomColors[7];
-		GridColor = DoomColors[8];
-		XHairColor = DoomColors[9];
-		NotSeenColor = DoomColors[10];
+			AlmostBackground.FromRGB(r, g, b);
+			break;
+		}
+
+		case 1:	// Doom
+			// Use colors corresponding to the original Doom's
+			Background = DoomColors[0];
+			YourColor = DoomColors[1];
+			AlmostBackground = DoomColors[2];
+			SecretSectorColor = 		
+				SecretWallColor =
+				WallColor = DoomColors[3];
+			TSWallColor = DoomColors[4];
+			FDWallColor = DoomColors[5];
+			LockedColor =
+				CDWallColor = DoomColors[6];
+			ThingColor_Item = 
+				ThingColor_Friend = 
+				ThingColor_Monster =
+				ThingColor = DoomColors[7];
+			GridColor = DoomColors[8];
+			XHairColor = DoomColors[9];
+			NotSeenColor = DoomColors[10];
+			break;
+
+		case 2:	// Strife
+			// Use colors corresponding to the original Strife's
+			Background = StrifeColors[0];
+			YourColor = StrifeColors[1];
+			AlmostBackground = DoomColors[2];
+			SecretSectorColor = 		
+				SecretWallColor =
+				WallColor = StrifeColors[3];
+			TSWallColor = StrifeColors[4];
+			FDWallColor = StrifeColors[5];
+			LockedColor =
+				CDWallColor = StrifeColors[6];
+			ThingColor_Item = 
+				ThingColor_Friend = 
+				ThingColor_Monster =
+				ThingColor = StrifeColors[7];
+			GridColor = StrifeColors[8];
+			XHairColor = StrifeColors[9];
+			NotSeenColor = StrifeColors[10];
+			break;
 	}
 
 	lastpal = palette;
@@ -1348,7 +1384,7 @@ void AM_drawWalls (bool allmap)
 					lines[i].special == Teleport_ZombieChanger ||
 					lines[i].special == Teleport_Line) &&
 					(lines[i].activation & SPAC_PlayerActivate) &&
-					am_usecustomcolors)
+					am_colorset == 0)
 				{ // intra-level teleporters
 					AM_drawMline(&l, IntraTeleportColor);
 				}
@@ -1356,7 +1392,7 @@ void AM_drawWalls (bool allmap)
 						 lines[i].special == Teleport_EndGame ||
 						 lines[i].special == Exit_Normal ||
 						 lines[i].special == Exit_Secret) &&
-						 am_usecustomcolors)
+						am_colorset == 0)
 				{ // inter-level/game-ending teleporters
 					AM_drawMline(&l, InterTeleportColor);
 				}
@@ -1372,7 +1408,7 @@ void AM_drawWalls (bool allmap)
 						 lines[i].special == ACS_LockedExecuteDoor ||
 						 (lines[i].special == Generic_Door && lines[i].args[4]!=0))
 				{
-					if (am_usecustomcolors)
+					if (am_colorset == 0)
 					{
 						int P_GetMapColorForLock(int lock);
 						int lock;
diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp
index 788e2d888f..c35b097970 100644
--- a/src/g_shared/a_randomspawner.cpp
+++ b/src/g_shared/a_randomspawner.cpp
@@ -1,3 +1,8 @@
+/*
+** a_randomspawner.cpp
+** A thing that randomly spawns one item in a list of many, before disappearing.
+*/
+
 #include "actor.h"
 #include "info.h"
 #include "m_random.h"
@@ -10,12 +15,7 @@
 #include "a_action.h"
 #include "thingdef/thingdef.h"
 
-/*
-- in the decorate definition define multiple drop items
-- use the function GetDropItems to get the first drop item, then iterate over them and count how many dropitems are defined.
-- with M_Random( ) % NUMBEROFDROPITEMS you get a random drop item number (let's call it n)
-- use GetDropItems again to get the first drop item, then iterate to the n-th drop item
-*/
+#define MAX_RANDOMSPAWNERS_RECURSION 32 // Should be largely more than enough, honestly.
 static FRandom pr_randomspawn("RandomSpawn");
 
 class ARandomSpawner : public AActor
@@ -32,10 +32,8 @@ class ARandomSpawner : public AActor
 		Super::PostBeginPlay();
 
 		drop = di = GetDropItems(RUNTIME_TYPE(this));
-		// Always make sure it actually exists.
 		if (di != NULL)
 		{
-			// First, we get the size of the array...
 			while (di != NULL)
 			{
 				if (di->Name != NAME_None)
@@ -59,7 +57,9 @@ class ARandomSpawner : public AActor
 				}
 			}
 			// So now we can spawn the dropped item.
-			if (pr_randomspawn() <= di->probability) // prob 255 = always spawn, prob 0 = never spawn.
+			if (special1 >= MAX_RANDOMSPAWNERS_RECURSION)	// Prevents infinite recursions
+				Spawn("Unknown", x, y, z, NO_REPLACE);		// Show that there's a problem.
+			else if (pr_randomspawn() <= di->probability)	// prob 255 = always spawn, prob 0 = never spawn.
 			{
 				newmobj = Spawn(di->Name, x, y, z, ALLOW_REPLACE);
 				// copy everything relevant
@@ -77,11 +77,32 @@ class ARandomSpawner : public AActor
 				newmobj->momx = momx;
 				newmobj->momy = momy;
 				newmobj->momz = momz;
+				newmobj->master = master;	// For things such as DamageMaster/DamageChildren, transfer mastery.
+				newmobj->target = target;
+				newmobj->tracer = tracer;
 				newmobj->CopyFriendliness(this, false);
+				// Special1 is used to count how many recursions we're in.
+				if (newmobj->IsKindOf(PClass::FindClass("RandomSpawner")))
+					newmobj->special1 = ++special1;
+
 			}
 		}
-		Destroy();
+		if ((newmobj != NULL) && ((newmobj->flags4 & MF4_BOSSDEATH) || (newmobj->flags2 & MF2_BOSS)))
+			this->target = newmobj; // If the spawned actor has either of those flags, it's a boss.
+		else Destroy();	// "else" because a boss-replacing spawner must wait until it can call A_BossDeath.
 	}
+
+	void Tick()	// This function is needed for handling boss replacers
+	{
+		Super::Tick();
+		if (target == NULL || target->health <= 0)
+		{
+			health = 0;
+			CALL_ACTION(A_BossDeath, this);
+			Destroy();
+		}
+	}
+
 };
 
 IMPLEMENT_CLASS (ARandomSpawner)
diff --git a/src/info.cpp b/src/info.cpp
index ec854f728d..cd4c294f4e 100644
--- a/src/info.cpp
+++ b/src/info.cpp
@@ -293,6 +293,43 @@ FActorInfo *FActorInfo::GetReplacee ()
 	return rep;
 }
 
+//==========================================================================
+//
+//
+//==========================================================================
+
+void FActorInfo::SetDamageFactor(FName type, fixed_t factor)
+{
+	if (factor != FRACUNIT) 
+	{
+		if (DamageFactors == NULL) DamageFactors=new DmgFactors;
+		DamageFactors->Insert(type, factor);
+	}
+	else 
+	{
+		if (DamageFactors != NULL) 
+			DamageFactors->Remove(type);
+	}
+}
+
+//==========================================================================
+//
+//
+//==========================================================================
+
+void FActorInfo::SetPainChance(FName type, int chance)
+{
+	if (chance >= 0) 
+	{
+		if (PainChances == NULL) PainChances=new PainChanceList;
+		PainChances->Insert(type, MIN(chance, 255));
+	}
+	else 
+	{
+		if (PainChances != NULL) 
+			PainChances->Remove(type);
+	}
+}
 
 //==========================================================================
 //
diff --git a/src/info.h b/src/info.h
index 46a02cd9b9..0f254b1192 100644
--- a/src/info.h
+++ b/src/info.h
@@ -201,6 +201,8 @@ struct FActorInfo
 	void BuildDefaults ();
 	void ApplyDefaults (BYTE *defaults);
 	void RegisterIDs ();
+	void SetDamageFactor(FName type, fixed_t factor);
+	void SetPainChance(FName type, int chance);
 
 	FState *FindState (FName name) const;
 	FState *FindState (int numnames, FName *names, bool exact=false) const;
diff --git a/src/m_options.cpp b/src/m_options.cpp
index 3abb41e17e..b4ba51feea 100644
--- a/src/m_options.cpp
+++ b/src/m_options.cpp
@@ -101,6 +101,7 @@ EXTERN_CVAR (Bool, cl_run)
 EXTERN_CVAR (Int, crosshair)
 EXTERN_CVAR (Bool, freelook)
 EXTERN_CVAR (Int, sv_smartaim)
+EXTERN_CVAR (Int, am_colorset)
 
 static void CalcIndent (menu_t *menu);
 
@@ -544,7 +545,6 @@ static void StartMapColorsMenu (void);
 
 EXTERN_CVAR (Int, am_rotate)
 EXTERN_CVAR (Int, am_overlay)
-EXTERN_CVAR (Bool, am_usecustomcolors)
 EXTERN_CVAR (Bool, am_showitems)
 EXTERN_CVAR (Bool, am_showmonsters)
 EXTERN_CVAR (Bool, am_showsecrets)
@@ -554,8 +554,9 @@ EXTERN_CVAR (Bool, am_showtotaltime)
 EXTERN_CVAR (Bool, am_drawmapback)
 
 static value_t MapColorTypes[] = {
-	{ 1, "Custom" },
-	{ 0, "Traditional Doom" }
+	{ 0, "Custom" },
+	{ 1, "Traditional Doom" },
+	{ 2, "Traditional Strife" }
 };
 
 static value_t SecretTypes[] = {
@@ -577,7 +578,7 @@ static value_t OverlayTypes[] = {
 };
 
 static menuitem_t AutomapItems[] = {
-	{ discrete, "Map color set",		{&am_usecustomcolors},	{2.0}, {0.0},	{0.0}, {MapColorTypes} },
+	{ discrete, "Map color set",		{&am_colorset},			{3.0}, {0.0},	{0.0}, {MapColorTypes} },
 	{ more,		"Set custom colors",	{NULL},					{0.0}, {0.0},	{0.0}, {(value_t*)StartMapColorsMenu} },
 	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
 	{ discrete, "Rotate automap",		{&am_rotate},		   	{3.0}, {0.0},	{0.0}, {RotateTypes} },
diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp
index d60bad3311..2a3b21eb20 100644
--- a/src/thingdef/thingdef_properties.cpp
+++ b/src/thingdef/thingdef_properties.cpp
@@ -413,7 +413,7 @@ static flagdef *FindFlag (const PClass *type, const char *part1, const char *par
 // properties is not recommended
 //
 //===========================================================================
-static void HandleDeprecatedFlags(AActor *defaults, bool set, int index)
+static void HandleDeprecatedFlags(AActor *defaults, FActorInfo *info, bool set, int index)
 {
 	switch (index)
 	{
@@ -436,8 +436,8 @@ static void HandleDeprecatedFlags(AActor *defaults, bool set, int index)
 		defaults->gravity = set? FRACUNIT/4 : FRACUNIT;
 		break;
 	case DEPF_FIRERESIST:
-		if (set) defaults->GetClass()->ActorInfo->DamageFactors->Insert("Fire", 0.5);
-		else defaults->GetClass()->ActorInfo->DamageFactors->Remove("Fire");
+		info->SetDamageFactor(NAME_Fire, set? FRACUNIT/2 : FRACUNIT);
+		break;
 	case DEPF_PICKUPFLASH:
 		if (set)
 		{
@@ -468,22 +468,23 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag)
 
 	const char *dot = strchr (flagname, '.');
 	flagdef *fd;
+	const PClass *cls = self->GetClass();
 
 	if (dot != NULL)
 	{
 		FString part1(flagname, dot-flagname);
-		fd = FindFlag (self->GetClass(), part1, dot+1);
+		fd = FindFlag (cls, part1, dot+1);
 	}
 	else
 	{
-		fd = FindFlag (self->GetClass(), flagname, NULL);
+		fd = FindFlag (cls, flagname, NULL);
 	}
 
 	if (fd != NULL)
 	{
 		if (fd->structoffset == -1)
 		{
-			HandleDeprecatedFlags(self, expression, fd->flagbit);
+			HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit);
 		}
 		else
 		{
@@ -495,7 +496,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag)
 	}
 	else
 	{
-		Printf("Unknown flag '%s' in '%s'\n", flagname, self->GetClass()->TypeName.GetChars());
+		Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars());
 	}
 }
 
@@ -520,7 +521,7 @@ void ParseActorFlag (FScanner &sc, Baggage &bag, int mod)
 		AActor *defaults = (AActor*)bag.Info->Class->Defaults;
 		if (fd->structoffset == -1)	// this is a deprecated flag that has been changed into a real property
 		{
-			HandleDeprecatedFlags(defaults, mod=='+', fd->flagbit);
+			HandleDeprecatedFlags(defaults, bag.Info, mod=='+', fd->flagbit);
 		}
 		else
 		{
@@ -963,9 +964,7 @@ static void ActorPainChance (FScanner &sc, AActor *defaults, Baggage &bag)
 		else painType=sc.String;
 		sc.MustGetToken(',');
 		sc.MustGetNumber();
-	
-		if (bag.Info->PainChances == NULL) bag.Info->PainChances=new PainChanceList;
-		(*bag.Info->PainChances)[painType] = (BYTE)sc.Number;
+		bag.Info->SetPainChance(painType, sc.Number);
 		return;
 	}
 	defaults->PainChance=sc.Number;
@@ -1636,7 +1635,6 @@ static void ActorDamageType (FScanner &sc, AActor *defaults, Baggage &bag)
 static void ActorDamageFactor (FScanner &sc, AActor *defaults, Baggage &bag)
 {
 	sc.MustGetString ();   
-	if (bag.Info->DamageFactors == NULL) bag.Info->DamageFactors=new DmgFactors;
 
 	FName dmgType;
 	if (sc.Compare("Normal")) dmgType = NAME_None;
@@ -1644,7 +1642,7 @@ static void ActorDamageFactor (FScanner &sc, AActor *defaults, Baggage &bag)
 
 	sc.MustGetToken(',');
 	sc.MustGetFloat();
-	(*bag.Info->DamageFactors)[dmgType]=(fixed_t)(sc.Float*FRACUNIT);
+	bag.Info->SetDamageFactor(dmgType, FLOAT2FIXED(sc.Float));
 }
 
 //==========================================================================