A_Saw

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


StateProvider

action void A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff", int flags = 0, double range = 0, double spread_xy = 2.8125, double spread_z = 0, double lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus")

Usage

A generalized version of the Doom's chainsaw attack that can be used in custom weapons. For monsters, use A_M_Saw.

Serves as a wrapper for LineAttack.

Parameters

  • Sound fullsound
The sound that plays if the attack doesn't hit anything. Defaults to "weapons/sawfull".
  • Sound hitsound
The sound that plays if the weapon hits a target. Defaults to "weapons/sawhit".
  • int damage
The amount of damage to deal. If 0 is passed, it's interpreted as 2.
The damage is subjected to the default Doom's randomization rules (multiplied by 1d8) unelss the SF_NORANDOM flag is used.
  • class<Actor> pufftype
The puff class to spawn if the attack hits a wall or invulnerable actor. Defaults to "BulletPuff".
  • int flags
Flags that modify the function's behavior. Multiple flags can be combined with |:
  • SF_NORANDOM — Disables damage randomization.
  • SF_RANDOMLIGHTMISS — Adds a 25% chance to toggle lighting if no target is hit.
  • SF_RANDOMLIGHTHIT — Randomly changes light on hit, like A_GauntletAttack (25% 0, 37.5% 1, 37.5% 2).
  • SF_RANDOMLIGHTBOTH — The two above flags combined, works like A_GauntletAttack.
  • SF_NOUSEAMMOMISS — Do not use ammo if nothing hit.
  • SF_NOUSEAMMO — Do not use ammo. Note, this flag is not necessary if the weapon doesn't have Weapon.AmmoType1 or Weapon.AmmoType2 specified (since without those the weapon can't consume ammo by definition).
  • SF_NOPULLIN — the player is not pulled towards their struck target on successful hits.
  • SF_NOTURN — the player's facing angle is not adjusted towards their struck target on successful hits.
  • SF_STEALARMOR — When lifesteal is used, the stolen health is converted to armor points, repairing the player's armor, instead of healing the player like normal.
  • SF_NORANDOMPUFFZ — The random z offset given to the puff when spawned is disabled.
  • double range
The maximum range of the attack. If this is 0, the range is as the calling player's MeleeRange + 20 (with some slight variance).
  • double spread_xy
Maximum angle of random horizontal spread. Defaults to 2.8125.
  • double spread_z
Maximum angle of random vertical spread. Defaults to 0.
  • double lifesteal
If positive, this value is used as a factor for giving back the inflicted damage as hit points to the actor using the calling weapon. (The function performs healing by calling GiveBody).
  • int lifestealmax
The limit of how much the player heals when stealing health from the victim. This works for when stealing health for health and health for armor. Positive values set an explicit limit. If a value of zero is passed, the player is healed up to their maximum health when stealing for health, and up to the armorbonustype item's Armor.MaxSaveAmount (see below) when stealing for armor. Default value is 0.
  • class<BasicArmorBonus>> armorbonustype
The armor bonus item to use for repairing armor when life-stealing. This must be an item derived from BasicArmorBonus. When stealing for armor, the item's Armor.SaveAmount property is taken into account; the life stolen value will be multiplied by that property's value. If this is not specified, ArmorBonus will be used as the default item.

Examples

This example is taken from Doom's chainsaw weapon.

  Fire:
    SAWG AB 4 A_Saw;
    SAWG B 0 A_ReFire;
    Goto Ready;

ZScript definition

Note: The ZScript definition below is for reference and may be different in the current version of GZDoom.The most up-to-date version of this code can be found on GZDoom GitHub.

In GZDoom this function is fully handled in ZScript:

	action void A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff", int flags = 0, double range = 0, double spread_xy = 2.8125, double spread_z = 0, double lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus")
	{
		FTranslatedLineTarget t;

		if (player == null)
		{
			return;
		}

		if (pufftype == null)
		{
			pufftype = 'BulletPuff';
		}
		if (damage == 0)
		{
			damage = 2;
		}
		if (!(flags & SF_NORANDOM))
		{
			damage *=  random[Saw](1, 10);
		}
		if (range == 0)
		{ 
			range = MeleeRange + MELEEDELTA + (1. / 65536.); // MBF21 SAWRANGE;
		}

		double ang = angle + spread_xy * (Random2[Saw]() / 255.);
		double slope = AimLineAttack (ang, range, t) + spread_z * (Random2[Saw]() / 255.);

		Weapon weap = player.ReadyWeapon;
		if (weap != null && !(flags & SF_NOUSEAMMO) && !(!t.linetarget && (flags & SF_NOUSEAMMOMISS)) && !weap.bDehAmmo &&
			invoker == weap && stateinfo != null && stateinfo.mStateType == STATE_Psprite)
		{
			if (!weap.DepleteAmmo (weap.bAltFire))
				return;
		}

		int puffFlags = (flags & SF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0;

		Actor puff;
		int actualdamage;
		[puff, actualdamage] = LineAttack (ang, range, slope, damage, 'Melee', pufftype, puffFlags, t);

		if (!t.linetarget)
		{
			if ((flags & SF_RANDOMLIGHTMISS) && (Random[Saw]() > 64))
			{
				player.extralight = !player.extralight;
			}
			A_StartSound (fullsound, CHAN_WEAPON);
			return;
		}

		if (flags & SF_RANDOMLIGHTHIT)
		{
			int randVal = Random[Saw]();
			if (randVal < 64)
			{
				player.extralight = 0;
			}
			else if (randVal < 160)
			{
				player.extralight = 1;
			}
			else
			{
				player.extralight = 2;
			}
		}

		if (lifesteal && !t.linetarget.bDontDrain)
		{
			if (flags & SF_STEALARMOR)
			{
				if (armorbonustype == null)
				{
					armorbonustype = "ArmorBonus";
				}
				if (armorbonustype != null)
				{
					BasicArmorBonus armorbonus = BasicArmorBonus(Spawn(armorbonustype));
					armorbonus.SaveAmount = int(armorbonus.SaveAmount * actualdamage * lifesteal);
					armorbonus.MaxSaveAmount = lifestealmax <= 0 ? armorbonus.MaxSaveAmount : lifestealmax;
					armorbonus.bDropped = true;
					armorbonus.ClearCounters();

					if (!armorbonus.CallTryPickup (self))
					{
						armorbonus.Destroy ();
					}
				}
			}

			else
			{
				GiveBody (int(actualdamage * lifesteal), lifestealmax);
			}
		}

		A_StartSound (hitsound, CHAN_WEAPON);
			
		// turn to face target
		if (!(flags & SF_NOTURN))
		{
			double anglediff = deltaangle(angle, t.angleFromSource);

			if (anglediff < 0.0)
			{
				if (anglediff < -4.5)
					angle = t.angleFromSource + 90.0 / 21;
				else
					angle -= 4.5;
			}
			else
			{
				if (anglediff > 4.5)
					angle = t.angleFromSource - 90.0 / 21;
				else
					angle += 4.5;
			}
		}
		if (!(flags & SF_NOPULLIN))
			bJustAttacked = true;
	}

See also