NSMonster: Initial work towards reworking states.
This commit is contained in:
parent
725a32a4d6
commit
6c895d73b7
6 changed files with 145 additions and 47 deletions
25
src/gs-entbase/server/info_node.qc
Normal file
25
src/gs-entbase/server/info_node.qc
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*QUAKED info_node (0 0 0) (-8 -8 -8) (8 8 8)
|
||||
It's a node, helping monsters navigate on the ground.
|
||||
|
||||
-------- KEYS --------
|
||||
"targetname" : Name
|
||||
|
||||
-------- TRIVIA --------
|
||||
This entity was introduced in Half-Life (1998).
|
||||
*/
|
|
@ -110,18 +110,19 @@ typedef enumflags
|
|||
MSF_MULTIPLAYER,
|
||||
MSF_FALLING,
|
||||
MSF_HORDE
|
||||
} monsterFlag_t;
|
||||
} monsterFlag_e;
|
||||
|
||||
/* movement states */
|
||||
typedef enum
|
||||
{
|
||||
MONSTER_IDLE,
|
||||
MONSTER_ALERT,
|
||||
MONSTER_FOLLOWING,
|
||||
MONSTER_CHASING,
|
||||
MONSTER_AIMING,
|
||||
MONSTER_DEAD,
|
||||
MONSTER_GIBBED
|
||||
} monsterState_t;
|
||||
} monsterState_e;
|
||||
|
||||
/* scripted sequence states */
|
||||
typedef enum
|
||||
|
@ -130,7 +131,7 @@ typedef enum
|
|||
SEQUENCESTATE_IDLE,
|
||||
SEQUENCESTATE_ACTIVE,
|
||||
SEQUENCESTATE_ENDING
|
||||
} sequenceState_t;
|
||||
} sequenceState_e;
|
||||
|
||||
/* alliance state */
|
||||
typedef enum
|
||||
|
@ -139,14 +140,14 @@ typedef enum
|
|||
MAL_ENEMY, /* unfriendly towards the player */
|
||||
MAL_ALIEN, /* unfriendly towards anyone but themselves */
|
||||
MAL_ROGUE /* no allies, not even amongst themselves */
|
||||
} allianceState_t;
|
||||
} allianceState_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MOVESTATE_IDLE,
|
||||
MOVESTATE_WALK,
|
||||
MOVESTATE_RUN
|
||||
} movementState_t;
|
||||
} movementState_e;
|
||||
|
||||
/* These numerations involve the m_iTriggerCondition attribute.
|
||||
* Basically these conditions are being checked and triggered depending on what
|
||||
|
@ -166,7 +167,7 @@ typedef enum
|
|||
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;
|
||||
} triggerCondition_e;
|
||||
|
||||
/* FIXME: I'd like to move this into NSMonster, but our current IsFriend()
|
||||
* check is currently only checking on a .takedamage basis. */
|
||||
|
@ -192,7 +193,7 @@ class NSMonster:NSSurfacePropEntity
|
|||
vector m_vecSequenceAngle;
|
||||
vector m_vecTurnAngle;
|
||||
int m_iSequenceFlags;
|
||||
movementState_t m_iMoveState;
|
||||
movementState_e m_iMoveState;
|
||||
|
||||
int m_iTriggerCondition;
|
||||
string m_strTriggerTarget;
|
||||
|
@ -203,7 +204,8 @@ class NSMonster:NSSurfacePropEntity
|
|||
/* attack/alliance system */
|
||||
entity m_eEnemy;
|
||||
float m_flAttackThink;
|
||||
int m_iMState;
|
||||
monsterState_e m_iMState;
|
||||
monsterState_e m_iOldMState;
|
||||
vector m_vecLKPos; /* last-known pos */
|
||||
|
||||
/* pathfinding */
|
||||
|
@ -233,6 +235,9 @@ class NSMonster:NSSurfacePropEntity
|
|||
virtual void(string) Sound;
|
||||
virtual void(string, string) SpawnKey;
|
||||
|
||||
virtual bool(void) IsAlive;
|
||||
virtual bool(int) IsFriend;
|
||||
|
||||
/* see/hear subsystem */
|
||||
float m_flSeeTime;
|
||||
virtual void(void) SeeThink;
|
||||
|
@ -274,6 +279,11 @@ class NSMonster:NSSurfacePropEntity
|
|||
virtual void(float) AnimPlay;
|
||||
virtual void(void) AnimationUpdate;
|
||||
|
||||
/* states */
|
||||
virtual void(monsterState_e, monsterState_e) StateChanged;
|
||||
virtual void(monsterState_e) SetState;
|
||||
virtual monsterState_e(void) GetState;
|
||||
|
||||
/* TriggerTarget/Condition */
|
||||
virtual int(void) GetTriggerCondition;
|
||||
virtual void(void) TriggerTargets;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
const int CONTENTBITS_MONSTER = CONTENTBIT_SOLID|CONTENTBIT_BODY|CONTENTBIT_MONSTERCLIP|CONTENTBIT_BOTCLIP;
|
||||
|
||||
#ifdef SERVER
|
||||
int
|
||||
NSMonster::GetTriggerCondition(void)
|
||||
|
@ -113,15 +115,15 @@ NSMonster::IdleNoise(void)
|
|||
{
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
NSMonster::IsFriend(int al)
|
||||
{
|
||||
if (m_iAlliance == MAL_ROGUE)
|
||||
return (0);
|
||||
return (false);
|
||||
else if (al == m_iAlliance)
|
||||
return (1);
|
||||
return (true);
|
||||
|
||||
return (0);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* The maximum distance to which we should attempt an attack */
|
||||
|
@ -201,8 +203,9 @@ NSMonster::SeeThink(void)
|
|||
/* check if we should invalidate current enemy */
|
||||
if (IsValidEnemy(m_eEnemy))
|
||||
return;
|
||||
|
||||
/* enemy is not valid anymore, reset it, clear route and search for new enemy */
|
||||
m_iMState = MONSTER_IDLE;
|
||||
SetState(MONSTER_ALERT);
|
||||
m_eEnemy = __NULL__;
|
||||
ClearRoute();
|
||||
m_flSeeTime = 0;
|
||||
|
@ -286,7 +289,7 @@ NSMonster::AttackThink(void)
|
|||
|
||||
/* something is blocking us */
|
||||
if (trace_fraction < 1.0f) {
|
||||
m_iMState = MONSTER_IDLE;
|
||||
SetState(MONSTER_ALERT);
|
||||
|
||||
/* FIXME: This is unreliable, but unlikely that a player ever is here */
|
||||
if (m_vecLKPos != [0,0,0]) {
|
||||
|
@ -296,13 +299,17 @@ NSMonster::AttackThink(void)
|
|||
m_vecLKPos = [0,0,0];
|
||||
}
|
||||
} else {
|
||||
m_iMState = MONSTER_AIMING;
|
||||
SetState(MONSTER_AIMING);
|
||||
|
||||
/* make sure we remember the last known position. */
|
||||
m_vecLKPos = m_eEnemy.origin;
|
||||
}
|
||||
|
||||
if (m_iMState == MONSTER_AIMING) {
|
||||
/* the state may have switched */
|
||||
if (m_flAttackThink > time)
|
||||
return;
|
||||
|
||||
if (GetState() == MONSTER_AIMING) {
|
||||
int m;
|
||||
if (MeleeCondition() == TRUE)
|
||||
m = AttackMelee();
|
||||
|
@ -311,7 +318,7 @@ NSMonster::AttackThink(void)
|
|||
|
||||
/* if we don't have the desired attack mode, walk */
|
||||
if (m == FALSE)
|
||||
m_iMState = MONSTER_CHASING;
|
||||
SetState(MONSTER_CHASING);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -508,12 +515,12 @@ NSMonster::WalkRoute(void)
|
|||
vector endangles;
|
||||
|
||||
/* we're busy shooting at something, don't walk */
|
||||
if (m_iMState == MONSTER_AIMING && m_eEnemy) {
|
||||
if (GetState() == MONSTER_AIMING && m_eEnemy) {
|
||||
endangles = vectoangles(m_eEnemy.origin - origin);
|
||||
|
||||
/* TODO: lerp */
|
||||
m_vecTurnAngle[1] = endangles[1];
|
||||
} else if (m_iNodes && m_iMState == MONSTER_IDLE) {
|
||||
} else if (m_iNodes && GetState() == MONSTER_IDLE) {
|
||||
/* we're on our last node */
|
||||
if (m_iCurNode < 0) {
|
||||
endangles = vectoangles(m_vecLastNode - origin);
|
||||
|
@ -522,7 +529,7 @@ NSMonster::WalkRoute(void)
|
|||
}
|
||||
m_vecTurnAngle[1] = endangles[1];
|
||||
input_movevalues = [m_flSequenceSpeed, 0, 0];
|
||||
} else if (m_iMState == MONSTER_CHASING && m_eEnemy) {
|
||||
} else if (GetState() == MONSTER_CHASING && m_eEnemy) {
|
||||
/* we've got 'em in our sights, just need to walk closer */
|
||||
endangles = vectoangles(m_eEnemy.origin - origin);
|
||||
input_movevalues = [GetChaseSpeed(), 0, 0];
|
||||
|
@ -543,9 +550,9 @@ NSMonster::WalkRoute(void)
|
|||
makevectors(angles);
|
||||
old_ang = v_forward;
|
||||
|
||||
tmp[0] = Math_Lerp(old_ang[0], new_ang[0], frametime * 5);
|
||||
tmp[0] = 0;
|
||||
tmp[1] = Math_Lerp(old_ang[1], new_ang[1], frametime * 5);
|
||||
tmp[2] = Math_Lerp(old_ang[2], new_ang[2], frametime * 5);
|
||||
tmp[2] = 0;
|
||||
angles = vectoangles(tmp);
|
||||
#endif
|
||||
}
|
||||
|
@ -604,7 +611,7 @@ NSMonster::AnimationUpdate(void)
|
|||
int fr = 0;
|
||||
int act = 0;
|
||||
|
||||
if (style == MONSTER_DEAD)
|
||||
if (GetState() == MONSTER_DEAD)
|
||||
return;
|
||||
|
||||
float spvel = vlen(velocity);
|
||||
|
@ -652,10 +659,41 @@ NSMonster::AnimationUpdate(void)
|
|||
AnimPlay(act);
|
||||
else
|
||||
SetFrame(fr);
|
||||
|
||||
}
|
||||
|
||||
const int CONTENTBITS_MONSTER = CONTENTBIT_SOLID|CONTENTBIT_BODY|CONTENTBIT_MONSTERCLIP|CONTENTBIT_BOTCLIP;
|
||||
/* for an NSMonster, health doesn't matter that much, as we could be a corpse */
|
||||
bool
|
||||
NSMonster::IsAlive(void)
|
||||
{
|
||||
if (GetState() == MONSTER_DEAD)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NSMonster::StateChanged(monsterState_e oldState, monsterState_e newState)
|
||||
{
|
||||
print(sprintf("%s state changed from %d to %d\n", classname, oldState, newState));
|
||||
}
|
||||
|
||||
void
|
||||
NSMonster::SetState(monsterState_e newState)
|
||||
{
|
||||
if (newState == m_iMState)
|
||||
return;
|
||||
|
||||
m_iOldMState = m_iMState;
|
||||
m_iMState = newState;
|
||||
StateChanged(m_iOldMState, m_iMState);
|
||||
}
|
||||
|
||||
monsterState_e
|
||||
NSMonster::GetState(void)
|
||||
{
|
||||
return m_iMState;
|
||||
}
|
||||
|
||||
void PMoveCustom_RunPlayerPhysics(entity target);
|
||||
void PMoveCustom_RunCrouchPhysics(entity target);
|
||||
void
|
||||
|
@ -733,9 +771,26 @@ NSMonster::Touch(entity eToucher)
|
|||
void
|
||||
NSMonster::Pain(void)
|
||||
{
|
||||
/* dead things tell nuthin */
|
||||
if (IsAlive() == false)
|
||||
return;
|
||||
|
||||
if (GetHealth() <= (base_health / 2)) {
|
||||
if (IsFriend(g_dmg_eAttacker.m_iAlliance) == true)
|
||||
m_iAlliance = MAL_ROGUE;
|
||||
}
|
||||
|
||||
if (IsFriend(g_dmg_eAttacker.m_iAlliance) == true)
|
||||
return;
|
||||
|
||||
/* if don't have an enemy, set one; else make it random */
|
||||
if (!m_eEnemy || (random() < 0.5))
|
||||
m_eEnemy = g_dmg_eAttacker;
|
||||
|
||||
/* an alert monster will take a while to calm back down */
|
||||
SetState(MONSTER_ALERT);
|
||||
|
||||
/* alert all nearby friendlies */
|
||||
AlertNearby();
|
||||
}
|
||||
|
||||
|
@ -743,7 +798,7 @@ void
|
|||
NSMonster::Death(void)
|
||||
{
|
||||
/* we were already dead before, so gib */
|
||||
if (style == MONSTER_DEAD) {
|
||||
if (GetState() == MONSTER_DEAD) {
|
||||
Gib();
|
||||
return;
|
||||
}
|
||||
|
@ -751,21 +806,21 @@ NSMonster::Death(void)
|
|||
m_iFlags = 0x0;
|
||||
|
||||
/* if we make more than 50 damage, gib immediately */
|
||||
if (health < -50) {
|
||||
if (GetHealth() < -50) {
|
||||
Gib();
|
||||
return;
|
||||
}
|
||||
|
||||
/* make sure we're not causing any more obituaries */
|
||||
flags &= ~FL_MONSTER;
|
||||
m_iFlags = 0x0;
|
||||
RemoveFlags(FL_MONSTER);
|
||||
|
||||
/* gibbing action */
|
||||
/* set the monster up for getting gibbed */
|
||||
SetMovetype(MOVETYPE_NONE);
|
||||
SetSolid(SOLID_CORPSE);
|
||||
health = 50 + health;
|
||||
style = MONSTER_DEAD;
|
||||
SetHealth(50 + GetHealth());
|
||||
SetState(MONSTER_DEAD);
|
||||
|
||||
/* monsters trigger their targets when dead */
|
||||
if (GetTriggerCondition() == MTRIG_DEATH)
|
||||
TriggerTargets();
|
||||
}
|
||||
|
@ -786,16 +841,16 @@ NSMonster::Respawn(void)
|
|||
v_angle[0] = Math_FixDelta(v_angle[0]);
|
||||
v_angle[1] = Math_FixDelta(v_angle[1]);
|
||||
v_angle[2] = Math_FixDelta(v_angle[2]);
|
||||
flags |= FL_MONSTER;
|
||||
takedamage = DAMAGE_YES;
|
||||
|
||||
AddFlags(FL_MONSTER);
|
||||
SetTakedamage(DAMAGE_YES);
|
||||
SetVelocity([0,0,0]);
|
||||
SetState(MONSTER_IDLE);
|
||||
SetHealth(base_health);
|
||||
m_eEnemy = __NULL__;
|
||||
m_iFlags = 0x0;
|
||||
iBleeds = TRUE;
|
||||
customphysics = Physics;
|
||||
velocity = [0,0,0];
|
||||
m_iFlags = 0x0;
|
||||
SendFlags = 0xff;
|
||||
style = MONSTER_IDLE;
|
||||
health = base_health;
|
||||
m_eEnemy = __NULL__;
|
||||
|
||||
SetAngles(v_angle);
|
||||
SetSolid(SOLID_SLIDEBOX);
|
||||
|
@ -804,7 +859,7 @@ NSMonster::Respawn(void)
|
|||
SetSize(base_mins, base_maxs);
|
||||
SetOrigin(GetSpawnOrigin());
|
||||
|
||||
droptofloor();
|
||||
DropToFloor();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1109,6 +1164,7 @@ NSMonster_AlertEnemyAlliance(vector pos, float radius, int alliance)
|
|||
/* if they're our friend... ignore*/
|
||||
if (f.IsFriend(alliance))
|
||||
continue;
|
||||
|
||||
/* if the monster is dead... ignore */
|
||||
if (f.health <= 0)
|
||||
continue;
|
||||
|
|
|
@ -79,6 +79,7 @@ class NSSurfacePropEntity:NSRenderableEntity
|
|||
float m_oldHealth;
|
||||
virtual void(void) Pain;
|
||||
virtual void(void) Death;
|
||||
virtual bool(void) IsAlive;
|
||||
|
||||
/* Generic Damage */
|
||||
nonvirtual void(float) SetTakedamage;
|
||||
|
|
|
@ -33,6 +33,12 @@ NSSurfacePropEntity::Spawned(void)
|
|||
/* networking */
|
||||
#ifdef SERVER
|
||||
|
||||
bool
|
||||
NSSurfacePropEntity::IsAlive(void)
|
||||
{
|
||||
return (health > 0) ? true : false;
|
||||
}
|
||||
|
||||
#if INDEV
|
||||
typedef enum
|
||||
{
|
||||
|
|
|
@ -139,7 +139,7 @@ NSTalkMonster::StartleAllies(void)
|
|||
void
|
||||
NSTalkMonster::Sentence(string sentence)
|
||||
{
|
||||
if (style == MONSTER_DEAD)
|
||||
if (GetState() == MONSTER_DEAD)
|
||||
return;
|
||||
|
||||
string seq = Sentences_GetSamples(sentence);
|
||||
|
@ -158,7 +158,7 @@ NSTalkMonster::Sentence(string sentence)
|
|||
void
|
||||
NSTalkMonster::Speak(string sentence)
|
||||
{
|
||||
if (style == MONSTER_DEAD)
|
||||
if (GetState() == MONSTER_DEAD)
|
||||
return;
|
||||
|
||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||
|
@ -463,7 +463,7 @@ NSTalkMonster::Physics(void)
|
|||
/* make sure we're forgetting about enemies and attack states in sequence */
|
||||
if (m_iSequenceState != SEQUENCESTATE_NONE) {
|
||||
m_eEnemy = __NULL__;
|
||||
m_iMState = MONSTER_IDLE;
|
||||
SetState(MONSTER_IDLE);
|
||||
}
|
||||
|
||||
/* override whatever we did above with this */
|
||||
|
@ -471,7 +471,7 @@ NSTalkMonster::Physics(void)
|
|||
input_angles = v_angle = angles = m_vecSequenceAngle;
|
||||
SetFrame(m_flSequenceEnd);
|
||||
} else {
|
||||
if (style != MONSTER_DEAD) {
|
||||
if (GetState() != MONSTER_DEAD) {
|
||||
if (m_iSequenceState == SEQUENCESTATE_NONE) {
|
||||
SeeThink();
|
||||
AttackThink();
|
||||
|
@ -612,9 +612,9 @@ NSTalkMonster::SendEntity(entity ePEnt, float fChanged)
|
|||
WriteCoord(MSG_ENTITY, origin[2]);
|
||||
}
|
||||
if (fChanged & BASEFL_CHANGED_ANGLES) {
|
||||
WriteShort(MSG_ENTITY, angles[0] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, 0);
|
||||
WriteShort(MSG_ENTITY, angles[1] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, angles[2] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, 0);
|
||||
}
|
||||
if (fChanged & BASEFL_CHANGED_MODELINDEX) {
|
||||
WriteShort(MSG_ENTITY, modelindex);
|
||||
|
|
Loading…
Reference in a new issue