Projectile

From ZDoom Wiki
(Redirected from Missile)
Jump to navigation Jump to search
DoomWiki.org
For more information on this article, visit the Projectile page on the Doom Wiki.

A projectile is an actor that moves at a constant velocity each tic (unless its velocity is modified by methods like A_ScaleVelocity). On impact with a SHOOTABLE actor, the projectile will deal damage according to its Damage property. If the projectile has the RIPPER flag, it will pass through the actor; otherwise it will stop there. No projectiles can pass through walls.

When a projectile collides, it explodes and enters its death state, although technically it is not killed. If the Crash and XDeath states are defined, they may also be entered instead of the Death state, according to the collision type:

Collision State entered
Wall Death
Bleeding actor XDeath
Non-bleeding actor Crash

A non-bleeding actor is an actor with the NOBLOOD flag.

Projectiles generally have the Projectile combo to be a fully-fledged projectile (i.e. to be able to activate projectile line specials, enter its Death state sequence when hitting something, etc.), but the defining flag that makes an actor a projectile is MISSILE. Any actor without the MISSILE flag is not a projectile.

Note that the MISSILE flag only defines two explicit behaviors:

  • An actor with this flag will stop (its velocity will be set to 0) and enter Death (or another suitable state sequence) upon hitting something.
  • This flag is also required to use the Bouncetype property or any of the related flags (DOOMBOUNCE, HEXENBOUNCE, etc.). Actors without the MISSILE flag cannot bounce, regardless of any other properties or flags.

The full Projectile combo implies the following flags:

  • NOBLOCKMAP - Excludes the actor from blockmap, so that it can't be passively collided with.
  • NOGRAVITY - Disables gravity. Note, this flag has to be unset after the combo if you want to make a gravity-affected projectile.
  • DROPOFF - The actor can cross ledges.
  • MISSILE - The actor stops and enters Death sequence when hitting something. Also allows the actor to recognize bouncing properties and flags.
  • ACTIVATEIMPACT - The actor activates "projectile hits" line specials.
  • ACTIVATEPCROSS - The actor activates "projectile crosses" line specials.
  • NOTELEPORT - The actor can't activate teleport specials.
  • BLOODSPLATTER - This is set by the combo only in Hexen and Heretic by default. With this flag projectiles cause the victims to spawn blood.
Note: It is acceptable to use the MISSILE flag on non-projectile actors (such as bouncing debris or gibs) to enable bouncing, HOWEVER the whole Projectile combo should not be used in this case, since the other behaviors it implies are normally undesirable for bouncing FX actors (such as being able to activate linedef actions and such).

Pointers

By default projectiles utilize only one pointer: target. Counter-intuitively, the pointer doesn't point to the actor that the projectiles' shooter is attacking but rather to whoever shot the projectile (i.e. a monster that fired it with A_SpawnProjectile or a player pawn whose weapon fired it with A_FireProjectile). Projectiles don't get any pointers to their potential victims because they don't need to: they simply damage any actor they collide with while moving.

Seeker projectiles (ones using the SEEKERMISSILE flag and seeking functions) store the actor they are tracking to their tracer pointer (the target pointer however is still used like above).

Having a proper target pointer is important for various reasons:

  • It lets the shooter get a credit for the kill or damage, which makes sure a proper obituary is printed, the damaged victim will start hunting the correct actor, infighting is triggered properly, and such.
  • It ensures proper collision: a projectile can't collide with its shooter, because otherwise it would explode immediately upon firing (since projectiles are actually spawned inside whoever shot them). If the target pointer isn't assigned correctly, the projectile may be able to collide with its shooter.
  • If the target pointer is not set correctly, A_Explode will always hurt the shooter, even if the XF_HURTSOURCE flag is not set.

As such, spawning a projectile in a way that doesn't set the target pointer is not recommended. If, for example, A_SpawnItemEx is used to spawn a projectile, the SXF_SETTARGET flag should be added to set the target correctly. If a projectile spawns more projectiles mid-flight (e.g. if it's some kind of a cluster bomb), the extra projectiles must have the target pointer set correctly (again, in case of A_SpawnItemEx the SXF_TRANSFERPOINTERS flag should be used).

(Note: the summon <classname> console command has special handling for projectiles, where it'll automatically assign the player who used the command as the target of the projectile.)

Projectiles do get a victim pointer to the actor they're colliding with (or flying through), but that pointer only exists within the context of the SpecialMissileHit and DoSpecialDamage virtual functions. Those functions can be used to modify certain projectile behaviors at the moment when it collides with another actor.

Speed

A projectile's speed property defines 2 things:

  • How many units away from the center of the actor the projectile will spawn (no more than the actor's radius however: projectiles need to spawn within their shooter to make sure they don't spawn inside geometry or other actors)
  • The initial velocity it receives upon spawning

Once a projectile is fired, the speed property is no longer used directly, and modifying it (directly in ZScript or via A_SetSpeed) will have no effect. At this point the projectile is flying with a constant momentum and will do so until it collides with something. Its momentum is tracked in its vel field. If you wish to change a projectile's velocity mid-flight, you can either use A_ScaleVelocity, or modify its vel vector in ZScript.

For example, this variation of the rocket will continuously increase its velocity until it reaches 60:

class SpeedingRocket : Rocket
{	
	override void Tick()
	{
		super.Tick();
		if (vel.length() < 60)
		{
			vel *= 1.05;
		}
	}
}

This variation changes its velocity to 60 after 35 tics (1 second) in flight (and also plays its seesound, the same sound it plays when fired).

class ExtraFuelRocket : Rocket
{	
	bool spedup; // store if the rocket was sped up
	
	override void Tick()
	{
		super.Tick();
		if (GetAge() > 35 && !spedup) //check the age and if it has been sped up yet
		{
			A_StartSound(seesound); //play the [[Actor_properties#Sound|seesound]]
			vel = vel.unit() * 60; //set velocity to 60
			spedup = true; //record that it has been sped up
		}
	}
}

Very fast projectiles

Very high speed values can cause issues with collision, where the projectile may fly through actors or geometry that it would normally collide with. For that purpose, the FastProjectile class exists, which performs collision checks multiple times per tic to make sure it collides properly.

In ZDoom the "too fast" speed was considered anything about 60. GZDoom, however, uses much more stable collision logic, so regular projectiles (not based on the FastProjectile class) can have much higher speed values without any collision issues.

Collision issues due to high speed tend to be more obvious if a gravity-affected projectile (one that does not have the NOGRAVITY flag}}) is fired inside a 3D floor-based water sector: the way gravity is multiplied underwater can cause weird pitch changes at high speeds.

Virtual methods

These virtual methods are defined in Actor, but their primary use is for projectiles.

Called whenever the projectile is clipping into another actor to determine if it should fly through, collide with it or disappear.
Called for bouncing projectiles when they hit an actor, line or plane (if the flags allow it).
Called when a projectile has hit another actor and is about to deal damage.