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;
}