static int PatchThing()

in doom_py/src/vizdoom/src/d_dehacked.cpp [762:1258]


static int PatchThing (int thingy)
{
	enum
	{
		// Boom flags
		MF_TRANSLATION	= 0x0c000000,	// if 0x4 0x8 or 0xc, use a translation
		MF_TRANSSHIFT	= 26,			// table for player colormaps
		// A couple of Boom flags that don't exist in ZDoom
		MF_SLIDE		= 0x00002000,	// Player: keep info about sliding along walls.
		MF_TRANSLUCENT	= 0x80000000,	// Translucent sprite?
		// MBF flags: TOUCHY is remapped to flags6, FRIEND is turned into FRIENDLY,
		// and finally BOUNCES is replaced by bouncetypes with the BOUNCES_MBF bit.
		MF_TOUCHY		= 0x10000000,	// killough 11/98: dies when solids touch it
		MF_BOUNCES		= 0x20000000,	// killough 7/11/98: for beta BFG fireballs
		MF_FRIEND		= 0x40000000,	// killough 7/18/98: friendly monsters
	};

	int result;
	AActor *info;
	BYTE dummy[sizeof(AActor)];
	bool hadHeight = false;
	bool hadTranslucency = false;
	bool hadStyle = false;
	FStateDefinitions statedef;
	bool patchedStates = false;
	ActorFlags oldflags;
	const PClass *type;
	SWORD *ednum, dummyed;

	type = NULL;
	info = (AActor *)&dummy;
	ednum = &dummyed;
	if (thingy > (int)InfoNames.Size() || thingy <= 0)
	{
		Printf ("Thing %d out of range.\n", thingy);
	}
	else
	{
		DPrintf ("Thing %d\n", thingy);
		if (thingy > 0)
		{
			type = InfoNames[thingy - 1];
			if (type == NULL)
			{
				info = (AActor *)&dummy;
				ednum = &dummyed;
				// An error for the name has already been printed while loading DEHSUPP.
				Printf ("Could not find thing %d\n", thingy);
			}
			else
			{
				info = GetDefaultByType (type);
				ednum = &type->ActorInfo->DoomEdNum;
			}
		}
	}

	oldflags = info->flags;

	while ((result = GetLine ()) == 1)
	{
		char *endptr;
		unsigned long val = strtoul (Line2, &endptr, 10);
		size_t linelen = strlen (Line1);

		if (linelen == 10 && stricmp (Line1, "Hit points") == 0)
		{
			info->health = val;
		}
		else if (linelen == 13 && stricmp (Line1, "Reaction time") == 0)
		{
			info->reactiontime = val;
		}
		else if (linelen == 11 && stricmp (Line1, "Pain chance") == 0)
		{
			info->PainChance = (SWORD)val;
		}
		else if (linelen == 12 && stricmp (Line1, "Translucency") == 0)
		{
			info->alpha = val;
			info->RenderStyle = STYLE_Translucent;
			hadTranslucency = true;
			hadStyle = true;
		}
		else if (linelen == 6 && stricmp (Line1, "Height") == 0)
		{
			info->height = val;
			info->projectilepassheight = 0;	// needs to be disabled
			hadHeight = true;
		}
		else if (linelen == 14 && stricmp (Line1, "Missile damage") == 0)
		{
			info->Damage = val;
		}
		else if (linelen == 5)
		{
			if (stricmp (Line1, "Speed") == 0)
			{
				info->Speed = val;
			}
			else if (stricmp (Line1, "Width") == 0)
			{
				info->radius = val;
			}
			else if (stricmp (Line1, "Alpha") == 0)
			{
				info->alpha = (fixed_t)(atof (Line2) * FRACUNIT);
				hadTranslucency = true;
			}
			else if (stricmp (Line1, "Scale") == 0)
			{
				info->scaleY = info->scaleX = clamp<fixed_t> (FLOAT2FIXED(atof (Line2)), 1, 256*FRACUNIT);
			}
			else if (stricmp (Line1, "Decal") == 0)
			{
				stripwhite (Line2);
				const FDecalTemplate *decal = DecalLibrary.GetDecalByName (Line2);
				if (decal != NULL)
				{
					info->DecalGenerator = const_cast <FDecalTemplate *>(decal);
				}
				else
				{
					Printf ("Thing %d: Unknown decal %s\n", thingy, Line2);
				}
			}
		}
		else if (linelen == 12 && stricmp (Line1, "Render Style") == 0)
		{
			stripwhite (Line2);
			int style = FindStyle (Line2);
			if (style >= 0)
			{
				info->RenderStyle = ERenderStyle(style);
				hadStyle = true;
			}
		}
		else if (linelen > 6)
		{
			if (linelen == 12 && stricmp (Line1, "No Ice Death") == 0)
			{
				if (val)
				{
					info->flags4 |= MF4_NOICEDEATH;
				}
				else
				{
					info->flags4 &= ~MF4_NOICEDEATH;
				}
			}
			else if (stricmp (Line1 + linelen - 6, " frame") == 0)
			{
				FState *state = FindState (val);

				if (type != NULL && !patchedStates)
				{
					statedef.MakeStateDefines(type);
					patchedStates = true;
				}

				if (!strnicmp (Line1, "Initial", 7))
					statedef.SetStateLabel("Spawn", state ? state : GetDefault<AActor>()->SpawnState);
				else if (!strnicmp (Line1, "First moving", 12))
					statedef.SetStateLabel("See", state);
				else if (!strnicmp (Line1, "Injury", 6))
					statedef.SetStateLabel("Pain", state);
				else if (!strnicmp (Line1, "Close attack", 12))
				{
					if (thingy != 1)	// Not for players!
					{
						statedef.SetStateLabel("Melee", state);
					}
				}
				else if (!strnicmp (Line1, "Far attack", 10))
				{
					if (thingy != 1)	// Not for players!
					{
						statedef.SetStateLabel("Missile", state);
					}
				}
				else if (!strnicmp (Line1, "Death", 5))
					statedef.SetStateLabel("Death", state);
				else if (!strnicmp (Line1, "Exploding", 9))
					statedef.SetStateLabel("XDeath", state);
				else if (!strnicmp (Line1, "Respawn", 7))
					statedef.SetStateLabel("Raise", state);
			}
			else if (stricmp (Line1 + linelen - 6, " sound") == 0)
			{
				FSoundID snd;
				
				if (val == 0 || val >= SoundMap.Size())
				{
					if (endptr == Line2)
					{ // Sound was not a (valid) number,
						// so treat it as an actual sound name.
						stripwhite (Line2);
						snd = Line2;
					}
				}
				else
				{
					snd = SoundMap[val-1];
				}

				if (!strnicmp (Line1, "Alert", 5))
					info->SeeSound = snd;
				else if (!strnicmp (Line1, "Attack", 6))
					info->AttackSound = snd;
				else if (!strnicmp (Line1, "Pain", 4))
					info->PainSound = snd;
				else if (!strnicmp (Line1, "Death", 5))
					info->DeathSound = snd;
				else if (!strnicmp (Line1, "Action", 6))
					info->ActiveSound = snd;
			}
		}
		else if (linelen == 4)
		{
			if (stricmp (Line1, "Mass") == 0)
			{
				info->Mass = val;
			}
			else if (stricmp (Line1, "Bits") == 0)
			{
				DWORD value[4] = { 0, 0, 0 };
				bool vchanged[4] = { false, false, false };
				// ZDoom used to block the upper range of bits to force use of mnemonics for extra flags.
				// MBF also defined extra flags in the same range, but without forcing mnemonics. For MBF
				// compatibility, the upper bits are freed, but we have conflicts between the ZDoom bits
				// and the MBF bits. The only such flag exposed to DEHSUPP, though, is STEALTH -- the others
				// are not available through mnemonics, and aren't available either through their bit value.
				// So if we find the STEALTH keyword, it's a ZDoom mod, otherwise assume FRIEND.
				bool zdoomflags = false;
				char *strval;

				for (strval = Line2; (strval = strtok (strval, ",+| \t\f\r")); strval = NULL)
				{
					if (IsNum (strval))
					{
						// I have no idea why everyone insists on using strtol here even though it fails
						// dismally if a value is parsed where the highest bit it set. Do people really
						// use negative values here? Let's better be safe and check both.
						if (strchr(strval, '-')) value[0] |= (unsigned long)strtol(strval, NULL, 10);
						else value[0] |= (unsigned long)strtoul(strval, NULL, 10);
						vchanged[0] = true;
					}
					else
					{
						// STEALTH FRIEND HACK!
						if (!stricmp(strval, "STEALTH")) zdoomflags = true;
						unsigned i;
						for(i = 0; i < BitNames.Size(); i++)
						{
							if (!stricmp(strval, BitNames[i].Name))
							{
								vchanged[BitNames[i].WhichFlags] = true;
								value[BitNames[i].WhichFlags] |= 1 << BitNames[i].Bit;
								break;
							}
						}
						if (i == BitNames.Size())
						{
							DPrintf("Unknown bit mnemonic %s\n", strval);
						}
					}
				}
				if (vchanged[0])
				{
					if (value[0] & MF_SLIDE)
					{
						// SLIDE (which occupies in Doom what is the MF_INCHASE slot in ZDoom)
						value[0] &= ~MF_SLIDE; // clean the slot
						// Nothing else to do, this flag is never actually used.
					}
					if (value[0] & MF_TRANSLATION)
					{
						info->Translation = TRANSLATION (TRANSLATION_Standard,
							((value[0] & MF_TRANSLATION) >> (MF_TRANSSHIFT))-1);
						value[0] &= ~MF_TRANSLATION;
					}
					if (value[0] & MF_TOUCHY)
					{
						// TOUCHY (which occupies in MBF what is the MF_UNMORPHED slot in ZDoom)
						value[0] &= ~MF_TOUCHY; // clean the slot
						info->flags6 |= MF6_TOUCHY; // remap the flag
					}
					if (value[0] & MF_BOUNCES)
					{
						// BOUNCES (which occupies in MBF the MF_NOLIFTDROP slot)
						// This flag is especially convoluted as what it does depend on what
						// other flags the actor also has, and whether it's "sentient" or not.
						value[0] &= ~MF_BOUNCES; // clean the slot

						// MBF considers that things that bounce can be damaged, even if not shootable.
						info->flags6 |= MF6_VULNERABLE;
						// MBF also considers that bouncers pass through blocking lines as projectiles.
						info->flags3 |= MF3_NOBLOCKMONST;
						// MBF also considers that bouncers that explode are grenades, and MBF grenades
						// are supposed to hurt everything, except cyberdemons if they're fired by cybies.
						// Let's translate that in a more generic way as grenades which hurt everything
						// except the class of their shooter. Yes, it does diverge a bit from MBF, as for
						// example a dehacked arachnotron that shoots grenade would kill itself quickly
						// in MBF and will not here. But class-specific checks are cumbersome and limiting.
						info->flags4 |= (MF4_FORCERADIUSDMG | MF4_DONTHARMCLASS);

						// MBF bouncing missiles rebound on floors and ceiling, but not on walls.
						// This is different from BOUNCE_Heretic behavior as in Heretic the missiles
						// die when they bounce, while in MBF they will continue to bounce until they
						// collide with a wall or a solid actor.
						if (value[0] & MF_MISSILE) info->BounceFlags = BOUNCE_Classic;
						// MBF bouncing actors that do not have the missile flag will also rebound on
						// walls, and this does correspond roughly to the ZDoom bounce style.
						else info->BounceFlags = BOUNCE_Grenade;

						// MBF grenades are dehacked rockets that gain the BOUNCES flag but
						// lose the MISSILE flag, so they can be identified here easily.
						if (!(value[0] & MF_MISSILE) && info->effects & FX_ROCKET)
						{
							info->effects &= ~FX_ROCKET;	// replace rocket trail
							info->effects |= FX_GRENADE;	// by grenade trail
						}

						// MBF bounce factors depend on flag combos:
						enum
						{
							MBF_BOUNCE_NOGRAVITY	= FRACUNIT,				// With NOGRAVITY: full momentum
							MBF_BOUNCE_FLOATDROPOFF	= (FRACUNIT * 85) / 100,// With FLOAT and DROPOFF: 85%
							MBF_BOUNCE_FLOAT		= (FRACUNIT * 70) / 100,// With FLOAT alone: 70%
							MBF_BOUNCE_DEFAULT		= (FRACUNIT * 45) / 100,// Without the above flags: 45%
							MBF_BOUNCE_WALL			= (FRACUNIT * 50) / 100,// Bouncing off walls: 50%
						};
						info->bouncefactor = ((value[0] & MF_NOGRAVITY) ? MBF_BOUNCE_NOGRAVITY
							: (value[0] & MF_FLOAT) ? (value[0] & MF_DROPOFF) ? MBF_BOUNCE_FLOATDROPOFF
							: MBF_BOUNCE_FLOAT : MBF_BOUNCE_DEFAULT);

						info->wallbouncefactor = ((value[0] & MF_NOGRAVITY) ? MBF_BOUNCE_NOGRAVITY : MBF_BOUNCE_WALL);

						// MBF sentient actors with BOUNCE and FLOAT are able to "jump" by floating up.
						if (info->IsSentient())
						{
							if (value[0] & MF_FLOAT) info->flags6 |= MF6_CANJUMP;
						}
						// Non sentient actors can be damaged but they shouldn't bleed.
						else
						{
							value[0] |= MF_NOBLOOD;
						}
					}
					if (zdoomflags && (value [0] & MF_STEALTH))
					{
						// STEALTH FRIEND HACK!
					}
					else if (value[0] & MF_FRIEND) 
					{
						// FRIEND (which occupies in MBF the MF_STEALTH slot)
						value[0] &= ~MF_FRIEND; // clean the slot
						value[0] |= MF_FRIENDLY; // remap the flag to its ZDoom equivalent
						// MBF friends are not blocked by monster blocking lines:
						info->flags3 |= MF3_NOBLOCKMONST;
					}
					if (value[0] & MF_TRANSLUCENT)
					{
						// TRANSLUCENT (which occupies in Boom the MF_ICECORPSE slot)
						value[0] &= ~MF_TRANSLUCENT; // clean the slot
						vchanged[2] = true; value[2] |= 2; // let the TRANSLUCxx code below handle it
					}
					if ((info->flags & MF_MISSILE) && (info->flags2 & MF2_NOTELEPORT)
						&& !(value[0] & MF_MISSILE))
					{
						// ZDoom gives missiles flags that did not exist in Doom: MF2_NOTELEPORT, 
						// MF2_IMPACT, and MF2_PCROSS. The NOTELEPORT one can be a problem since 
						// some projectile actors (those new to Doom II) were not excluded from 
						// triggering line effects and can teleport when the missile flag is removed.
						info->flags2 &= ~MF2_NOTELEPORT;
					}
					info->flags = ActorFlags::FromInt (value[0]);
				}
				if (vchanged[1])
				{
					if (value[1] & 0x00000004)	// old BOUNCE1
					{ 	
						value[1] &= ~0x00000004;
						info->BounceFlags = BOUNCE_DoomCompat;
					}
					// Damage types that once were flags but now are not
					if (value[1] & 0x20000000)
					{
						info->DamageType = NAME_Ice;
						value[1] &= ~0x20000000;
					}
					if (value[1] & 0x10000000)
					{
						info->DamageType = NAME_Fire;
						value[1] &= ~0x10000000;
					}
					if (value[1] & 0x00000001)
					{
						info->gravity = FRACUNIT/4;
						value[1] &= ~0x00000001;
					}
					info->flags2 = ActorFlags2::FromInt (value[1]);
				}
				if (vchanged[2])
				{
					if (value[2] & 7)
					{
						hadTranslucency = true;
						if (value[2] & 1)
							info->alpha = TRANSLUC25;
						else if (value[2] & 2)
							info->alpha = TRANSLUC50;
						else if (value[2] & 4)
							info->alpha = TRANSLUC75;
						info->RenderStyle = STYLE_Translucent;
					}
					if (value[2] & 8)
						info->renderflags |= RF_INVISIBLE;
					else
						info->renderflags &= ~RF_INVISIBLE;
				}
				DPrintf ("Bits: %d,%d (0x%08x,0x%08x)\n", info->flags.GetValue(), info->flags2.GetValue(),
													      info->flags.GetValue(), info->flags2.GetValue());
			}
			else if (stricmp (Line1, "ID #") == 0)
			{
				*ednum = (SWORD)val;
			}
		}
		else Printf (unknown_str, Line1, "Thing", thingy);
	}

	if (info != (AActor *)&dummy)
	{
		// Reset heights for things hanging from the ceiling that
		// don't specify a new height.
		if (info->flags & MF_SPAWNCEILING &&
			!hadHeight &&
			thingy <= (int)OrgHeights.Size() && thingy > 0)
		{
			info->height = OrgHeights[thingy - 1] * FRACUNIT;
			info->projectilepassheight = 0;
		}
		// If the thing's shadow changed, change its fuzziness if not already specified
		if ((info->flags ^ oldflags) & MF_SHADOW)
		{
			if (info->flags & MF_SHADOW)
			{ // changed to shadow
				if (!hadStyle)
					info->RenderStyle = STYLE_OptFuzzy;
				if (!hadTranslucency)
					info->alpha = FRACUNIT/5;
			}
			else
			{ // changed from shadow
				if (!hadStyle)
					info->RenderStyle = STYLE_Normal;
			}
		}
		// If this thing's speed is really low (i.e. meant to be a monster),
		// bump it up, because all speeds are fixed point now.
		if (abs(info->Speed) < 256)
		{
			info->Speed <<= FRACBITS;
		}

		if (info->flags & MF_SPECIAL)
		{
			PushTouchedActor(const_cast<PClass *>(type));
		}

		// If MF_COUNTKILL is set, make sure the other standard monster flags are
		// set, too. And vice versa.
		if (thingy != 1) // don't mess with the player's flags
		{
			if (info->flags & MF_COUNTKILL)
			{
				info->flags2 |= MF2_PUSHWALL | MF2_MCROSS | MF2_PASSMOBJ;
				info->flags3 |= MF3_ISMONSTER;
			}
			else
			{
				info->flags2 &= ~(MF2_PUSHWALL | MF2_MCROSS);
				info->flags3 &= ~MF3_ISMONSTER;
			}
		}
		// Everything that's altered here gets the CANUSEWALLS flag, just in case
		// it calls P_Move().
		info->flags4 |= MF4_CANUSEWALLS;
		if (patchedStates)
		{
			statedef.InstallStates(type->ActorInfo, info);
		}
	}

	return result;
}