SpecialMissileHit
virtual int SpecialMissileHit(Actor victim)
Usage
Called by projectiles when they hit another actor. Overriding it allows creating custom collision behavior.
Parameters
- Actor victim
- The actor that the projectile is colliding with. This can be literally actor, including the ones that the projectile wouldn't collide with normally. The shooter of the projectile is not excluded from this, so it's important to check
if (victim != target)
if the overridden behavior shouldn't apply to the shooter.
Return values
Note: the MHIT* constants were added in GZDoom 4.12.0. Prior to that, numbers had to be used directly.
- -1
- MHIT_DEFAULT
- (Default) The projectile utilizes its standard collision behavior and does whatever it would normally do based on its own and the victim's flags and properties.
- 0
- MHIT_DESTROY
- The projectile is destroyed but doesn't deal damage, enter its Death state or perform any other special behavior.
- 1
- MHIT_PASS
- The projectile passes through the actor without interacting with it. NOTE: this should not be confused with RIPPER. The RIPPER flag adds special ripping behavior that only activates if this function returns -1 (the default value) and will be completely bypassed if this function returns 1
Examples
This version of the PlasmaBall doesn't deal any damage but instead pushes enemies it hits away and upwards:
class BowlingBall : PlasmaBall
{
array<Actor> victims;
override int SpecialMissileHit(Actor victim)
{
// Check that victim is a valid victim, not the same actor
// as the shooter, is shootable and isn't yet present
// in the victims array:
if (victim && (!target || victim != target) && victim.bSHOOTABLE && !victim.bDONTTHRUST && victims.Find(victim) == victims.Size())
{
// Push horizontally away from self:
victim.vel.xy = self.Vec2To(victim).Unit() * 15;
// Push vertically:
victim.vel.z += 8;
// Add to array:
victims.Push(victim);
}
return MHIT_PASS;
}
}
This shows an example of a ripping projectile without the RIPPER flag that only damages its victims once (in contrast to default rippers that damage every tic while their hitbox is colliding with the victim's hitbox):
class MyPiercingMissile : Actor
{
// array of damaged victims
array<Actor> damagedVictims;
Default
{
Projectile;
Damage 20; //example damage
}
override int SpecialMissileHit(Actor victim)
{
// Victim doesn't exist or it's the projectile's
// shooter - pass through it:
if (!victim || (target && victim == target))
{
return MHIT_PASS;
}
// Victim doesn't allow ripping - stop and explode:
if (victim.bDONTRIP)
{
return MHIT_DEFAULT;
}
// Victim is shootable, is not non-shootable, and hasn't
// yet been damaged by this projectile:
if (victim.bSHOOTABLE && !victim.bNONSHOOTABLE && damagedVictims.Find(victim) == damagedVictims.Size())
{
// damage it:
victim.DamageMobj(inflictor: self,
source: target? target : Actor(self),
damage: self.GetMissileDamage(3, 2), //uses default ripper dmg RNG; can use different values if desired
mod: 'normal');
// add it to the array so it won't be damaged
// again:
damagedVictims.Push(victim);
}
// Pass through the actor regardless of whether it's been
// damaged or not:
return MHIT_PASS;
}
}
See also GetMissileDamage (for getting the usual randomized damage rules), DamageMobj (for dealing damage) and the used flags: SHOOTABLE, NONSHOOTABLE, DONTRIP. This, of course, can be tweaked to modify behavior to, for example, always deal explicit damage (and ignore the Damage property), treat the flags differently (such as always piercing actors even with DONTRIP), and so on. This example specifically aims to emulate the regular ripper projectilles, but in a way that deal damage only once. It doesn't define any states because it's meant to be used as a base class for projectiles in your project.