528 lines
14 KiB
C++
528 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2016-2022 Vera Visions LLC.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
var bool autocvar_ai_debugLogic = false;
|
|
void
|
|
_NSMonster_Log(string msg)
|
|
{
|
|
if (autocvar_ai_debugLogic == true)
|
|
print(sprintf("%f %s\n", time, msg));
|
|
}
|
|
#define NSMonster_Log(...) _NSMonster_Log(sprintf(__VA_ARGS__))
|
|
|
|
/**
|
|
Bitfield enumeration for NSMonster its SendFlags field.
|
|
|
|
These give hint as to which internal fields get networked to the client.
|
|
*/
|
|
typedef enumflags
|
|
{
|
|
MONFL_CHANGED_ORIGIN_X,
|
|
MONFL_CHANGED_ORIGIN_Y,
|
|
MONFL_CHANGED_ORIGIN_Z,
|
|
MONFL_CHANGED_ANGLES_X,
|
|
MONFL_CHANGED_ANGLES_Y,
|
|
MONFL_CHANGED_ANGLES_Z,
|
|
MONFL_CHANGED_MODELINDEX,
|
|
MONFL_CHANGED_SIZE,
|
|
MONFL_CHANGED_FLAGS,
|
|
MONFL_CHANGED_SOLID,
|
|
MONFL_CHANGED_FRAME,
|
|
MONFL_CHANGED_SKINHEALTH,
|
|
MONFL_CHANGED_MOVETYPE,
|
|
MONFL_CHANGED_EFFECTS,
|
|
MONFL_CHANGED_BODY,
|
|
MONFL_CHANGED_SCALE,
|
|
MONFL_CHANGED_VELOCITY,
|
|
MONFL_CHANGED_RENDERCOLOR,
|
|
MONFL_CHANGED_RENDERAMT,
|
|
MONFL_CHANGED_RENDERMODE,
|
|
MONFL_CHANGED_HEADYAW
|
|
} nsmonster_changed_t;
|
|
|
|
/** List of supported ACT types.
|
|
These originate from GoldSrc and framegroups within models can be tagged
|
|
with them. This way the game-logic doesn't need to know the exact framegroup
|
|
but can instead pick a random ACT and we'll pick the right framegroup for you. */
|
|
typedef enum {
|
|
ACT_RESET = 0,
|
|
ACT_IDLE = 1i,
|
|
ACT_GUARD,
|
|
ACT_WALK,
|
|
ACT_RUN,
|
|
ACT_FLY,
|
|
ACT_SWIM,
|
|
ACT_HOP,
|
|
ACT_LEAP,
|
|
ACT_FALL,
|
|
ACT_LAND,
|
|
ACT_STRAFE_LEFT,
|
|
ACT_STRAFE_RIGHT,
|
|
ACT_ROLL_LEFT,
|
|
ACT_ROLL_RIGHT,
|
|
ACT_TURN_LEFT,
|
|
ACT_TURN_RIGHT,
|
|
ACT_CROUCH,
|
|
ACT_CROUCHIDLE,
|
|
ACT_STAND,
|
|
ACT_USE,
|
|
ACT_SIGNAL1,
|
|
ACT_SIGNAL2,
|
|
ACT_SIGNAL3,
|
|
ACT_TWITCH,
|
|
ACT_COWER,
|
|
ACT_SMALL_FLINCH,
|
|
ACT_BIG_FLINCH,
|
|
ACT_RANGE_ATTACK1,
|
|
ACT_RANGE_ATTACK2,
|
|
ACT_MELEE_ATTACK1,
|
|
ACT_MELEE_ATTACK2,
|
|
ACT_RELOAD,
|
|
ACT_ARM,
|
|
ACT_DISARM,
|
|
ACT_EAT,
|
|
ACT_DIESIMPLE,
|
|
ACT_DIEBACKWARD,
|
|
ACT_DIEFORWARD,
|
|
ACT_DIEVIOLENT,
|
|
ACT_BARNACLE_HIT,
|
|
ACT_BARNACLE_PULL,
|
|
ACT_BARNACLE_CHOMP,
|
|
ACT_BARNACLE_CHEW,
|
|
ACT_SLEEP,
|
|
ACT_INSPECT_FLOOR,
|
|
ACT_INSPECT_WALL,
|
|
ACT_IDLE_ANGRY,
|
|
ACT_WALK_HURT,
|
|
ACT_RUN_HURT,
|
|
ACT_HOVER,
|
|
ACT_GLIDE,
|
|
ACT_FLY_LEFT,
|
|
ACT_FLY_RIGHT,
|
|
ACT_DETECT_SCENT,
|
|
ACT_SNIFF,
|
|
ACT_BITE,
|
|
ACT_THREAT_DISPLAY,
|
|
ACT_FEAR_DISPLAY,
|
|
ACT_EXCITED,
|
|
ACT_SPECIAL_ATTACK1,
|
|
ACT_SPECIAL_ATTACK2,
|
|
ACT_COMBAT_IDLE,
|
|
ACT_WALK_SCARED,
|
|
ACT_RUN_SCARED,
|
|
ACT_VICTORY_DANCE,
|
|
ACT_DIE_HEADSHOT,
|
|
ACT_DIE_CHESTSHOT,
|
|
ACT_DIE_GUTSHOT,
|
|
ACT_DIE_BACKSHOT,
|
|
ACT_FLINCH_HEAD,
|
|
ACT_FLINCH_CHEST,
|
|
ACT_FLINCH_STOMACH,
|
|
ACT_FLINCH_LEFTARM,
|
|
ACT_FLINCH_RIGHTARM,
|
|
ACT_FLINCH_LEFTLEG,
|
|
ACT_FLINCH_RIGHTLEG,
|
|
} monster_activity_t;
|
|
|
|
/** Monster flags, these are defined by the level designers. */
|
|
typedef enumflags
|
|
{
|
|
MSF_WAITTILLSEEN,
|
|
MSF_GAG,
|
|
MSF_MONSTERCLIP,
|
|
MSF_RESERVED1,
|
|
MSF_PRISONER,
|
|
MSF_RESERVED2,
|
|
MSF_IGNOREPLAYER,
|
|
MSF_WAITFORSCRIPT,
|
|
MSF_PREDISASTER,
|
|
MSF_FADECORPSE,
|
|
MSF_MULTIPLAYER,
|
|
MSF_FALLING,
|
|
MSF_HORDE
|
|
} monsterFlag_t;
|
|
|
|
/** Behaviour states. */
|
|
typedef enum
|
|
{
|
|
MONSTER_IDLE,
|
|
MONSTER_ALERT,
|
|
MONSTER_FOLLOWING,
|
|
MONSTER_CHASING,
|
|
MONSTER_AIMING,
|
|
MONSTER_DEAD,
|
|
MONSTER_GIBBED
|
|
} monsterState_t;
|
|
|
|
/** Scripted sequence states. */
|
|
typedef enum
|
|
{
|
|
SEQUENCESTATE_NONE,
|
|
SEQUENCESTATE_IDLE,
|
|
SEQUENCESTATE_ACTIVE,
|
|
SEQUENCESTATE_ENDING
|
|
} sequenceState_t;
|
|
|
|
/** Alliance states. */
|
|
typedef enum
|
|
{
|
|
MAL_FRIEND, /* friendly towards the player */
|
|
MAL_ENEMY, /* unfriendly towards the player */
|
|
MAL_ALIEN, /* unfriendly towards anyone but themselves */
|
|
MAL_ROGUE /* no allies, not even amongst themselves */
|
|
} allianceState_t;
|
|
|
|
/** Movement states */
|
|
typedef enum
|
|
{
|
|
MOVESTATE_IDLE,
|
|
MOVESTATE_WALK,
|
|
MOVESTATE_RUN
|
|
} movementState_t;
|
|
|
|
/** These numerations involve the m_iTriggerCondition attribute.
|
|
Basically these conditions are being checked and triggered depending on what
|
|
it's set to. If any of those checks are successful, we trigger our target
|
|
under the m_strTriggerTarget attribute. */
|
|
typedef enum
|
|
{
|
|
MTRIG_NONE, /**< nothing */
|
|
MTRIG_SEEPLAYER_ANGRY, /**< we see an enemy player, that we want to harm */
|
|
MTRIG_PAIN, /**< taken damage */
|
|
MTRIG_HALFHEALTH, /**< lost half of our base_health */
|
|
MTRIG_DEATH, /**< we have died. */
|
|
MTRIG_SQUADMEMBERDEAD, /**< a squad member died */
|
|
MTRIG_SQUADLEADERDEAD, /**< the squad leader died */
|
|
MTRIG_HEARNOISE, /**< we hear some noise around the world. */
|
|
MTRIG_HEARENEMYPLAYER, /**< we hear a player we are enemies with */
|
|
MTRIG_HEARWEAPONS, /**< we hear weapons being fired */
|
|
MTRIG_SEEPLAYER, /**< we see a player, don't have to be angry at him. */
|
|
MTRIG_SEEPLAYER_RELAXED, /**< we see a player and we're currently attacking anything */
|
|
} triggerCondition_t;
|
|
|
|
/* FIXME: I'd like to move this into NSMonster, but our current IsFriend()
|
|
* check is currently only checking on a .takedamage basis. */
|
|
.int m_iAlliance;
|
|
|
|
/** This entity class represents non-player characters.
|
|
They have the ability to move around (or stand still) but are all
|
|
capable of fighting if prompted to.
|
|
|
|
There are a few methods that you need to reimplement in order for them
|
|
to do some basic combat:
|
|
|
|
virtual void(void) AttackDraw;
|
|
virtual void(void) AttackHolster;
|
|
virtual int(void) AttackMelee;
|
|
virtual int(void) AttackRanged;
|
|
|
|
Check their individual descriptions as to how you're supposed to approach them.
|
|
*/
|
|
class NSMonster:NSNavAI
|
|
{
|
|
public:
|
|
void NSMonster(void);
|
|
|
|
#ifdef SERVER
|
|
/* overrides */
|
|
virtual void Save(float);
|
|
virtual void Restore(string,string);
|
|
virtual void EvaluateEntity(void);
|
|
virtual float SendEntity(entity,float);
|
|
virtual void Touch(entity);
|
|
//virtual void Hide(void);
|
|
virtual void Respawn(void);
|
|
virtual void Pain(void);
|
|
virtual void Death(void);
|
|
virtual void Physics(void);
|
|
virtual void Gib(void);
|
|
virtual void Sound(string);
|
|
virtual void SpawnKey(string,string);
|
|
|
|
/** Internal use only.
|
|
Run every frame to go through the main AI loop. */
|
|
virtual void RunAI(void);
|
|
/** Overridable: Called after a while when they've got nothing to do. */
|
|
virtual void IdleNoise(void);
|
|
/** Overridable: Called when they start falling. */
|
|
virtual void FallNoise(void);
|
|
/** Overridable: Called when this monster gets 'alerted' to something new. */
|
|
virtual void AlertNoise(void);
|
|
|
|
/** Returns if they're considered alive. */
|
|
virtual bool IsAlive(void);
|
|
/** Returns whether they are allied with the type in question */
|
|
virtual bool IsFriend(int);
|
|
/** Overridable: Called once, when the monster has died. */
|
|
virtual void HasBeenKilled(void);
|
|
/** Overridable: Called every time the monster is hurt, while still alive. */
|
|
virtual void HasBeenHit(void);
|
|
/* Overridable: Called when the monster was gibbed. */
|
|
virtual void HasBeenGibbed(void);
|
|
/* Overridable: Called when the monster has been alerted to threat. */
|
|
virtual void HasBeenAlerted(void);
|
|
|
|
/* see/hear subsystem */
|
|
/** Internal use only. Called every frame to simulate vision. */
|
|
virtual void SeeThink(void);
|
|
/** Overridable: Returns the field of view in degrees. */
|
|
virtual float SeeFOV(void);
|
|
|
|
/** FIXME: Same as WarnAllies/StartleAllies? WTF? */
|
|
virtual void AlertNearby(void);
|
|
|
|
/* movement */
|
|
/** Overridable: Returns the walking speed in Quake units per second. */
|
|
virtual float GetWalkSpeed(void);
|
|
/** Overridable: Returns the chase speed in Quake units per second. */
|
|
virtual float GetChaseSpeed(void);
|
|
/** Overridable: Returns the running speed in Quake units per second. */
|
|
virtual float GetRunSpeed(void);
|
|
/** Overridable: Returns the turning speed in euler-angle units per second. */
|
|
virtual float GetYawSpeed(void);
|
|
|
|
/* attack system */
|
|
/** Overridable: Called when they're drawing a weapon. */
|
|
virtual void AttackDraw(void);
|
|
/** Overridable: Called when they're holstering a weapon. */
|
|
virtual void AttackHolster(void);
|
|
/** Overridable: Called when aiming their weapon. */
|
|
virtual void AttackThink(void);
|
|
/** Overridable: Called when attempting to melee attack. Return 0 if impossible. */
|
|
virtual int AttackMelee(void);
|
|
/** Overridable: Called when attempting to attack from a distance. Return 0 if impossible. */
|
|
virtual int AttackRanged(void);
|
|
|
|
/** Overridable: Returns the distance in qu of what'll be a successfull melee attack. */
|
|
virtual float MeleeMaxDistance(void);
|
|
|
|
/** Returns whether or not we should attempt a melee attack.
|
|
FIXME: Should pass a parameter for the enemy in question instead! */
|
|
virtual bool MeleeCondition(void);
|
|
|
|
/** Returns TRUE if 'enemy' should be considered a valid target for killing */
|
|
nonvirtual bool IsValidEnemy(entity);
|
|
|
|
/* sequences */
|
|
/** Internal use only. Called when a sequence is done. */
|
|
virtual void FreeState(void);
|
|
/** Internal use only. Called when a sequence is done. */
|
|
virtual void FreeStateMoved(void);
|
|
/** Internal use only. Called when a movement route is done. */
|
|
virtual void RouteEnded(void);
|
|
/** Internal use only. Called every frame to progress through a route. */
|
|
virtual void WalkRoute(void);
|
|
|
|
/** Returns the type of sequence they're currently in. */
|
|
nonvirtual int GetSequenceState(void);
|
|
/** Returns if they're currently in a scripted sequence. */
|
|
nonvirtual bool InSequence(void);
|
|
|
|
/* animation cycles */
|
|
/** DEPRECATED, Overridable: Called when we need to play a fresh idle framegroup. */
|
|
virtual int AnimIdle(void);
|
|
/** DEPRECATED, Overridable: Called when we need to play a fresh walking framegroup. */
|
|
virtual int AnimWalk(void);
|
|
/** DEPRECATED, Overridable: Called when we need to play a fresh running framegroup. */
|
|
virtual int AnimRun(void);
|
|
/** Overridable: Returns which framegroup to play for a given ACT. */
|
|
virtual float FramegroupForAct(float);
|
|
/** Call to play an ACT on the given NSMonster. */
|
|
nonvirtual void ActPlay(float);
|
|
/** Call to play a single animation onto it, which cannot be interrupted by movement. */
|
|
virtual void AnimPlay(float);
|
|
/** Internal use only. Run every frame to update animation parameters. */
|
|
virtual void AnimationUpdate(void);
|
|
/** Returns if we're currently in a forced animation sequence. */
|
|
nonvirtual bool InAnimation(void);
|
|
|
|
/* states */
|
|
/** Called whenever the state of this NSMonster changes. */
|
|
virtual void StateChanged(monsterState_t,monsterState_t);
|
|
/** Sets the current state of this NSMonster. */
|
|
nonvirtual void SetState(monsterState_t);
|
|
/** Returns the current state of this NSMonster. */
|
|
nonvirtual monsterState_t GetState(void);
|
|
|
|
/* TriggerTarget/Condition */
|
|
/** Returns the condition under which they'll trigger their targets. */
|
|
nonvirtual int GetTriggerCondition(void);
|
|
/** Call to trigger their targets manually. */
|
|
virtual void TriggerTargets(void);
|
|
#endif
|
|
|
|
#ifdef CLIENT
|
|
|
|
/** overrides */
|
|
virtual void customphysics(void);
|
|
virtual float predraw(void);
|
|
virtual void ReceiveEntity(float,float);
|
|
#endif
|
|
|
|
private:
|
|
|
|
vector v_angle_net;
|
|
|
|
#ifdef CLIENT
|
|
nonvirtual void _RenderDebugViewCone();
|
|
#endif
|
|
|
|
PREDICTED_FLOAT(m_flHeadYaw)
|
|
PREDICTED_FLOAT_N(frame1time)
|
|
PREDICTED_FLOAT_N(subblendfrac)
|
|
PREDICTED_FLOAT_N(bonecontrol1)
|
|
|
|
#ifdef SERVER
|
|
entity m_eLookAt;
|
|
entity m_ssLast;
|
|
vector oldnet_velocity;
|
|
float m_flPitch;
|
|
int m_iFlags;
|
|
vector base_mins;
|
|
vector base_maxs;
|
|
float base_health;
|
|
|
|
/* sequences */
|
|
string m_strRouteEnded;
|
|
int m_iSequenceRemove;
|
|
int m_iSequenceState;
|
|
float m_flSequenceEnd;
|
|
float m_flSequenceSpeed;
|
|
vector m_vecSequenceAngle;
|
|
int m_iSequenceFlags;
|
|
movementState_t m_iMoveState;
|
|
|
|
int m_iTriggerCondition;
|
|
string m_strTriggerTarget;
|
|
|
|
/* model events */
|
|
float m_flBaseTime;
|
|
|
|
/* attack/alliance system */
|
|
entity m_eEnemy;
|
|
float m_flAttackThink;
|
|
monsterState_t m_iMState;
|
|
monsterState_t m_iOldMState;
|
|
vector m_vecLKPos; /* last-known pos */
|
|
|
|
/* see/hear subsystem */
|
|
float m_flSeeTime;
|
|
/* animation cycles */
|
|
float m_flAnimTime;
|
|
|
|
/* timer for keeping track of the target */
|
|
float m_flTrackingTime;
|
|
|
|
PREDICTED_VECTOR_N(view_ofs)
|
|
|
|
/* caching variables, don't save these */
|
|
float m_actIdle;
|
|
bool m_bTurning;
|
|
float m_flIdleNext;
|
|
float _m_flMeleeAttempts;
|
|
float _m_flMeleeDelay;
|
|
float _m_flBurstCount;
|
|
bool _m_bShouldThrow;
|
|
|
|
/* save these please */
|
|
float _m_flReloadTracker;
|
|
bool m_bWeaponDrawn;
|
|
|
|
/* entityDef related */
|
|
float m_flEyeHeight;
|
|
string m_sndSight;
|
|
string m_sndIdle;
|
|
float m_flIdleMin;
|
|
float m_flIdleMax;
|
|
string m_sndFootstep;
|
|
string m_sndChatter;
|
|
string m_sndChatterCombat;
|
|
string m_sndPain;
|
|
|
|
string m_sndMeleeAttack;
|
|
string m_sndMeleeAttackHit;
|
|
string m_sndMeleeAttackMiss;
|
|
|
|
string m_sndDeath;
|
|
string m_sndThud;
|
|
|
|
/* attack definitions, if defined will fire projectiles */
|
|
string m_defSpecial1;
|
|
float m_flSpecial1Range;
|
|
string m_defSpecial2;
|
|
float m_flSpecial2Range;
|
|
string m_defRanged1;
|
|
float m_flRanged1Range;
|
|
string m_defRanged2;
|
|
float m_flRanged2Range;
|
|
|
|
/* ranged1 only */
|
|
int m_iNumProjectiles;
|
|
float m_flProjectileDelay;
|
|
float m_flProjectileSpread;
|
|
|
|
/* general */
|
|
float m_flAttackCone;
|
|
float m_flAttackAccuracy;
|
|
|
|
/* melee attack */
|
|
string m_defMelee;
|
|
float m_flMeleeRange;
|
|
|
|
string m_sndRangedAttack;
|
|
float m_flReloadCount;
|
|
float m_flReloadDelay;
|
|
string m_sndReload;
|
|
|
|
string m_sndRangedAttack2;
|
|
|
|
bool m_bWeaponStartsDrawn;
|
|
string m_strBodyOnDraw;
|
|
|
|
float m_flWalkSpeed;
|
|
float m_flRunSpeed;
|
|
|
|
nonvirtual void _LerpTurnToEnemy(void);
|
|
nonvirtual void _LerpTurnToPos(vector);
|
|
nonvirtual void _LerpTurnToYaw(vector);
|
|
virtual void _Alerted(void);
|
|
#endif
|
|
};
|
|
|
|
#ifdef CLIENT
|
|
string Sentences_GetSamples(string);
|
|
string Sentences_ProcessSample(string);
|
|
void NSMonster_ReadEntity(bool);
|
|
#endif
|
|
|
|
#ifdef SERVER
|
|
void NSMonster_AlertEnemyAlliance(vector pos, float radius, int alliance);
|
|
entity NSMonster_FindClosestPlayer(entity);
|
|
#endif
|
|
|
|
.float baseframe2;
|
|
.float baseframe1time;
|
|
.float baseframe2time;
|
|
.float baselerpfrac;
|
|
.float bonecontrol1;
|
|
.float bonecontrol2;
|
|
.float bonecontrol3;
|
|
.float bonecontrol4;
|
|
.float bonecontrol5;
|
|
.float subblendfrac;
|
|
.float subblend2frac;
|
|
.float basesubblendfrac;
|
|
.float basesubblend2frac;
|