Barrel suicide

From DoomWiki.org


A cacodemon committing barrel suicide

Because of a bug in the implementation of monster infighting in Doom v1.1 and earlier, monsters will try to commit suicide by attacking themselves if they are injured when they destroy barrels.

A simple explanation for this behavior is as follows: when a barrel is damaged, the Doom engine tracks which player or monster caused this damage. When the barrel explodes, the source of the previous damage is then passed on to objects damaged by the explosion. Ideally, a monster hurt in a barrel explosion will then retaliate against the monster or player that caused the barrel to explode (an example of monster infighting, if the source is another monster). However, the damage sourcing is done after the check to see if the barrel isn't damaged enough to be destroyed (see below), therefore a barrel can be first hit by a monster, and then destroyed by another, and it is the first monster who will be targeted by any AI victim of the explosion. Furthermore, if a barrel is destroyed in a single hit, no one will be considered responsible.

If a monster manages to damage itself in a barrel explosion, it is the cause of its own injury. Doom v1.1 does not check for this special case (it is not a scenario that occurs frequently during normal play). As a result, monsters that injure themselves through a barrel explosion will retaliate against themselves. Monsters such as cacodemons, barons of hell and imps will "tear themselves apart" with their own melee attack. Former humans (or monsters without melee attacks) will "go crazy", firing ahead blindly, possibly causing monster infighting as a result.

[edit] Technical

The likely cause of this can be seen in the Doom source code. In the P_DamageMobj function in p_inter.c is the following code:

if ( (!target->threshold || target->type == MT_VILE)
        && source && source != target
        && source->type != MT_VILE)
   {
       // if not intent on another player,
       // chase after this one
       target->target = source;
       target->threshold = BASETHRESHOLD;
       if (target->state == &states[target->info->spawnstate]
           && target->info->seestate != S_NULL)
           P_SetMobjState (target, target->info->seestate);
   }

This code is executed when an object is damaged by another object; this includes barrel explosions. The variable source is a reference to the object which caused the damage, while the variable target is a reference to the object on which damage is inflicted. When a barrel is destroyed, any affected enemies are injured with source equal to the player or enemy which caused the explosion.

The above code is supposed to cause monsters to retaliate against players which attack them. However, when a monster injures itself through a barrel explosion, both target and source will reference the monster. It is likely that the code in bold type was not present in version 1.1. As a result, the monster's target (target->target, the object it is currently chasing/attacking) is set to itself. The result is that it attacks itself.

[edit] Bug

The idea was presumably that barrels would "blame" the mobj directly responsible for their explosion. However, the P_DamageMobj function does not work in the proper order for this to actually happen. This excerpt shows the problem:

   // do the damage	
   target->health -= damage;	
   if (target->health <= 0)
   {
	P_KillMobj (source, target);
	return;
   }

   if ( (P_Random () < target->info->painchance)
	 && !(target->flags&MF_SKULLFLY) )
   {
	target->flags |= MF_JUSTHIT;	// fight back!
	
	P_SetMobjState (target, target->info->painstate);
   }
			
   target->reactiontime = 0;		// we're awake now...	

   if ( (!target->threshold || target->type == MT_VILE)
	 && source && source != target
	 && source->type != MT_VILE)
   {
	// if not intent on another player,
	// chase after this one
	target->target = source;
	target->threshold = BASETHRESHOLD;
	if (target->state == &states[target->info->spawnstate]
	    && target->info->seestate != S_NULL)
	    P_SetMobjState (target, target->info->seestate);
   }

The first block will automatically destroy the damaged actor if it has no remaining hit points, the last block, detailed in the previous section of this article, switches the target. Note the return; instruction after P_KillMobj in the first block: this means that if the mobj is killed, it will not run the rest of the function, and will therefore not switch target. P_KillMobj() itself does not have target switching mechanism either. (As as aside, this code also show some actor class-specific hacks: a charging lost soul will not be placed in its pain state, and an arch-vile will never be targeted, making it immune to infighting).

[edit] External links