Skip to content

KAI_BaseProjectile

inkoalawetrust edited this page Aug 11, 2024 · 9 revisions

KAI_BaseProjectile

Parent class: FastProjectile

Description

This is the base class for projectiles made with the KAI library. You do not need to make projectiles based on this class, but does so does provide some benefits:

  • It's based on FastProjectile, so it can properly move both with fast and slow projectiles.
  • Unlike fast projectiles, gravity and bouncing/reflecting work on KAI projectiles just like they do on standard ones.
  • It's got a custom trail system made for VERY fast projectiles, since the stock FastProjectile trails break down at speeds above ~120 MU/tic.
  • It has a deflection function, which makes the projectile deflect like a bullet.
  • Allows rippers to only rip each actor they hit once, and specify how many rips it can perform before losing its' ripper flag.
  • Has code that allows you to change the visual pitch of the projectile based on its' actual pitch. Similar to what KAI_BaseTurret allows for.
  • Has a virtual that is called for each trail spawned, with a pointer to the trail.
  • Not really a feature, but KAI projectiles face the direction they are heading at immediately after spawning.

Features

Trail spawning

The trail of a KAI projectile.

KAI_BaseProjectile has a custom trail spawning system, which spawns a line of trail Visual Thinkers once the projectile is fired, which then moves relative to it for every tick. Which allows for solid bullet tracer-like trails that work regardless of how fast the projectile is. Unlike FastProjectile trails, which begin breaking up with really fast projectiles like the same shell as in the above image.

The trail of a really fast projectile using just the stock FastProjectile trails

Deflection

A KAI vehicle deflecting a bullet projectile

KAI projectiles have the ability to deflect off of actors, complete with a spark visual effect. Especially useful for bullet deflection effects. This works differently from ZDooms normal projectile reflection. Deflection chances, angles, damage penalties etc can be specified on a per-actor basis by overriding SpecialMissileHit().

Flags

NoTrailLine

Make the projectile use the stock FastProjectile trail spawning code instead of the new system.

NoFriendlyFire

The projectile will not collide with actors friendly to its' shooter.

RipOnce

Used for ripper projectiles, this flag makes them only rip through each actor they hit once, instead of ripping and applying damage multiple times, which makes handling ripper damage and LOF checks simpler. And also prevents inconsistent damage, like Revenants taking less damage than Arachnotrons due to being smaller.

Properties

TrailDelay

How many ticks should it take after the projectile spawns before it begins spawning a trail ? This affects both FastProjectile and KAI trails.

Custom trail properties

These properties only matter for the custom trail spawning system.

TrailSprite

The VisualThinker to use for trail pieces, must be based on KAI_Tracer.

MaxTrails

The maximum length of the trail line.

InitialTrailsSkipped

How many trails pieces extending from the front of the projectile should not be spawned ? Useful for making trails not spawn inside of projectiles.

TrailFadeAndScaleStart

After which trail piece should the scale and/or alpha of the subsequent trail pieces begin falling off. Useful for making trails like this.

ScaleAmountX, ScaleAmountY

How much should the X and Y scale, respectively. Begin increasing from each trail piece. Negative amounts lower the scale.

FadeAmount

How much should the alpha scale from each next piece. Positive amounts increase the alpha, negatives lower it.

TrailLineSpacing

How spaced apart should the trail pieces be, in map units.

RipDepth

How many rips the ripper projectile can perform before stopping.

BUG: This flag might cause issues with tightly spaced actors, like ripping a lot more actors or doing additional damage. I've tried a few solutions but they either didn't work or made things worse.

Variables

DontCollide

The actor to ignore collision with, this is primarily set by DeflectProjectile() to prevent projectiles getting stuck inside actors.

LastRipped, PreLastRipped

Stores the last actor that was ripped by a ripper, and the actor before it that was ripped. Used by the +RIPONCE flag to stop rippers from ripping the actor they already hit. And handled by StoreLastRipped().

LastTracerPosition, CurrentTracerPosition

Tracks' the projectiles' tracer pointer like LastEnemyPosition does for NPCs.

Functions

TrailSpawn()

Parameters

  • Trail: A pointer to the KAI_Tracer trail segment spawned.

Function

Called for every trail segment spawned by the projectile when it is initially created. Only used by the KAI's custom trail line spawning code.

ActorTrailSpawn()

Parameters

  • Trail: A pointer to the KAI_TracerActor trail segment spawned.

Function

Behaves like TrailSpawn() but for spawning more expensive actor-based trails instead, also used by trails spawned with the stock FastProjectile trail code. And of course can also be used for any custom projectile trail code you might wanna make (Just like TrailSpawn() can).

DeflectProjectile()

Parameters:

  • Victim: The actor that the projectile is deflecting off of.
  • DeflectionChance: What's the chance that the projectile will deflect ?
  • DamageReductionFactor: How much is the damage the Victim receives by the deflected projectile reduced, relative to its' normal damage ?
  • SlowDownFactor: How much is the projectile slowed down or sped up post deflection ? Default is 1 (No change).
  • RandomAngle: The range in which the projectile can bounce away from the Victim, stored in the X and Y components of a Vector2.
  • RandomPitch: Same as RandomAngle put for the projectiles' pitch.
  • Volume: The volume at which the projectiles' BounceSound plays. Default is 1.0.
  • Attentuation: The attenuation at which the projectiles' BounceSound plays.

Return type(s):

  • Returns true if the projectile deflected, false otherwise.

Function:

Deflects the projectile off the Victim actor, and also spawns a visual spark effect at the location of the deflection. This function is primarily meant to be used in the projectiles' SpecialMissileHit().

Example

This is the SpecialMissileHit() override of the 7.62mm round from my Military Vehicles Pack mod.

	Override Int SpecialMissileHit (Actor Victim)
	{
		If (Super.SpecialMissileHit(Victim) == 1) Return 1;
		
		//Always bounce off players with god mode. Or player pawns and monsters with Invulnerable or NoDamage.
		If ((HasGodMode(Victim) || IsIndestructible(Victim) && !IsInanimateObject(Victim)) && DeflectProjectile (Victim,0,FRandom(0.4,0.6),FRandom (1.,0.7),(10,-10),(10,-10))) Return 1;
		//Be at least somewhat likely to bounce off if hitting an APC.
		Else If ((Victim Is "MVP_APC" || Victim Is "MVP_APCProp") && DeflectProjectile (Victim,128,FRandom(0.4,0.6),FRandom (1.,0.7),(10,-10),(10,-10))) Return 1;
		//Be very likely to bounce off a non-bleeding actor with a lot of health.
		Else If (Victim.bNoBlood && !IsIndestructible (Victim) && Victim.SpawnHealth() >= 2000 && DeflectProjectile (Victim,64,FRandom(0.5,0.7),FRandom (1.,0.8),(6,-6),(6,-6),attenuation:0.75)) {bRipper = False; Return 1;}
		
		
		Return -1;
	}

As you can see, the bullet has different deflection stats based on if it hit an indestructible NPC or player, if it hit an APC specifically, or if it hit a generic durable inorganic actor.

KAI_SeekerMissile()

Parameters:

  • Threshold: If the projectile is within this FOV of the tracer, it will instantly face the target.
  • Strength: How much of the angle to the tracer the projectile will use, this can range from 0.0 to 1.0, 1.0 means that it instantly faces the tracer.
  • Flags: The flags to pass to the function, supports all the A_SeekerMissile flags except for SMF_PRECISE.
    • SMF_SMART: Instead of moving to where the tracer currently is, the projectile will go to where they will be in the next tick.
  • Inaccuracy: How inaccurate should the projectile be in each axis, when tracking the tracer ? This works like actor radii, so a value of (8,8,8) will make the track position a random point within a 16x16x16 MU cube.
  • Distance, Chance: Works like the A_SeekerMissile() parameters, and only does something if SMF_LOOK is on.

Return type(s):

  • Returns true if the projectile successfully tracked the tracer, like A_SeekerMissile() does.

Function:

Makes the projectile move towards its' tracer pointer, if any. This is a ZScript version of A_SeekerMissile(), with full 3D tracking by default. It does work differently however.

GetFutureTrajectory()

Parameters:

  • Inaccuracy: The Vector3 passed by KAI_SeekerMissile to randomize the tracking pos with every call.

Return type(s):

  • A Vector3 of where the tracer will be in the next tick.

Function:

Tracks where the projectiles' tracer will be in the next tick.

UpdateProjectileElevation()

Parameters:

  • FaceDown: The sprite frame to return if the projectiles' pitch is within 30 to 70 degrees.
  • FaceDown2: The sprite frame to return if the projectiles' pitch is over 70 degrees.
  • FaceStraight: The sprite frame to return if the projectiles' pitch is within 30 to -30 degrees.
  • FaceUp: The sprite frame to return if the projectiles' pitch is within -30 to -70 degrees.
  • FaceUp2: The sprite frame to return if the projectiles' pitch is below -70 degrees.
  • Mirror: If the actor is facing down, mirror the FaceDown and FaceDown2 frames vertically with +YFLIP. Can be used to save up on sprite elevations, similar to mirror sprites horizontally.

Return type(s):

  • Returns a sprite frame number based on the conditions above.

Function:

This function returns a sprite frame based on the pitch. Used to make projectiles able to visually face up and down like vehicle turrets. Preferably this should be called every few ticks.

Example

This is how the function is used by one of the projectiles in my Military Vehicles Pack mod. The missiles' first animation frame is C (Facing straight head like normal), and the state that loops until the projectile impacts has a malleable sprite frame, which makes it easier for me to call UpdateProjectileElevation() to change the sprite sheets' frame based on the projectiles' pitch. The frame letters corresponding to each number are also mapped in the code comment.

		Spawn:
			MVP1 C 0;
			MVP1 # 4 Light ("AutocannonTracer")
			{
				A_FaceMovementDirection ();
				Frame = UpdateProjectileElevation (5,6,2,3,4); //F, G, C, D, E
			}
			Wait;

GetAngleToPos()

Direct copy of the eponymous function from KAI_Actor, due to KAI_BaseProjectile not inheriting from it.

IsHostileToShooter()

Parameters:

  • Other: The actor to check if they are hostile to the projectiles' shooter (Stored in the target pointer).

Return type(s):

  • Returns true if Other is hostile to the projectiles' shooter, false otherwise.

Function:

Checks if the Other actor is an enemy of the projectiles' target. If the projectiles' target is a KAI_Actor, then the IsActorHostile() virtual of the target is called. If they are not a KAI actor, the native IsHostile() is called instead.

StoreLastRipped()

Parameters:

  • Victim: The actor to store in the LastRipped pointer.

Function:

If the projectile is a ripper, this stores the actor that was hit, and the actor that was ripped before that.

IncrementRipDepth()

Parameters:

  • Victim: The victim of the rip. Does nothing.
  • FromOverride: Set this to true if the function was called from an override to SpecialMissileHit(). If you have custom conditions for what the ripper can't rip.

Function:

Decrements the RipDepth property every time the projectile successfully rips through an actor. And remove the ripper flag once it the depth equals 1 or less.

NOTE: If your ripper projectile has additional conditions that make it ignore an actor that KAI_BaseProjectiles' SpecialMissileHit() considered a successful collision. You can undo the new rip information by storing LastRipped and PreLastRipped in pointers before calling Super.SpecialMissileHit(), and then reverting the pointers post-super callback. If the additional conditions failed, something like this:

	Override Int SpecialMissileHit (Actor Victim)
	{
		Actor OldLastRip = LastRipped; Actor OldPreLastRip = PreLastRipped;
		
		Int Result = Super.SpecialMissileHit(Victim);
		
		If (Result == -1 && fail_conditions)
		{
			LastRipped = OldLastRip; PreLastRip = OldPreLastRip; //Revert pointer changes in the super callback.
			RipDepth++; //Undo depth counter countdown.
			Result = 1;
		}
		
		Return Result;
	}

KAI_Tracer

KAI_Tracer is a VisualThinker, and is the object class accepted by KAI_BaseProjectile.TrailSprite. Due to being a visual thinker, it runs about twice as fast as KAI_TracerActor does, running more or less the same code. It has the following variables and functions for controlling its' behavior:

Variables

Note: Most of these are really just relevant for trail segments spawned by the projectile trail line code, hence why these are mostly things that are otherwise directly settable with VisualThinker.Spawn().

DefaultScale

An FVector2 variable. Used to specify the default scale that the thinker uses. Should ideally be set in SetProperties(). The virtual sets this to (0.5,0.5) by default

DefaultSprite

A TextureID variable. Used to set the graphic that's attached to the thinker. Should also be set in SetProperties().

NoWarp

Boolean flag. Makes the trail segment not warp relative to its' Owner using the specified WarpOffsets.

DestroyIfNoOwner

Boolean flag. Makes the thinker remove itself if it has no Owner, or the Owner is a projectile that has detonated.

RanPostBeginPlayEarly

Internal boolean. Just used by the trail line spawning code to run PostBeginPlay() early (Like BeginPlay() in actors, which thinkers don't have in general). To ensure the right order of execution for setting rendering properties like the thinkers' visual scale. The flag is there to ensure PostBeginPlay() doesn't run twice at a later date, when the engine actually calls it.

WarpOffsets

Vector3 variable. Keeps track of where to warp the trail segment relative to the Owner pointers' position.

SpawnedWhen

Keeps track of the time the thinker spawned for GetAge(). Like the SpawnTime variable in actors.

Owner

A pointer to the actor the trail segment is attached to.

Functions

SetProperties()

This is the virtual function used by KAI_Tracer to set defaults like what graphic to use, what scale, etc for the thinker. Called by PostBeginPlay(), and should also be called first in PostBeginPlay(), before any of the code that might use the variables it sets the default values of.

GetAge()

Functions identically to the actor function. Might be useful if if you need something that requires the trails' age, like making it despawn after sometime.

KAI_TracerActor

KAI_TracerActor is a trail actor class. Not used on its' own in favor of KAI_Tracer. This actor runs almost no code in its' Tick() virtual, it's purely a visual effect. It does however use existing actor flags and properties to control its' behavior:

  • ReactionTime: How long the tracer should exist before being removed, measured in tics.
  • bNeverRespawn: Turning on this flag causes the trail to automatically vanish if it has no master or its' master is dead.
  • bStandStill: The trail will not warp relative to its' target.

See also:

Clone this wiki locally