MBF21 Developer Spec v1.4

October 20, 2021 ยท View on GitHub

The contents of this file are aimed at developers, with links to relevant code and extra technical details. MBF21 supports the full spec of boom & mbf, plus the following.

Sectors

Instant death sector special

  • Implementation
  • Bit 12 (4096) turns on "alternate damage meaning" for bit 5 & 6:
DecBit 6-5Description
000Kills a player unless they have a rad suit or invulnerability
3201Kills a player
6410Kills all players and exits the map (normal exit)
9611Kills all players and exits the map (secret exit)

Kill monsters sector special

  • PR
  • Bit 13 turns on "kill monsters" flag for sectors - kills grounded monsters.

Lines

Block land monsters line flag

  • PR
  • Uses bit 12 (4096).

Block players line flag

Line scroll special variants

  • PR
  • Scroll like special 255, but also apply the scroll to all other linedefs which share the same tag.
  • The scrolling speed is divided by 8 to give more fine-grained control.
  • 1024 is without control sector / acceleration.
  • 1025 uses control sector.
  • 1026 uses control sector + acceleration.

Things

Dehacked Thing Groups

Infighting
  • Add Infighting group = N in Thing definition.
  • N is a nonnegative integer.
  • Things with the same value of N will not target each other after taking damage.
Projectile
  • Add Projectile group = M in Thing definition.
  • M is an integer.
  • Things with the same value of M will not deal projectile damage to each other.
  • A negative value of M means that species has no projectile immunity, even to other things in the same species.
Splash
  • Add Splash group = S in Thing definition.
  • S is a nonnegative integer.
  • If an explosion (or other splash damage source) has the same splash group as the target, it will not deal damage.
Examples
Thing 12 (Imp)
Projectile group = -1
Splash group = 0

Thing 16 (Baron of Hell)
Projectile group = 2

Thing 18 (Hell Knight)
Infighting group = 1
Projectile group = 1

Thing 21 (Arachnotron)
Infighting group = 1
Projectile group = 2

Thing 31 (Barrel)
Splash group = 0

In this example:

  • Imp projectiles now damage other Imps (and they will infight with their own kind).
  • Barons and Arachnotrons are in the same projectile group: their projectiles will no longer damage each other.
  • Barons and Hell Knights are not in the same projectile group: their projectiles will now damage each other, leading to infighting.
  • Hell Knights and Arachnotrons are in the same infighting group: they will not infight with each other, despite taking damage from each other's projectiles.
  • Imps and Barrels are in the same splash group: barrels no longer deal splash damage to imps.
  • Note that the group numbers are separate - being in infighting group 1 doesn't mean you are in projectile group 1.

New Thing Flags

  • commit
  • Add MBF21 Bits = X in the Thing definition.
  • The format is the same as the existing Bits field.
  • Example: MBF21 Bits = LOGRAV+DMGIGNORED+MAP07BOSS1.
  • The value can also be given as a number (sum of the individual flag values below).
  • Implementations match between DSDA-Doom and Eternity Engine for labeled flags.
DSDA-DoomEternity EngineValueDescription
MF2_LOGRAVMF2_LOGRAV0x00001Lower gravity (1/8)
MF2_SHORTMRANGEMF2_SHORTMRANGE0x00002Short missile range (archvile)
MF2_DMGIGNOREDMF3_DMGIGNORED0x00004Other things ignore its attacks (archvile)
MF2_NORADIUSDMGMF4_NORADIUSDMG0x00008Doesn't take splash damage (cyberdemon, mastermind)
MF2_FORCERADIUSDMGMF4_FORCERADIUSDMG0x00010Thing causes splash damage even if the target shouldn't
MF2_HIGHERMPROBMF2_HIGHERMPROB0x00020Higher missile attack probability (cyberdemon)
MF2_RANGEHALFMF2_RANGEHALF0x00040Use half distance for missile attack probability (cyberdemon, mastermind, revenant, lost soul)
MF2_NOTHRESHOLDMF3_NOTHRESHOLD0x00080Has no targeting threshold (archvile)
MF2_LONGMELEEMF2_LONGMELEE0x00100Has long melee range (revenant)
MF2_BOSSMF2_BOSS0x00200Full volume see / death sound & splash immunity (from heretic)
MF2_MAP07BOSS1MF2_MAP07BOSS10x00400Tag 666 "boss" on doom 2 map 7 (mancubus)
MF2_MAP07BOSS2MF2_MAP07BOSS20x00800Tag 667 "boss" on doom 2 map 7 (arachnotron)
MF2_E1M8BOSSMF2_E1M8BOSS0x01000E1M8 boss (baron)
MF2_E2M8BOSSMF2_E2M8BOSS0x02000E2M8 boss (cyberdemon)
MF2_E3M8BOSSMF2_E3M8BOSS0x04000E3M8 boss (mastermind)
MF2_E4M6BOSSMF2_E4M6BOSS0x08000E4M6 boss (cyberdemon)
MF2_E4M8BOSSMF2_E4M8BOSS0x10000E4M8 boss (mastermind)
MF2_RIPMF3_RIP0x20000Ripper projectile (does not disappear on impact)
MF2_FULLVOLSOUNDS0x40000Full volume see / death sounds (cyberdemon, mastermind)

Rip sound

  • commit
  • When set, this is the sound that plays for ripper projectiles when they rip through something.
  • Add Rip sound = X in the Thing definition.
  • X is the sound index, as seen in other sound fields.

Fast speed

  • PR, commit
  • Sets the thing speed for skill 5 / -fast.
  • Add Fast speed = X in the Thing definition.
  • X has the same units as the normal Speed field.

Melee range

  • PR
  • Sets the range at which a monster will initiate a melee attack.
  • Also affects the range for vanilla melee attack codepointers, e.g. A_SargAttack, A_TroopAttack, etc.
    • Similarly, adjusting the player mobj's melee range will adjust the range of A_Punch and A_Saw
  • Add Melee range = X in the Thing definition.
  • X is in fixed point, like the Radius and Height fields

Weapons

Weapon Flags

  • PR
  • Add MBF21 Bits = X in the Weapon definition.
  • The format is the same as the existing thing Bits field.
  • Example: MBF21 Bits = SILENT+NOAUTOFIRE.
  • The value can also be given as a number (sum of the individual flag values below).
NameValueDescription
NOTHRUST0x001Doesn't thrust things
SILENT0x002Weapon is silent
NOAUTOFIRE0x004Weapon won't autofire when swapped to
FLEEMELEE0x008Monsters consider it a melee weapon
AUTOSWITCHFROM0x010Can be switched away from when ammo is picked up
NOAUTOSWITCHTO0x020Cannot be switched to when ammo is picked up

MBF21 defaults:

WeaponFlags
FistFLEEMELEE+AUTOSWITCHFROM+NOAUTOSWITCHTO
PistolAUTOSWITCHFROM
Shotgun
Chaingun
Rocket LauncherNOAUTOFIRE
Plasma Rifle
BFGNOAUTOFIRE
ChainsawNOTHRUST+FLEEMELEE+NOAUTOSWITCHTO
Super Shotgun

Ammo pickup weapon autoswitch changes

  • PR
  • Weapon autoswitch on ammo pickup now accounts for the ammo per shot of a weapon, as well as the NOAUTOSWITCHTO and AUTOSWITCHFROM weapon flags, allowing more accuracy and customization of this behaviour.
  • If the current weapon is enabled for AUTOSWITCHFROM and the player picks up ammo for a different weapon, autoswitch will occur for the highest ranking weapon (by index) matching these conditions:
    • player has the weapon
    • weapon is not flagged with NOAUTOSWITCHTO
    • weapon uses the ammo that was picked up
    • player did not have enough ammo to fire the weapon before
    • player now has enough ammo to fire the weapon

New DEHACKED "Ammo per shot" Weapon field

  • PR
  • Add Ammo per shot = X in the Weapon definition.
  • Value must be a nonnegative integer.
  • Tools should assume this value is undefined for all vanilla weapons (i.e. always write it to the patch if the user specifies any valid value)
  • Weapons WITH this field set will use the ammo-per-shot value when:
    • Checking if there is enough ammo before firing
    • Determining if the weapon has ammo during weapon auto-switch
    • Deciding how much ammo to subtract in native Doom weapon attack pointers
      • Exceptions: A_Saw and A_Punch will never attempt to subtract ammo.
    • The amount param is zero for certain new MBF21 DEHACKED codepointers (see below).
  • Weapons WITHOUT this field set will use vanilla Doom semantics for all above behaviors.
  • For backwards-compatibility, setting the BFG cells/shot misc field will also set the BFG weapon's Ammo per shot field (but not vice-versa).

Frames

Frame Flags

  • PR
  • Add MBF21 Bits = X in the Frame definition.
  • The format is the same as the existing thing Bits field.
  • Example: MBF21 Bits = SKILL5FAST.
  • The value can also be given as a number (sum of the individual flag values below).
NameValueDescription
SKILL5FAST0x001Tics halve on nightmare skill (demon)

New "Args" fields for DEHACKED states

  • PR
  • Defines 8 new integer fields in the state table for use as codepointer arguments
  • Args are defined in dehacked by adding Args1 = X, Args2 = X... up to Args8 = X in the State definition.
  • Default value for each arg is determined by the frame's codepointer. For ease of implementation, DEHACKED tools may wish to simply pass through any args that are explicitly set by the user (i.e. assume the default value is undefined).
  • For future-proofing, if args are defined on a state than its action pointer expects (e.g. defining Args3 on a state that uses A_WeaponSound), an error must be thrown on startup.

New DEHACKED Codepointers

  • PR, PR, PR, PR, PR, PR
  • All new MBF21 pointers use the new "Args" fields for params, rather than misc1/misc2 fields
  • Arg fields are listed in order in the docs below, e.g. for A_SpawnObject, type is Args1, angle is Args2, etc.
  • Although all args are integers internally, there are effectively the following types of args:
    • int: An integer. 'Nuff said.
    • uint: An unsigned integer. Must be greater than or equal to zero.
    • fixed A fixed-point number.
      • For user-friendliness, a suggested convention for MBF21-supporting DEHACKED tools is to convert numbers with decimal points to their fixed-point representation when saving the patch (e.g. "1.0" gets saved as "65536"), so this doesn't get super-ugly on the user side of life.
    • The reference implementation of these codepointers in dsda-doom is designed to be as easy to implement as possible for Boom-derived sourceports. Though some modification of internal functions is needed for certain codepointers, the changes are minimally invasive and should not require mounds of refactoring.
Actor pointers
  • A_SpawnObject(type, angle, x_ofs, y_ofs, z_ofs, x_vel, y_vel, z_vel)

    • Generic actor spawn function.
    • Args:
      • type (uint): Type (dehnum) of actor to spawn
      • angle (fixed): Angle (degrees), relative to calling actor's angle
      • x_ofs (fixed): X (forward/back) spawn position offset
      • y_ofs (fixed): Y (left/right) spawn position offset
      • z_ofs (fixed): Z (up/down) spawn position offset
      • x_vel (fixed): X (forward/back) velocity
      • y_vel (fixed): Y (left/right) velocity
      • z_vel (fixed): Z (up/down) velocity
    • Notes:
      • If the spawnee is a missile, the tracer and target pointers are set as follows:
        • If spawner is also a missile, tracer and target are copied to spawnee
        • Otherwise, spawnee's target is set to the spawner and tracer is set to the spawner's target.
  • A_MonsterProjectile(type, angle, pitch, hoffset, voffset)

    • Generic monster projectile attack.
    • Args:
      • type (uint): Type (dehnum) of actor to spawn
      • angle (fixed): Angle (degrees), relative to calling actor's angle
      • pitch (fixed): Pitch (degrees), relative to calling actor's pitch
      • hoffset (fixed): Horizontal spawn offset, relative to calling actor's angle
      • voffset (fixed): Vertical spawn offset, relative to actor's default projectile fire height
    • Notes:
      • The spawned projectile's tracer pointer is always set to the spawner's target, for generic seeker missile support.
      • For demo-compatible ports, the pitch arg must use the same approximated slope calculation that Doom's monster aim / autoaim uses (finetangent table) & only adjust the projectile's z-velocity, rather than calculate true 3D pitch.
  • A_MonsterBulletAttack(hspread, vspread, numbullets, damagebase, damagedice)

    • Generic monster bullet attack.
    • Args:
      • hspread (fixed): Horizontal spread (degrees, in fixed point)
      • vspread (fixed): Vertical spread (degrees, in fixed point)
      • numbullets (uint): Number of bullets to fire; if not set, defaults to 1
      • damagebase (uint): Base damage of attack; if not set, defaults to 3
      • damagedice (uint): Attack damage random multiplier; if not set, defaults to 5
    • Notes:
      • Damage formula is: damage = (damagebase * random(1, damagedice))
      • Damage arg defaults are identical to Doom's monster bullet attack damage values.
      • For demo-compatible ports, the vspread arg must use the same approximated slope calculation that Doom's monster aim / autoaim uses (finetangent table), rather than calculate true 3D pitch.
  • A_MonsterMeleeAttack(damagebase, damagedice, sound, range)

    • Generic monster melee attack.
    • Args:
      • damagebase (uint): Base damage of attack; if not set, defaults to 3
      • damagedice (uint): Attack damage random multiplier; if not set, defaults to 8
      • sound (uint): Sound to play if attack hits
      • range (fixed): Attack range; if not set, defaults to calling actor's melee range property
    • Notes:
      • Damage formula is: damage = (damagebase * random(1, damagedice))
  • A_RadiusDamage(damage, radius)

    • Generic explosion.
    • Args:
      • damage (uint): Max explosion damge
      • radius (uint): Explosion radius, in map units
  • A_NoiseAlert

    • Alerts monsters within sound-travel distance of the calling actor's target.
    • No Args.
  • A_HealChase(state, sound)

    • Generic A_VileChase. Chases the player and resurrects any corpses with a Raise state when bumping into them.
    • Args:
      • state (uint): State to jump to on the calling actor when resurrecting a corpse
      • sound (uint): Sound to play when resurrecting a corpse
  • A_SeekTracer(threshold, maxturnangle)

    • Generic seeker missile function.
    • Args:
      • threshold (fixed): If angle to target is lower than this, missile will 'snap' directly to face the target
      • maxturnangle (fixed): Maximum angle a missile will turn towards the target if angle is above the threshold
    • Notes:
      • When using this function, keep in mind that seek 'strength' also depends on how often this function is called -- e.g. calling A_SeekTracer every tic will result in a much more aggressive seek than calling it every 4 tics, even if the args are the same
      • This function uses Heretic's seeker missile logic (P_SeekerMissile), rather than Doom's, with the following changes:
        • The actor's tracer pointer is used as the seek target, rather than Heretic's special1 field
        • On the z-axis, the missile will seek towards the vertical centerpoint of the seek target, rather than the bottom (resulting in much friendly behavior when seeking toward enemies on high ledges).
        • Z-axis seeking will always be attempted, rather than use Heretic's behavior of skipping the Z-axis adjustment if the missile's z position falls between the actor's top/bottom z positions.
  • A_FindTracer(fov, rangeblocks)

    • Searches for a valid tracer (seek target), if the calling actor doesn't already have one. Particularly useful for player missiles.
    • Args:
      • fov (fixed): Field-of-view, relative to calling actor's angle, to search for targets in. If zero, the search will occur in all directions.
      • rangeblocks (uint): Distance to search, in map blocks (128 units); if not set, defaults to 10.
    • Notes:
      • This function will no-op if the calling actor already has a tracer. To forcibly re-acquire a new tracer, call A_ClearTracer first.
      • This function uses a variant of Hexen's blockmap search algorithm (P_RoughMonsterSearch), with the rules for picking a valid target defined as follows (in order of evaluation):
        • Actors without the SHOOTABLE flag are skipped
        • The projectile's owner (target) is skipped
        • Actors on the same "team" are skipped (e.g. MF_FRIEND actors will not pick players or fellow MF_FRIENDs), with a few exceptions:
          • If actors are infighting (i.e. candidate actor is projectile owner's target), actor will not be skipped
          • Players will not be skipped in deathmatch
            • This rule is to work around a weird quirk in MBF (namely, that players have MF_FRIEND set even in DM); ports with a robust "team" implementation may wish to adjust or append to this logic to take player team into account.
        • If the fov arg is nonzero, actors outside of an fov-degree cone, relative to the missile's angle, are skipped
        • Actors not in line-of-sight of the missile are skipped
  • A_ClearTracer

    • Clears the calling actor's tracer (seek target) field.
    • No Args.
  • A_JumpIfHealthBelow(state, health)

    • Jumps to a state if caller's health is below the specified threshold.
    • Args:
      • state (uint): State to jump to
      • health (int): Health to check
    • Notes:
      • The jump will only occur if health is BELOW the given value -- e.g. A_JumpIfHealthBelow(420, 69) will jump to state 420 if health is 68 or lower.
  • A_JumpIfTargetInSight(state)

    • Jumps to a state if caller's target is in line-of-sight.
    • Args:
      • state (uint): State to jump to
      • fov (fixed): Field-of-view, relative to calling actor's angle, to check for target in. If zero, the check will occur in all directions.
  • A_JumpIfTargetCloser(state, distance)

    • Jumps to a state if caller's target is closer than the specified distance.
    • Args:
      • state (uint): State to jump to
      • distance (fixed): Distance threshold, in map units
    • Notes:
      • For demo-compatible ports, this function must the same approximate distance check as other functions in the game (P_AproxDistance).
      • The jump will only occur if distance is BELOW the given value -- e.g. A_JumpIfTargetCloser(420, 69.0) will jump to state 420 if distance is 68.0 or lower.
  • A_JumpIfTracerInSight(state)

    • Jumps to a state if caller's tracer (seek target) is in line-of-sight.
    • Args:
      • state (uint): State to jump to
      • fov (fixed): Field-of-view, relative to calling actor's angle, to check for tracer in. If zero, the check will occur in all directions.
  • A_JumpIfTracerCloser(state, distance)

    • Jumps to a state if caller's tracer (seek target) is closer than the specified distance.
    • Args:
      • state (uint): State to jump to
      • distance (fixed): Distance threshold, in map units
    • Notes:
      • For demo-compatible ports, this function must the same approximate distance check as other functions in the game (P_AproxDistance).
      • The jump will only occur if distance is BELOW the given value -- e.g. A_JumpIfTracerCloser(420, 69.0) will jump to state 420 if distance is 68.0 or lower.
  • A_JumpIfFlagsSet(state, flags, flags2)

    • Jumps to a state if caller has the specified thing flags set.
    • Args:
      • state (uint): State to jump to.
      • flags (int): Standard actor flag(s) to check
      • flags2 (int): MBF21 actor flag(s) to check
    • Notes:
      • If multiple flags are specified in a field, jump will only occur if all the flags are set (e.g. AND comparison, not OR)
  • A_AddFlags(flags, flags2)

    • Adds the specified thing flags to the caller.
    • Args:
      • flags (int): Standard actor flag(s) to add
      • flags2 (int): MBF21 actor flag(s) to add
  • A_RemoveFlags(flags, flags2)

    • Removes the specified thing flags from the caller.
    • Args:
      • flags (int): Standard actor flag(s) to remove
      • flags2 (int): MBF21 actor flag(s) to remove
Weapon pointers
  • A_WeaponProjectile(type, angle, pitch, hoffset, voffset)

    • Generic weapon projectile attack.
    • Args:
      • type (uint): Type (dehnum) of actor to spawn
      • angle (fixed): Angle (degrees), relative to player's angle
      • pitch (fixed): Pitch (degrees), relative to player's pitch
      • hoffset (fixed): Horizontal spawn offset, relative to player's angle
      • voffset (fixed): Vertical spawn offset, relative to player's default projectile fire height
    • Notes:
      • Unlike native Doom attack codepointers, this function will not consume ammo, trigger the Flash state, or play a sound.
      • The spawned projectile's tracer pointer is set to the player's autoaim target, if available.
      • For demo-compatible ports, the pitch arg must use the same approximated slope calculation that Doom's monster aim / autoaim uses (finetangent table) & only adjust the projectile's z-velocity, rather than calculate true 3D pitch.
  • A_WeaponBulletAttack(hspread, vspread, numbullets, damagebase, damagedice)

    • Generic weapon bullet attack.
    • Args:
      • hspread (fixed): Horizontal spread (degrees, in fixed point)
      • vspread (fixed): Vertical spread (degrees, in fixed point)
      • numbullets (uint): Number of bullets to fire; if not set, defaults to 1
      • damagebase (uint): Base damage of attack; if not set, defaults to 5
      • damagedice (uint): Attack damage random multiplier; if not set, defaults to 3
    • Notes:
      • Unlike native Doom attack codepointers, this function will not consume ammo, trigger the Flash state, or play a sound.
      • Damage formula is: damage = (damagebase * random(1, damagedice))
      • Damage arg defaults are identical to Doom's weapon bullet attack damage values.
        • Note that these defaults are intentionally different for weapons vs monsters (5d3 vs 3d5) -- yup, Doom did it this way. :P
      • For demo-compatible ports, the vspread arg must use the same approximated slope calculation that Doom's monster aim / autoaim uses (finetangent table), rather than calculate true 3D pitch.
  • A_WeaponMeleeAttack(damagebase, damagedice, zerkfactor, sound, range)

    • Generic weapon melee attack.
    • Args:
      • damagebase (uint): Base damage of attack; if not set, defaults to 2
      • damagedice (uint): Attack damage random multiplier; if not set, defaults to 10
      • zerkfactor (fixed): Berserk damage multiplier; if not set, defaults to 1.0
      • sound (uint): Sound index to play if attack hits
      • range (fixed): Attack range; if not set, defaults to player mobj's melee range property
    • Notes:
      • Damage formula is: damage = (damagebase * random(1, damagedice)); this is then multiplied by zerkfactor if the player has Berserk.
  • A_WeaponSound(sound, fullvol)

    • Generic playsound for weapons.
    • Args:
      • sound (uint): Sound index to play.
      • fullvol (int): If nonzero, play this sound at full volume across the entire map.
  • A_WeaponJump(state, chance)

    • Random state jump for weapons.
    • Args:
      • state (uint): State index to jump to.
      • chance (int): Chance out of 256 to perform the jump. 0 (or below) never jumps, 256 (or higher) always jumps.
  • A_ConsumeAmmo(amount)

    • Subtracts ammo from the currently-selected weapon's ammo pool.
    • Args:
      • amount (int): Amount of ammo to subtract. If zero, will default to the current weapon's ammopershot value.
    • Notes:
      • This function will not reduce ammo below zero.
      • This function will no-op if the current weapon uses the am_noammo ("None"/"Infinite") ammotype.
      • If amount is negative, ammo will be added to the ammo pool instead.
  • A_CheckAmmo(state, amount)

    • Jumps to state if ammo is below amount.
    • Args:
      • state (uint): State index to jump to.
      • amount (uint): Amount of ammo to check. If zero, will default to the current weapon's ammopershot value.
    • Notes:
      • The jump will only occur if ammo is BELOW the given value -- e.g. A_CheckAmmo(420, 69) will jump to state 420 if ammo is 68 or lower.
      • This function will no-op if the current weapon uses the am_noammo ("None"/"Infinite") ammotype.
  • A_RefireTo(state, noammocheck)

    • Jumps to state if the fire button is currently being pressed and the weapon has enough ammo to fire.
    • Args:
      • state (uint): State index to jump to.
      • noammocheck (int): If nonzero, skip the ammo check.
  • A_GunFlashTo(state, nothirdperson)

    • Generic weapon muzzle flash.
    • Args:
      • state (uint): State index to set the flash psprite to.
      • nothirdperson (int): If nonzero, do not change the 3rd-person player sprite to the player muzzleflash state.
  • A_WeaponAlert

    • Alerts monsters within sound-travel distance of the player's presence. Useful for weapons with the WPF_SILENT flag set.
    • No Args.

Miscellaneous

New comp flags

  • comp_ledgeblock: commit
    • Ledges block ground enemies
    • Exception: movement due to scrolling / pushers / pullers disables comp_ledgeblock for the next xy movement: commit
  • comp_friendlyspawn: PR
    • When on: A_Spawn new thing inherits friend flag from source thing.
    • When off: A_Spawn new thing keeps its default friend flag.
  • comp_voodooscroller: PR
    • When on: voodoo dolls on slow scrollers move too slowly
    • When off: voodoo dolls move the same speed as the floor
  • comp_reservedlineflag: commit, commit
    • When on: the line flag 0x0800 clears extended flags (flags &= 0x01ff).
    • When off: the line flag 0x0800 means nothing.

Summary of comp flags since mbf in pr+ and changes:

NameIndexDefaultDescription
comp_moveblock-190Large negative displacements are mishandled
comp_respawn*200Creatures with no spawnpoint respawn at (0,0)
comp_sound-210Assorted sound errors
comp_666-220Buggy pre-udoom boss checks
comp_soul*230Lost souls do not bounce
comp_maskedanim-240Two-sided midtextures don't animate
comp_ouchface-250Buggy vanilla ouchface code
comp_maxhealth-260Max health in deh only applies to potions
comp_translucency-270Disable some predefined translucency
comp_ledgeblock281Ledges block ground enemies
comp_friendlyspawn291A_Spawn new thing inherits friendliness
comp_voodooscroller300Voodoo dolls on slow scrollers move too slowly
comp_reservedlineflag311Line flag 0x0800 clears extended flags
  • Comp options marked with a - have been deoptionalized in mbf21 (forced to 0). Many of these have nothing to do with demo compatibility - others are simple bug fixes.
  • Comp options marked with a * are already implemented in EE.

Option default changes

  • comp_pursuit: 1 (was 0)

Demo format / header

  • MBF21 occupies complevel 21.
  • Demo version is 221.
  • longtics are enabled while recording.
  • Old option entries removed:
    • variable_friction (always 1)
    • allow_pushers (always 1)
    • demo_insurance (always 0)
    • obsolete / empty bytes
  • New option entries:
    • New comp options from above
    • Size of comp option list
      • This could be used to add comp options later without adding a complevel
      • When reading a demo that has a size larger than what you expect, abort
  • Header options block:
KeyBytes
monsters_remember1
weapon_recoil1
player_bobbing1
respawnparm1
fastparm1
nomonsters1
rngseed4
monster_infighting1
dogs1
distfriend2
monster_backing1
monster_avoid_hazards1
monster_friction1
help_friends1
dog_jumping1
monkeys1
comp list size (23+)1
comp_telefrag1
comp_dropoff1
comp_vile1
comp_pain1
comp_skull1
comp_blazing1
comp_doorlight1
comp_model1
comp_god1
comp_falloff1
comp_floors1
comp_skymap1
comp_pursuit1
comp_doorstuck1
comp_staylift1
comp_zombie1
comp_stairs1
comp_infcheat1
comp_zerotags1
comp_respawn1
comp_soul1
comp_ledgeblock1
comp_friendlyspawn1
comp_voodooscroller1
comp_reservedlineflag1
  • The comp list size is variable - take care when reading demos.
    • If the comp list size is <24, then comp_voodooscroller equals 1.
    • If the comp list size is <25, then comp_reservedlineflag equals 0.

Fixes / adjustments since mbf

  • Fix 3 key door bug
    • Already fixed in pr+ / EE.
    • code
  • Fix T_VerticalDoor mistake
    • Already fixed in pr+ / EE.
    • code
  • Fix buggy comp_stairs implementation
    • Already fixed in pr+ / EE.
    • code and code
  • P_CreateSecNodeList global tmthing fix
    • Already fixed in pr+ / EE.
    • code
  • A_CheckReload downstate
    • Already fixed in pr+ / EE.
    • code
  • Fix P_DivlineSide bug
    • Already fixed in pr+ / EE.
    • code
  • P_InterceptVector precision / overflow fix
    • Already fixed in pr+ / EE.
    • code
  • Fix generalized crusher walkover lines
    • Already fixed in EE, but not in pr+.
    • commit
  • Fix blockmap issue seen in btsx e2 Map 20
    • Already fixed in EE, but not in pr+.
    • commit
  • Fix missing dropoff condition
    • Already fixed in pr+, but not in EE.
    • code
    • EE
  • P_KillMobj thinker updates
    • Changed in pr+, reverted for mbf21.
    • commit
  • A_Mushroom changes
    • Changed in pr+, reverted for mbf21.
    • commit
  • Fix negative ammo counts
  • Fix weapon autoswitch not taking DEHACKED ammotype changes into account

Important Notes

  • The default ammopershot value for fist / chainsaw is 1 (matters for backwards compatibility).
  • Depending on how your port has modified spawning code, you may have a lingering bug related to the validcount variable. Basically, in vanilla doom, this variable is incremented in the wrong position in P_CheckPosition. The explanation for why this is a problem is complicated, but ripper projectiles (and possibly other cases) will expose this bug and cause desyncs. If your port strives for demo compatibility, I recommend adding an extra validcount increment in P_CheckPosition before running the P_BlockLinesIterator, as was done in dsda-doom (wrap it in a compatibility check for mbf21 if necessary): code. Without that fix, vanilla heretic demos go out of sync, and the same will be true for mbf21 demos with certain dehacked features in play. A port like crispy doom wouldn't be affected because it hasn't modified the spawn code in a way that introduces the bugged validcount. Ports like pr+ and ee do have this problem, for example.