nuclide/src/shared/NSMonster.h

436 lines
12 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,
} 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 */
/** Overridable: Called when we need to play a fresh idle framegroup. */
virtual int AnimIdle(void);
/** Overridable: Called when we need to play a fresh walking framegroup. */
virtual int AnimWalk(void);
/** Overridable: Called when we need to play a fresh running framegroup. */
virtual int AnimRun(void);
/** 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:
#ifdef CLIENT
nonvirtual void _RenderDebugViewCone();
#endif
#ifdef SERVER
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;
nonvirtual void _LerpTurnToEnemy(void);
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