-
Notifications
You must be signed in to change notification settings - Fork 0
Virtual functions
All NPCs in the library share a set of common virtual functions. Which allow them to define custom behaviors on a per-class basis. Like for defining custom attack decision code without having to create your own chase function from scratch.
- None
- None
Used to set the actors' default user variable values, for variables that did not have a custom value passed to them in the editor. GZDoom already has a //$UserDefaultValue editor key. However only works for actors spawned by the map. While this function allows for values to also be set for actors spawned by ACS or the console.
Say your NPC has a User_SpecialAttackChance variable, allowing mappers to define how likely the actor is to do a special attack. The default chance should be 24 out of 255 for every time it attacks. //$UserDefaultValue can set the default value for NPCs placed on the map. But the default value will be kept to 0 for NPCs spawned through ACS, or the console.
//$UserDefaultValue 24
Int User_SpecialAttackChance; //How often does the NPC do its' AOE attack ?
This is where UserVariableDefaults() helps:
Override Void UserVariableDefaults()
{
If (User_SpecialAttackChance == 0) User_SpecialAttackChance = 24; //Default AOE chance.
}
- None
- True or false.
Returns whether or not the NPC can currently attack, by default it just checks if the actor has a missile or melee state, is not dead, and is not in a SECF_NOATTACK sector. More or less vanilla behavior.
- NoStateJump: If true, the actor should not jump to any state even if the function returns true. This needs to manually be obeyed by overrides to this virtual.
- Returns true if the actor should attack, and also makes them actually enter the intended attack state if NoStateJump is off.
This virtual is the attack decision code of KAI NPCs. By default, it is to be used by chase functions to determine what attack the NPC should perform, if any, and make them actually go to that state. Overriding it allows for custom attack decision code on a per-actor basis, without needing to write new chase functions or other hacks.
By default, this virtual is a mostly direct ZScript rip of A_Chase's attack decision code. So naturally, it behaves the same as well.
- None
- Returns true or false, if it returns false, the rest of KAI_Wander() is not called.
Allows executing code every time KAI_Wander() is called, or aborting the functions' execution.
- None
- Returns a Vector3 coordinate to the location at which the NPC is going to attack.
Used to determine where the NPC is going to attack at.
If the NPC has a target, it returns their position, otherwise it returns a null vector*.
Note
This is a ClearScope function. So that it can be called from KAI_LOFRaycast as well.
- Other: The actor to check if it is hostile.
- Returns true if Other is hostile, false otherwise
Checks if the Other actor is an enemy to the calling KAI NPC or not. Can be used to give NPCs different conditions for if another actor is an enemy or not. Like for making an enemy NPC that doesn't always consider other NPCs to not be hostile like IsHostile() does.
Just calls IsHostile() by default.
- EventType: The event type that affected the group the NPC is in. To get the group event enums, either do KAI_NPCGroup.GRPEVNT_WHATEVER or WhatGroup.GRPEVNT_WHATEVER.
- Affected: The actor that was affected by the event that struck the group, such as the actor that joined or left the group.
- WhatGroup: What specific group that the NPC is in was affected by this event ?
Called when an NPC group that the NPC is in is struck by an event, as long as the group doesn't have the NPCGRP_NOMEMBERALERT flag. Useful for making individual NPCs react to events on their own, like getting mad if a specific group member dies, or saying something when a new member joins.
Does nothing if the event type is GRPEVNT_NONE, the Affected pointer is null, or the NPC itself is dead. These checks should usually be added as quick checks when you override this function.
If (EventType == KAI_NPCGroup.GRPEVNT_NONE || !Affected || IsDead(Self))
Return;
- Other: The actor whose threat level should be assessed.
- CheckPlayers: Should players be returned as THREAT_UNSTOPPABLE if they have buddha or god mode. Or be automatically returned as THREAT_DANGEROUS otherwise ? Default is true.
- Returns a threat level as an enum value.
Determines how dangerous the Other actor is. For more information, check the AssessThreatLevel() article.
- BasePos: With the default code, this is the fallback position vector to return if no position or actor to follow is found. On its' own, it could be used for whatever else per-actor, such as being a position to offset from.
- IgnoreDontFollowPlayers: Used to make the actor ignore +DONTFOLLOWPLAYERS if that flag is relevant in the code.
- A Vector3 that is the position of where the NPC should follow, in case it isn't necessarily following an actor pointer. But if an actor IS returned, this vector should be that actors' position!
- A pointer to the actor the function has determined should be followed.
As the name says, handles determining what actor or position the NPC should follow around.
By default it makes the actor follow nothing if the current order isn't ORDER_FOLLOW. Otherwise if the actor is hostile, it makes them follow the leader of the first group they're in, or fall back to following their master pointer (If any). If the actor is friendly, it will try to follow the player through the first group they're in, and if they're not in any player-led groups, follow their friendplayer. If IgnoringPlayers is on, it will follow a similar group follow logic as for hostile NPCs, check for a non-player master to follow if there's no groups its' in, and then finally fall back to the friendplayer if there's nobody else to follow.
- NewOrder: The new order that SetNPCOrder() will give to the NPC.
- All other parameters are the same as in the SetNPCOrder() call that triggered the virtual.
- If it returns true, SetNPCOrder() will run, otherwise, it will abort.
Can be used to pass additional logic to NPCs whenever they have an order given. Such as to react in a certain way if ordered by a commander of a specific class type, or do some special behavior if the function was called from ACS and such.
- OldOrder: The previous order that the NPC had before SetNPCOrder() changed it.
- All other parameters are the same as in the SetNPCOrder() call that triggered the virtual.
Works like OnOrderChange(), except that it is called after all of SetNPCOrder() is called.
Warning
Due to GZDoom having half broken vector support, the deflection funnel dimensions are output as a function return instead of just being passed by reference. Altering Funnel will NOT reflect outside the virtual!
Important
All parameters besides Projectile are passed by reference, meaning that if you alter the parameter directly, the OnActorDeflect() parameters will directly change.
- Projectile: The projectile that is going to deflect
- FailChance: The chance of the deflection failing, this is the random chance OnActorDeflect() calculated for the deflection to fail. NOT the callers' DeflectionChance property. If it goes higher than the aforementioned property, the deflection will not occur.
- DamFac: The callers' DeflectDamageFactor. Can be altered to change how much damage the caller takes.
- VelFac: The callers' DeflectSlowDownFactor. Can be altered to change how much the projectile (de/a)ccelerates post-deflection.
- Funnel: The callers' DeflectionFunnel. Can be altered to change the size and shape of the funnel.
- Returns the Vector4 funnel for OnActorDeflect() to use.
Called when A projectile detects it can deflect with the caller in OnActorDeflect(). This function can be used to just detect when a projectile deflects from the caller, or can also be used to entirely alter the behavior of the deflection. For example, the deflection can be aborted entirely by setting the fail chance super high.
This is how my Military Vehicles Pack uses this virtual to add deflection effects on impact:
//MVP projectile deflections add slight variation to the parameters. Alongside playing deflection sounds and sparks.
Override Vector4 OnProjectileDeflect (Actor Projectile, Int &FailChance, Double &DamFac, Double &VelFac, Vector4 Funnel)
{
Vector4 Ret = Super.OnProjectileDeflect (Projectile, FailChance, DamFac, VelFac, Funnel);
If (FailChance >= DeflectionChance) //Not happening.
Return Ret;
VelFac *= FRandom (0.8,1.0);
DamFac *= FRandom (0.8,1.0);
Ret.XY *= FRandom (0.8,1.2);
Double VertRNG = FRandom (0.8,1.2);
Ret.Z *= VertRNG; Ret.W = VertRNG;
//MVP Specific effects.
TextureID NullTex;
For (Int I = 0; I < 24; I++)
{
Projectile.A_SpawnParticleEx (
"yellow",
NullTex,
STYLE_Add,
lifetime:23,
size:3,
xoff:FRandom (6,-6),
yoff:FRandom (6,-6),
zoff:FRandom (6,-6),
velx:FRandom (1,-1),
vely:FRandom (1,-1),
velz:FRandom (1,3),
accelz: FRandom (-0.1,-0.25)
);
}
A_StartSound (Projectile.BounceSound,flags:CHANF_OVERLAP,1,0.9); //Ricochet sound, uses the BounceSound property, naturally.
Return Ret;
}
And this is how it can be used to directly alter deflection parameters based on custom conditions:
Override Vector4 OnProjectileDeflect (Actor Projectile, Int &FailChance, Double &DamFac, Double &VelFac, Vector4 Funnel)
{
//This particular damage type makes the projectile more likely to not deflect, but if it does, the funnel in which it can fly off in is much tighter.
If (Projectile.DamageType == 'LargeRifleBullet')
{
FailChance += 104;
Funnel.X = 10; Funnel.Y = -10;
Funnel.Z = 10; Funnel.W = -10;
}
Return Super.OnProjectileDeflect (Projectile, FailChance, DamFac, VelFac, Funnel);
}
- Type: The type of voice line the actor will say, default types are:
- VOICE_NONE: No voice type uttered at all, can be useful if for example you want a custom voice type spoken, but if it can't be found, the actor just says nothing.
- VOICE_SEE, VOICE_PLAYERSEE, VOICE_FEAR, VOICE_IDLE, VOICE_HAZARD, VOICE_PLAYERFEAR, VOICE_ORDERCHANGE, VOICE_GROUPJOIN, VOICE_GROUPLEAVE, VOICE_DEATH, VOICE_ALLYDEATH, VOICE_ALLYPLAYERDEATH, VOICE_ENEMYDEATH, VOICE_ALLYVEHICLEDEATH, VOICE_ENEMYVEHICLEDEATH, VOICE_ALLYREVIVE, VOICE_ENEMYREVIVE, VOICE_REVIVE: Check here for the voice types and their normal intended purpose.
- Voice: A pointer to the KAI_Voice to use, generally the return of GetNPCVoice() (Or a cached result) is used here.
- CustomType: The name of the custom voice type to look for in the Voice pack. Useful for, say, allowing NPCs to comment on more specific things, like a particular species of enemy they encounter.
- Volume: The volume at which the voice line is said, default is 1.0.
- Attenuation: The attenuation at which the voice line is said, default is ATTN_NORM.
- Channel: The sound channel for A_StartSound() to play the voice line at, default is CHAN_VOICE (Duh).
- Returns true if the actor said a voice line, false if not.
Makes the NPC say a random voice line from the voice pack and Type (Or CustomType) passed.
If a CustomType for SayVoiceLine() to look for is passed, but isn't found because the Voice doesn't have it, the function will instead play the default Type passed, this is useful as fallback voice line for the NPC to say.
An example of an NPC using one of the default voice types:
SayVoiceLine(VOICE_IDLE,KAIHandler.GetNPCVoice(User_Voice),volume:0.35,0.85);
An NPC saying a custom voice type:
SayVoiceLine(VOICE_NONE,KAIHandler.GetNPCVoice(User_Voice),"Taunt",0.35,0.85);
This NPC has a unique voice type for being spooked by demons, but also has VOICE_FEAR passed as the default type, so if the pack being searched for doesn't have a FearDemon voice type, they will instead utter a generic fear line if available.
SayVoiceLine(VOICE_FEAR,KAIHandler.GetNPCVoice(User_Voice),"FearDemon",0.45,0.85);
*A vector with a value of (Double.NaN,Double.NaN,Double.NaN), or -2147483647 on each member. Which is returned as an empty Vector3 by IsEmptyVector3.
- Home
- Features
- Classes
- Functions
- Guides