Classes:Powerup

From ZDoom Wiki
Jump to navigation Jump to search
Note: Wait! Stop! Before you copy this actor's definition into your mod, remember the following things:
  1. You do not need to copy that actor, since it is already defined.
  2. In fact, it's not just useless, it's actually harmful as it can cause problems.
  3. If you want to modify it, or use a modified version, using inheritance is the way to go.
  4. The actor definitions here are put on the wiki for reference purpose only. Learn from them, don't copy them.
  5. There is only one exception: if what you want is changing Ammo capacity, you need to create a new type from Ammo.
Power-up
Actor type Internal Game MiniZDoomLogoIcon.png (ZDoom)
DoomEd Number None Class Name Powerup


Classes: InventoryPowerup
 →PowerBuddha
 →PowerDamage
 →PowerDoubleFiringSpeed
 →PowerDrain
 →PowerFlight
 →PowerFrightener
 →PowerHighJump
 →PowerInfiniteAmmo
 →PowerInvisibility
  →PowerGhost
  →PowerShadow
 →PowerInvulnerable
 →PowerIronFeet
  →PowerMask
 →PowerLightAmp
  →PowerTorch
 →PowerMinotaur
 →PowerMorph
 →PowerProtection
 →PowerReflection
 →PowerRegeneration
 →PowerScanner
 →PowerSpeed
 →PowerStrength
 →PowerTargeter
 →PowerTimeFreezer
 →PowerWeaponLevel2

Powerups are classes that are placed in the player's inventory to tell the game that the player possesses a power. In essense, the Powerup class is a variation of the Inventory class that comes with a few extra features, such as a timer to keep track of its duration (when the duration runs out, the powerup is removed), as well as InitEffect() and EndEffect() ZScript virtual functions that define what happens to the owner of the powerup when it's attached and detached. The Powerup class is normally used to handle various "powerup" effects, such as invulnerability, invisibility, etc., but can also be utilized to add any kind of timed effect affecting its owner.

In this context, the word "powerup" refers to the power itself, not to the PowerupGiver item that you pick up in order to get the power. For example in Doom, the radiation suit is a powerup giver and is not itself a powerup; instead it gives a power, in this case, protection from radiation, whimsically named the "iron feet" powerup by the programmers who made the game.

In the same way, the word "powerup" has traditionally been used for all kinds of artifacts in the game, but some of them are not actually true powerups nor powerup givers; for example in Doom, the soulsphere is actually a very powerful form of the health bonus item and the gained health is just normal health; however, the iron feet power that the radiation suit gives is a true powerup, because the effect lasts over a period of time and stays with the player until it is exhausted.

If a negative duration is given to a powerup subclass, it will be the amount of seconds the power stays active. If a powerup has the INVENTORY.HUBPOWER flag, it stays active when moving between levels within a hub. If a powerup has an Inventory.Icon property, this icon is displayed on the HUD while the power is active. (All standard HUDs will place it in the upper-right corner.)

Powerup and PowerupGiver

Normally, defining a custom powerup requires defining a custom PowerupGiver first (which functions as a pickup), then defining a powerup (unless one of the default one is used, like PowerInvulnerable), and then attaching the powerup to its PowerupGiver through the Powerup.Type property.

Properties and flags, such as Powerup.duration, can be defined either in a powerup class or a PowerupGiver class. If defined in the PowerupGiver, they will be transferred to the powerup itself.

In ZScript, If a powerup carries behavior but does not exist as a pickup in the world and can only be received through scripts, then the powerup can be given to the player directly, and you don't need to define a PowerupGiver for it. However, if the powerup is meant to exist as an item, then it needs a PowerupGiver. Powerups themselves cannot be placed in the world to function as pickups.

Powerups support these special properties in addition to the basic inventory properties:

  • Powerup.Color color [, alpha]
  • Powerup.Color BlueMap
  • Powerup.Color GoldMap
  • Powerup.Color GreenMap
  • Powerup.Color InverseMap
  • Powerup.Color RedMap
  • Powerup.Color None
Specifies the color and the opacity of the screen blend that is used when the powerup is active. Color is specified either as three integers (DECORATE only), an "RR GG BB" string or a color name from the X11R6RGB lump. Alpha is optional, and is a value between 0.0 and 1.0, defaulting to 0.33333 if not specified. Instead of specifying a color you can also use the predefined values BlueMap, GoldMap, GreenMap, InverseMap and RedMap. These do not set a blending value but instead use a predefined blend.
Using a color value of None for a PowerupGiver, results in having no screen blend. Setting an arbitrary color with zero alpha, instead, to override the powerup's own does not achieve the same task.
  • Powerup.Colormap [sourcecolor, ]destcolor
A generalization of the colormaps (which, counter-intuitively, are used with Powerup.Color), this creates a color ramp from the source color to the destination color. This uses decimal numbers for the red, green, blue components, ranging from 0.0 to 1.0, inclusive. If only one color is provided, then black (0.0, 0.0, 0.0) is used as the source color. For example, Powerup.Colormap 0.0, 0.0, 0.0, 1.0, 1.0, 1.0 and Powerup.Colormap 1.0, 1.0, 1.0 both create a grayscale, Powerup.Colormap 1.0, 1.0, 1.0, 0.0, 0.0, 0.0 creates the well known inverse map and Powerup.Colormap 1.0, 0.0, 0.0, 0.0, 1.0, 0.0 creates a red to green effect.
  • Powerup.Duration time
The length of time the power up will last in tics (1/35 of a second). If a negative duration is given, it will be taken as the duration in seconds (e.g., -30 is half a minute, while 30 is less than a second). The maximum duration is 0x7FFFFFFF, which corresponds to about two years of real time and can therefore safely be used for permanent powerups. Note, however, that due to the design of the TimeFreezer powerup, using such a duration value for this powerup is not recommended, as the engine will add two tics to the powerup's duration if the duration's value was even and the powerup was activated on an odd game tic, resulting in a signed overflow error. A value of 0x7FFFFFFD is safe to use in such a case.
  • Powerup.Mode mode
For some powerups which may behave in different ways, specify the mode. Currently affects Invulnerability, Invisibility and Environment protection.
  • Invulnerability modes: None and Reflective. "None" is invulnerability without any added effect. "Reflective" also makes all projectiles bounce off of the player while the powerup is in effect.
  • Invisibility modes: Cumulative, Fuzzy, Opaque, Stencil and Translucent. "Cumulative" allow multiple invisibility powerups of the same type to stack when active at the same time, adding their strength value together. "Fuzzy" uses the fuzz effect renderstyle, like that of the spectre. "Opaque" results in an opaque renderstyle, making the actor not invisible graphically speaking. "Stencil" uses the stencil renderstyle, showing just the shape. "Translucent" is merely a way of explicitly specifying the default invisibility mode.
  • Environment protection modes: Normal and Full. "Normal" is environment protection's default mode. "Full" is total-protection mode. It is similar to "Normal", but it also protects against the types of damaging floors that the "Normal" mode does not protect against, whether it is partial protection or not.
  • Powerup.Strength strength
The magnitude of a powerup's effect. Currently applies to Invisibility and Regeneration, as other powerups have unquantifiable effects or use predetermined fields such as DamageFactor.
  • Invisibility strength: determines how translucent the affected actor becomes. 0 is for opaque, and 100 is for fully invisible. Does not apply to "Fuzzy" mode.
  • Regeneration strength: determines the amount of health regenerated.

Methods

Note: This feature is for ZScript only.

All of these methods are virtual and can be overridden to define/add custom behavior.

Called when a power up becomes active for the first time.
Called when the power up ends.
Returns the icon drawn on the screen for the power up
Returns true if the power up is about to run out and is currently in the middle of a blink. Returns false otherwise.

Powerups also use the DoEffect virtual (defined in Inventory) to apply their looped effects.

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.
class Powerup : Inventory
{
	int EffectTics, MaxEffectTics;	
	color BlendColor;
	Name Mode;			// Meaning depends on powerup - used for Invulnerability and Invisibility
	double Strength;		// Meaning depends on powerup - currently used only by Invisibility
	int Colormap;
	const SPECIALCOLORMAP_MASK =  0x00b60000;

	property Strength: Strength;
	property Mode: Mode;
	
	// Note, that while this is an inventory flag, it only has meaning on an active powerup.
	override bool GetNoTeleportFreeze() 
	{ 
		return bNoTeleportFreeze; 
	}

	//===========================================================================
	//
	// APowerup :: Tick
	//
	//===========================================================================

	override void Tick ()
	{
		// Powerups cannot exist outside an inventory
		if (Owner == NULL)
		{
			Destroy ();
		}
		if (EffectTics == 0 || (EffectTics > 0 && --EffectTics == 0))
		{
			Destroy ();
		}
	}

	//===========================================================================
	//
	// APowerup :: HandlePickup
	//
	//===========================================================================

	override bool HandlePickup (Inventory item)
	{
		if (item.GetClass() == GetClass())
		{
			let power = Powerup(item);
			if (power.EffectTics == 0)
			{
				power.bPickupGood = true;
				return true;
			}
			// Color gets transferred if the new item has an effect.

			// Increase the effect's duration.
			if (power.bAdditiveTime) 
			{
				EffectTics += power.EffectTics;
				MaxEffectTics = Max(EffectTics, MaxEffectTics);
				BlendColor = power.BlendColor;
			}
			// If it's not blinking yet, you can't replenish the power unless the
			// powerup is required to be picked up.
			else if (EffectTics > BLINKTHRESHOLD && !power.bAlwaysPickup)
			{
				return true;
			}
			// Reset the effect duration.
			else if (power.EffectTics > EffectTics)
			{
				EffectTics = MaxEffectTics = power.EffectTics;
				BlendColor = power.BlendColor;
			}
			power.bPickupGood = true;
			return true;
		}
		return false;
	}

	//===========================================================================
	//
	// APowerup :: CreateCopy
	//
	//===========================================================================

	override Inventory CreateCopy (Actor other)
	{
		// Get the effective effect time.
		EffectTics = MaxEffectTics = abs (EffectTics);
		// Abuse the Owner field to tell the
		// InitEffect method who started it;
		// this should be cleared afterwards,
		// as this powerup instance is not
		// properly attached to anything yet.
		Owner = other;
		// Actually activate the powerup.
		InitEffect ();
		// Clear the Owner field, unless it was
		// changed by the activation, for example,
		// if this instance is a morph powerup;
		// the flag tells the caller that the
		// ownership has changed so that they
		// can properly handle the situation.
		if (!bCreateCopyMoved)
		{
			Owner = NULL;
		}
		// All done.
		return self;
	}

	//===========================================================================
	//
	// APowerup :: CreateTossable
	//
	// Powerups are never droppable, even without IF_UNDROPPABLE set.
	//
	//===========================================================================

	override Inventory CreateTossable (int amount)
	{
		return NULL;
	}

	//===========================================================================
	//
	// APowerup :: InitEffect
	//
	//===========================================================================

	virtual void InitEffect() 
	{
		// initialize this only once instead of recalculating repeatedly.
		Colormap = ((BlendColor & 0xFFFF0000) == SPECIALCOLORMAP_MASK)? BlendColor & 0xffff : PlayerInfo.NOFIXEDCOLORMAP;
	}

	//===========================================================================
	//
	// APowerup :: DoEffect
	//
	//===========================================================================

	override void DoEffect ()
	{
		if (Owner == NULL || Owner.player == NULL)
		{
			return;
		}

		if (EffectTics > 0)
		{
			if (Colormap != PlayerInfo.NOFIXEDCOLORMAP)
			{
				if (!isBlinking())
				{
					Owner.player.fixedcolormap = Colormap;
				}
				else if (Owner.player.fixedcolormap == Colormap)	
				{
					// only unset if the fixed colormap comes from this item
					Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP;
				}
			}
		}
	}

	//===========================================================================
	//
	// APowerup :: EndEffect
	//
	//===========================================================================

	virtual void EndEffect ()
	{
		if (colormap != PlayerInfo.NOFIXEDCOLORMAP && Owner && Owner.player && Owner.player.fixedcolormap == colormap)
		{ // only unset if the fixed colormap comes from this item
			Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP;
		}
	}

	//===========================================================================
	//
	// APowerup :: Destroy
	//
	//===========================================================================

	override void OnDestroy ()
	{
		EndEffect ();
		Super.OnDestroy();
	}

	//===========================================================================
	//
	// APowerup :: GetBlend
	//
	//===========================================================================

	override color GetBlend ()
	{
		if (Colormap != Player.NOFIXEDCOLORMAP) return 0;
		if (isBlinking()) return 0;
		return BlendColor;
	}

	//===========================================================================
	//
	// Inventory :: GetPowerupIcon
	//
	// Returns the icon that should be drawn for an active powerup.
	//
	//===========================================================================

	virtual clearscope version("2.5") TextureID GetPowerupIcon() const
	{
		return Icon;
	}

	//===========================================================================
	//
	// APowerup :: isBlinking 
	//
	//===========================================================================

	virtual clearscope bool isBlinking() const
	{
		return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink);
	}

	//===========================================================================
	//
	// APowerup :: OwnerDied
	//
	// Powerups don't last beyond death.
	//
	//===========================================================================

	override void OwnerDied ()
	{
		Destroy ();
	}

	
}

DECORATE definition

Note: This is legacy code, kept here for reference only. DECORATE is still supported but no longer used by GZDoom. GZDoom internally uses the ZScript definition above.
ACTOR Powerup : Inventory native {}