From ZDoom Wiki
Jump to navigation Jump to search
For more information on this article, visit the A_BFGSpray page on the Doom Wiki.

void A_BFGSpray [(class<Actor> spraytype [, int numrays [, int damagecnt [, double ang [, double distance [, double vrange [, int defdamage [, int flags]]]]]]])]


Performs the secondary, "tracer" attack of the BFG9000. Note that this function will create unpredictable effects if used on a non-missile.

The tracer actors (spraytype) which are spawned by the function could have custom damage types applied to them, much like other weapon puffs. Note that the EXTREMEDEATH flag still has no effect on this, however, so modders must use DamageType "Extreme" for it to work properly. If PUFFGETSOWNER is used, however, it will use the calling actor's owner as the inflictor -- meaning if the owner has EXTREMEDEATH, then it will actually enact that flag. Specifically, if a BFG ball shot from a player calls A_BFGSpray and the sprays have the PUFFGETSOWNER flag, it relies upon the player's actor to determine causing extreme death or not. A tracer with the MTHRUSPECIES flag set prevents this function from having any effect on actors with the same species as the shooter's own.


  • spraytype: The actor to spawn at the position of each shootable actor that is hit. Default is "BFGExtra".
  • numrays: Spawns this many of the specified actor across an angle of 90 degrees with the target in the center. Default is 40, i.e. one tracer every 2.25°.
  • damagecnt: The count of iterations to calculate the damage for original BFG formula; it will add N random values from 1 to 8 together based on this value. Default is 15.
  • ang: Determines the field of view. Any actors within this angle are subject to being sprayed and damaged. Default is 90 (which is the player's FOV).
  • distance: Determines how far the function should search for monsters from the calling actor. Default is 1024.
  • vrange: Determines maximum vertical angle to autoaim at, in degrees. Default is 32.
  • defdamage: If greater than 0, the tracers deal this exact amount of damage and damagecnt is ignored. Default is 0.
  • flags: The following flags can be combined by usgin the bit-wise OR operator (|):
    • BFGF_MISSILEORIGIN — If set, the tracers are emitted from the projectile rather than the shooter.
    • BFGF_HURTSOURCE — If set, the shooter can be struck by their own projectile's tracers. This only has an effect if BFGF_MISSILEORIGIN is also set.


Because of the unique behavior of the BFG, this function has very limited use in custom projects. The only built-in class that makes use of it is the BFGBall, which is fired by the BFG, and triggers the function upon exploding.

    BFE1 AB 8 Bright
    BFE1 C 8 Bright A_BFGSpray
    BFE1 DEF 8 Bright

ZScript definition

extend class Actor
	// A_BFGSpray
	// Spawn a BFG explosion on every monster in view
	void A_BFGSpray(class<Actor> spraytype = "BFGExtra", int numrays = 40, int damagecnt = 15, double ang = 90, double distance = 16*64, double vrange = 32, int defdamage = 0, int flags = 0)
		int damage;
		FTranslatedLineTarget t;

		// validate parameters
		if (spraytype == null) spraytype = "BFGExtra";
		if (numrays <= 0) numrays = 40;
		if (damagecnt <= 0) damagecnt = 15;
		if (ang == 0) ang = 90.;
		if (distance <= 0) distance = 16 * 64;
		if (vrange == 0) vrange = 32.;

		// [RH] Don't crash if no target
		if (!target) return;

		// [XA] Set the originator of the rays to the projectile (self) if
		//      the new flag is set, else set it to the player (target)
		Actor originator = (flags & BFGF_MISSILEORIGIN) ? self : target;

		// offset angles from its attack ang
		for (int i = 0; i < numrays; i++)
			double an = angle - ang / 2 + ang / numrays*i;

			originator.AimLineAttack(an, distance, t, vrange);

			if (t.linetarget != null)
				Actor spray = Spawn(spraytype, t.linetarget.pos + (0, 0, t.linetarget.Height / 4), ALLOW_REPLACE);

				int dmgFlags = 0;
				Name dmgType = 'BFGSplash';

				if (spray != null)
					if ((spray.bMThruSpecies && target.GetSpecies() == t.linetarget.GetSpecies()) || 
						(!(flags & BFGF_HURTSOURCE) && target == t.linetarget)) // [XA] Don't hit oneself unless we say so.
						spray.Destroy(); // [MC] Remove it because technically, the spray isn't trying to "hit" them.
					if (spray.bPuffGetsOwner) spray.target = target;
					if (spray.bFoilInvul) dmgFlags |= DMG_FOILINVUL;
					if (spray.bFoilBuddha) dmgFlags |= DMG_FOILBUDDHA;
					dmgType = spray.DamageType;

				if (defdamage == 0)
					damage = 0;
					for (int j = 0; j < damagecnt; ++j)
						damage += Random[BFGSpray](1, 8);
					// if this is used, damagecnt will be ignored
					damage = defdamage;

				int newdam = t.linetarget.DamageMobj(originator, target, damage, dmgType, dmgFlags|DMG_USEANGLE, t.angleFromSource);
				t.TraceBleed(newdam > 0 ? newdam : damage, self);