NSMonster/NSTalkMonster: more reshuffling done. Started moving the pathfinding components over into a new class named NSNavAI
This commit is contained in:
parent
b275275652
commit
4566d07313
12 changed files with 427 additions and 318 deletions
|
@ -11,6 +11,7 @@ server/info_null.qc
|
|||
server/info_notnull.qc
|
||||
server/info_intermission.qc
|
||||
server/info_node.qc
|
||||
server/info_node_air.qc
|
||||
server/info_hint.qc
|
||||
server/button_target.qc
|
||||
server/cycler.qc
|
||||
|
|
|
@ -74,9 +74,9 @@ The 'IgnoreFacing' field can be one of 3 values:
|
|||
|
||||
The 'MinimumState' and 'MaximumState' field can be one of 3 values:
|
||||
|
||||
AISTATE_IDLE = 0
|
||||
AISTATE_ALERT = 1
|
||||
AISTATE_COMBAT = 2
|
||||
AISTATE_IDLE = 0
|
||||
AISTATE_ALERT = 1
|
||||
AISTATE_COMBAT = 2
|
||||
|
||||
-------- TRIVIA --------
|
||||
This entity was introduced in Half-Life 2 (2004).
|
||||
|
@ -157,7 +157,6 @@ info_hint:NSPointTrigger
|
|||
virtual void(string, string) SpawnKey;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
info_hint::Save(float handle)
|
||||
{
|
||||
|
|
|
@ -24,4 +24,30 @@ It's a node, helping monsters navigate on the ground.
|
|||
This entity was introduced in Half-Life (1998).
|
||||
*/
|
||||
|
||||
CLASSEXPORT(info_node, info_notnull)
|
||||
class
|
||||
info_node:NSPointTrigger
|
||||
{
|
||||
void(void) info_node;
|
||||
|
||||
virtual void(void) Respawn;
|
||||
};
|
||||
|
||||
void
|
||||
info_node::Respawn(void)
|
||||
{
|
||||
SetSize([0,0,0], [0,0,0]);
|
||||
SetSolid(SOLID_NOT);
|
||||
SetMovetype(MOVETYPE_NONE);
|
||||
|
||||
/* drop to floor, then 32-units above ground
|
||||
see https://twhl.info/wiki/page/Tutorial%3A_All_about_info_nodes */
|
||||
SetOrigin(GetSpawnOrigin());
|
||||
DropToFloor();
|
||||
SetOrigin(GetOrigin() + [0,0,32]);
|
||||
}
|
||||
|
||||
void
|
||||
info_node::info_node(void)
|
||||
{
|
||||
|
||||
}
|
51
src/gs-entbase/server/info_node_air.qc
Normal file
51
src/gs-entbase/server/info_node_air.qc
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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_air (0 0 0) (-8 -8 -8) (8 8 8)
|
||||
It's a node, helping monsters navigate in the air as well as underwater.
|
||||
|
||||
-------- KEYS --------
|
||||
"targetname" : Name
|
||||
|
||||
-------- TRIVIA --------
|
||||
This entity was introduced in Half-Life (1998).
|
||||
*/
|
||||
|
||||
class
|
||||
info_node_air:NSPointTrigger
|
||||
{
|
||||
void(void) info_node_air;
|
||||
|
||||
virtual void(void) Respawn;
|
||||
};
|
||||
|
||||
void
|
||||
info_node_air::Respawn(void)
|
||||
{
|
||||
SetSize([0,0,0], [0,0,0]);
|
||||
SetSolid(SOLID_NOT);
|
||||
SetMovetype(MOVETYPE_NONE);
|
||||
|
||||
/* air nodes just stay where they are
|
||||
see https://twhl.info/wiki/page/Tutorial%3A_All_about_info_nodes */
|
||||
SetOrigin(GetSpawnOrigin());
|
||||
}
|
||||
|
||||
void
|
||||
info_node_air:: info_node_air(void)
|
||||
{
|
||||
|
||||
}
|
|
@ -12,6 +12,7 @@ shared/NSPhysicsEntity.qc
|
|||
shared/NSBrushTrigger.qc
|
||||
shared/NSPointTrigger.qc
|
||||
shared/NSVehicle.qc
|
||||
shared/NSNavAI.qc
|
||||
shared/NSMonster.qc
|
||||
shared/NSTalkMonster.qc
|
||||
shared/NSProjectile.qc
|
||||
|
|
|
@ -173,7 +173,7 @@ typedef enum
|
|||
* check is currently only checking on a .takedamage basis. */
|
||||
.int m_iAlliance;
|
||||
|
||||
class NSMonster:NSSurfacePropEntity
|
||||
class NSMonster:NSNavAI
|
||||
{
|
||||
#ifdef SERVER
|
||||
entity m_ssLast;
|
||||
|
@ -191,7 +191,6 @@ class NSMonster:NSSurfacePropEntity
|
|||
float m_flSequenceEnd;
|
||||
float m_flSequenceSpeed;
|
||||
vector m_vecSequenceAngle;
|
||||
vector m_vecTurnAngle;
|
||||
int m_iSequenceFlags;
|
||||
movementState_e m_iMoveState;
|
||||
|
||||
|
@ -208,11 +207,6 @@ class NSMonster:NSSurfacePropEntity
|
|||
monsterState_e m_iOldMState;
|
||||
vector m_vecLKPos; /* last-known pos */
|
||||
|
||||
/* pathfinding */
|
||||
int m_iNodes;
|
||||
int m_iCurNode;
|
||||
nodeslist_t *m_pRoute;
|
||||
vector m_vecLastNode;
|
||||
#endif
|
||||
|
||||
void(void) NSMonster;
|
||||
|
@ -222,13 +216,13 @@ class NSMonster:NSSurfacePropEntity
|
|||
virtual void(float) Save;
|
||||
virtual void(string,string) Restore;
|
||||
|
||||
virtual bool(void) CanCrouch;
|
||||
virtual void(entity) Touch;
|
||||
virtual void(void) Hide;
|
||||
virtual void(void) Respawn;
|
||||
virtual void(void) Pain;
|
||||
virtual void(void) Death;
|
||||
virtual void(void) Physics;
|
||||
virtual void(void) RunAI;
|
||||
virtual void(void) IdleNoise;
|
||||
virtual void(void) FallNoise;
|
||||
virtual void(void) Gib;
|
||||
|
@ -266,10 +260,9 @@ class NSMonster:NSSurfacePropEntity
|
|||
/* sequences */
|
||||
virtual void(void) FreeState;
|
||||
virtual void(void) FreeStateMoved;
|
||||
virtual void(void) ClearRoute;
|
||||
virtual void(void) CheckRoute;
|
||||
virtual void(void) RouteEnded;
|
||||
virtual void(void) WalkRoute;
|
||||
virtual void(vector) NewRoute;
|
||||
virtual int(void) GetSequenceState;
|
||||
|
||||
/* animation cycles */
|
||||
float m_flAnimTime;
|
||||
|
|
|
@ -352,15 +352,6 @@ NSMonster::AttackHolster(void)
|
|||
m_flAttackThink = time + 0.5f;
|
||||
}
|
||||
|
||||
void
|
||||
NSMonster::ClearRoute(void)
|
||||
{
|
||||
if (m_iNodes) {
|
||||
m_iNodes = 0;
|
||||
memfree(m_pRoute);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NSMonster::FreeState(void)
|
||||
{
|
||||
|
@ -409,104 +400,25 @@ NSMonster::FreeStateMoved(void)
|
|||
}
|
||||
|
||||
void
|
||||
NSMonster::CheckRoute(void)
|
||||
NSMonster::RouteEnded(void)
|
||||
{
|
||||
float flDist;
|
||||
vector evenpos;
|
||||
super::RouteEnded();
|
||||
|
||||
if (!m_iNodes)
|
||||
return;
|
||||
|
||||
/* level out position/node stuff */
|
||||
if (m_iCurNode < 0) {
|
||||
evenpos = m_vecLastNode;
|
||||
evenpos[2] = origin[2];
|
||||
} else {
|
||||
evenpos = m_pRoute[m_iCurNode].dest;
|
||||
evenpos[2] = origin[2];
|
||||
}
|
||||
|
||||
flDist = floor(vlen(evenpos - origin));
|
||||
|
||||
if (flDist < 8) {
|
||||
NSLog("^2NSMonster::^3CheckRoute^7: " \
|
||||
"%s reached node\n", this.targetname);
|
||||
m_iCurNode--;
|
||||
velocity = [0,0,0]; /* clamp friction */
|
||||
|
||||
/* we've still traveling and from this node we may be able to walk
|
||||
* directly to our end-destination */
|
||||
if (m_iCurNode > -1) {
|
||||
tracebox(origin, mins, maxs, m_vecLastNode, MOVE_NORMAL, this);
|
||||
|
||||
/* can we walk directly to our target destination? */
|
||||
if (trace_fraction == 1.0) {
|
||||
dprint("^2NSMonster::^3CheckRoute^7: " \
|
||||
"Walking directly to last node\n");
|
||||
m_iCurNode = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_iCurNode < -1) {
|
||||
ClearRoute();
|
||||
NSLog("^2NSMonster::^3CheckRoute^7: %s reached end", this.targetname);
|
||||
|
||||
/* mark that we've ended a sequence, if we're in one and que anim */
|
||||
/* mark that we've ended a sequence, if we're in one and que anim */
|
||||
if (m_iSequenceState == SEQUENCESTATE_ACTIVE) {
|
||||
if (m_flSequenceEnd) {
|
||||
float duration = frameduration(modelindex, m_flSequenceEnd);
|
||||
m_iSequenceState = SEQUENCESTATE_ENDING;
|
||||
think = (m_iSequenceFlags & SSFL_NOSCRIPTMOVE) ? FreeState : FreeStateMoved;
|
||||
nextthink = time + duration;
|
||||
NSLog("^2NSMonster::^3CheckRoute^7: %s overriding anim for %f seconds (modelindex %d, frame %d)", this.targetname, duration, modelindex, m_flSequenceEnd);
|
||||
NSLog("^2NSNavAI::^3CheckRoute^7: %s overriding anim for %f seconds (modelindex %d, frame %d)", this.targetname, duration, modelindex, m_flSequenceEnd);
|
||||
} else {
|
||||
/* we still need to trigger targets */
|
||||
think = (m_iSequenceFlags & SSFL_NOSCRIPTMOVE) ? FreeState : FreeStateMoved;
|
||||
nextthink = time;
|
||||
NSLog("^2NSMonster::^3CheckRoute^7: %s has no anim, finished sequence.", this.targetname);
|
||||
NSLog("^2NSNavAI::^3CheckRoute^7: %s has no anim, finished sequence.", this.targetname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* crouch attempt */
|
||||
if (CanCrouch()) {
|
||||
vector src;
|
||||
bool shouldcrouch = false;
|
||||
|
||||
/* test up */
|
||||
src = origin + [0,0,24];
|
||||
makevectors(angles);
|
||||
traceline(src, src + v_forward * 128, MOVE_NORMAL, this);
|
||||
|
||||
/* we hit something */
|
||||
if (trace_fraction < 1.0) {
|
||||
src = origin + [0,0, -8];
|
||||
traceline(src, src + v_forward * 128, MOVE_NORMAL, this);
|
||||
|
||||
/* we can crouch here, so let's do it */
|
||||
if (trace_fraction >= 1.0)
|
||||
shouldcrouch = true;
|
||||
}
|
||||
|
||||
/* entire way-link needs to be crouched. that's the law of the land */
|
||||
if (shouldcrouch || Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_CROUCH)
|
||||
input_buttons |= INPUT_BUTTON8;
|
||||
}
|
||||
|
||||
/*if (flDist == m_flLastDist) {
|
||||
m_flNodeGiveup += frametime;
|
||||
} else {
|
||||
m_flNodeGiveup = bound(0, m_flNodeGiveup - frametime, 1.0);
|
||||
}
|
||||
|
||||
m_flLastDist = flDist;
|
||||
|
||||
if (m_flNodeGiveup >= 1.0f) {
|
||||
print(sprintf("NSMonster::CheckNode: %s gave up route\n",
|
||||
this.netname));
|
||||
ClearRoute();
|
||||
}*/
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -516,92 +428,35 @@ NSMonster::WalkRoute(void)
|
|||
|
||||
/* we're busy shooting at something, don't walk */
|
||||
if (GetState() == MONSTER_AIMING && m_eEnemy) {
|
||||
endangles = vectoangles(m_eEnemy.origin - origin);
|
||||
|
||||
/* TODO: lerp */
|
||||
m_vecTurnAngle[1] = endangles[1];
|
||||
} else if (m_iNodes && GetState() == MONSTER_IDLE) {
|
||||
/* we're on our last node */
|
||||
if (m_iCurNode < 0) {
|
||||
endangles = vectoangles(m_vecLastNode - origin);
|
||||
} else {
|
||||
endangles = vectoangles(m_pRoute[m_iCurNode].dest - origin);
|
||||
}
|
||||
m_vecTurnAngle[1] = endangles[1];
|
||||
input_movevalues = [m_flSequenceSpeed, 0, 0];
|
||||
input_angles = vectoangles(m_eEnemy.origin - origin);
|
||||
input_angles[0] = input_angles[2] = 0;
|
||||
} else if (m_iNodes && (GetState() == MONSTER_IDLE || GetState() == MONSTER_ALERT)) {
|
||||
input_angles = GetRouteDirection();
|
||||
input_angles[0] = input_angles[2] = 0;
|
||||
input_movevalues = GetRouteMovevalues() * m_flSequenceSpeed;
|
||||
} 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_angles = vectoangles(m_eEnemy.origin - origin);
|
||||
input_angles[0] = input_angles[2] = 0;
|
||||
input_movevalues = [GetChaseSpeed(), 0, 0];
|
||||
m_vecTurnAngle[1] = endangles[1];
|
||||
} else
|
||||
return;
|
||||
|
||||
/* functional */
|
||||
input_angles[1] = v_angle[1] = m_vecTurnAngle[1];
|
||||
|
||||
#if 1
|
||||
/* cosmetic */
|
||||
/* yaw interpolation */
|
||||
{
|
||||
vector new_ang;
|
||||
vector old_ang;
|
||||
vector tmp;
|
||||
makevectors(m_vecTurnAngle);
|
||||
makevectors(input_angles);
|
||||
new_ang = v_forward;
|
||||
makevectors(angles);
|
||||
old_ang = v_forward;
|
||||
|
||||
tmp[0] = 0;
|
||||
tmp[0] = Math_Lerp(old_ang[0], new_ang[0], frametime * 5);
|
||||
tmp[1] = Math_Lerp(old_ang[1], new_ang[1], frametime * 5);
|
||||
tmp[2] = 0;
|
||||
angles = vectoangles(tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
NSMonster::NewRoute(vector destination)
|
||||
{
|
||||
/* engine calls this upon successfully creating a route */
|
||||
static void NewRoute_RouteCB(entity ent, vector dest, int numnodes, nodeslist_t *nodelist)
|
||||
{
|
||||
NSMonster p = (NSMonster)ent;
|
||||
p.m_iNodes = numnodes;
|
||||
p.m_iCurNode = numnodes - 1;
|
||||
p.m_pRoute = nodelist;
|
||||
|
||||
//traceline(p.origin, dest, MOVE_NORMAL, this);
|
||||
tracebox(p.origin, p.mins, p.maxs, dest, MOVE_NORMAL, this);
|
||||
|
||||
/* can we walk directly to our target destination? */
|
||||
if (trace_fraction == 1.0) {
|
||||
dprint("^2NSMonster::^3NewRoute^7: " \
|
||||
"Walking directly to last node\n");
|
||||
p.m_iCurNode = -1;
|
||||
} else {
|
||||
dprint("^2NSMonster::^3NewRoute^7: " \
|
||||
"Path obstructed, calculating route\n");
|
||||
|
||||
/* run through all nodes, mark the closest direct path possible */
|
||||
for (int i = 0; i < p.m_iNodes; i++) {
|
||||
vector vecDest = p.m_pRoute[i].dest;
|
||||
tracebox(p.origin, p.mins, p.maxs, vecDest, TRUE, p);
|
||||
//traceline(p.origin, vecDest, MOVE_NORMAL, this);
|
||||
|
||||
if (trace_fraction == 1.0) {
|
||||
p.m_iCurNode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_nodes_present)
|
||||
return;
|
||||
|
||||
ClearRoute();
|
||||
|
||||
if (!m_iNodes) {
|
||||
route_calculate(this, destination, 0, NewRoute_RouteCB);
|
||||
m_vecLastNode = destination;
|
||||
tmp[2] = Math_Lerp(old_ang[2], new_ang[2], frametime * 5);
|
||||
input_angles = vectoangles(tmp);
|
||||
input_angles[0] = input_angles[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -694,6 +549,19 @@ NSMonster::GetState(void)
|
|||
return m_iMState;
|
||||
}
|
||||
|
||||
int
|
||||
NSMonster::GetSequenceState(void)
|
||||
{
|
||||
return m_iSequenceState;
|
||||
}
|
||||
|
||||
void
|
||||
NSMonster::RunAI(void)
|
||||
{
|
||||
SeeThink();
|
||||
AttackThink();
|
||||
}
|
||||
|
||||
void PMoveCustom_RunPlayerPhysics(entity target);
|
||||
void PMoveCustom_RunCrouchPhysics(entity target);
|
||||
void
|
||||
|
@ -702,37 +570,46 @@ NSMonster::Physics(void)
|
|||
input_movevalues = [0,0,0];
|
||||
input_impulse = 0;
|
||||
input_buttons = 0;
|
||||
input_angles = v_angle;
|
||||
input_timelength = frametime;
|
||||
input_angles = angles;
|
||||
|
||||
/* override whatever we did above with this */
|
||||
if (m_iSequenceState == SEQUENCESTATE_ENDING) {
|
||||
input_angles = v_angle = angles = m_vecSequenceAngle;
|
||||
/* when stuck in a sequence, forget enemies, combat stance */
|
||||
if (GetSequenceState() != SEQUENCESTATE_NONE) {
|
||||
m_eEnemy = __NULL__;
|
||||
SetState(MONSTER_IDLE);
|
||||
}
|
||||
|
||||
/* we're ending a scripted sequence, so play its animation */
|
||||
if (GetSequenceState() == SEQUENCESTATE_ENDING) {
|
||||
input_angles[1] = m_vecSequenceAngle[1];
|
||||
SetFrame(m_flSequenceEnd);
|
||||
} else if (movetype == MOVETYPE_WALK) {
|
||||
if (m_iSequenceState == SEQUENCESTATE_NONE) {
|
||||
SeeThink();
|
||||
AttackThink();
|
||||
} else {
|
||||
/* if still alive... */
|
||||
if (GetState() != MONSTER_DEAD) {
|
||||
/* not in a sequence */
|
||||
if (GetSequenceState() == SEQUENCESTATE_NONE) {
|
||||
RunAI();
|
||||
}
|
||||
|
||||
AnimationUpdate();
|
||||
}
|
||||
|
||||
CheckRoute();
|
||||
WalkRoute();
|
||||
|
||||
hitcontentsmaski = CONTENTBITS_MONSTER;
|
||||
|
||||
if (CanCrouch())
|
||||
PMoveCustom_RunCrouchPhysics(this);
|
||||
else
|
||||
PMoveCustom_RunPlayerPhysics(this);
|
||||
SetOrigin(origin);
|
||||
IdleNoise();
|
||||
button8 = input_buttons & INPUT_BUTTON8; // duck
|
||||
|
||||
AnimationUpdate();
|
||||
SetOrigin(origin);
|
||||
}
|
||||
|
||||
if (!(flags & FL_ONGROUND) && velocity[2] < -415) {
|
||||
if (!(m_iFlags & MSF_FALLING))
|
||||
if (!(GetFlags() & FL_ONGROUND) && velocity[2] < -415) {
|
||||
if (!(m_iFlags & MSF_FALLING)) {
|
||||
FallNoise();
|
||||
}
|
||||
|
||||
m_iFlags |= MSF_FALLING;
|
||||
} else {
|
||||
|
@ -747,13 +624,11 @@ NSMonster::Physics(void)
|
|||
}
|
||||
}
|
||||
|
||||
m_flBaseTime = frame1time;
|
||||
frame1time += frametime;
|
||||
}
|
||||
|
||||
bool
|
||||
NSMonster::CanCrouch(void)
|
||||
{
|
||||
return false;
|
||||
processmodelevents(modelindex, frame, m_flBaseTime,
|
||||
frame1time, Game_ServerModelEvent);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -891,37 +766,6 @@ NSMonster::SendEntity(entity ePEnt, float fChanged)
|
|||
|
||||
WriteByte(MSG_ENTITY, ENT_MONSTER);
|
||||
|
||||
/* newly popped into the PVS, sadly this is the only hacky way to check
|
||||
* for this right now. convince the engine maintainer to make this more sensible */
|
||||
if (fChanged == 0xFFFFFF) {
|
||||
/* check for defaults. if these are predictable fields, don't even bother
|
||||
* networking them! you're just wasting bandwidth. */
|
||||
if (frame == 0)
|
||||
fChanged &= ~BASEFL_CHANGED_FRAME;
|
||||
if (skin == 0)
|
||||
fChanged &= ~BASEFL_CHANGED_SKIN;
|
||||
if (effects == 0)
|
||||
fChanged &= ~BASEFL_CHANGED_EFFECTS;
|
||||
if (m_iBody == 0)
|
||||
fChanged &= ~BASEFL_CHANGED_BODY;
|
||||
if (scale == 0.0 || scale == 1.0)
|
||||
fChanged &= ~BASEFL_CHANGED_SCALE;
|
||||
if (origin == [0,0,0])
|
||||
fChanged &= ~BASEFL_CHANGED_ORIGIN;
|
||||
if (angles == [0,0,0])
|
||||
fChanged &= ~BASEFL_CHANGED_ANGLES;
|
||||
if (velocity == [0,0,0])
|
||||
fChanged &= ~BASEFL_CHANGED_VELOCITY;
|
||||
if (mins == [0,0,0] && maxs == [0,0,0])
|
||||
fChanged &= ~BASEFL_CHANGED_SIZE;
|
||||
if (solid == SOLID_NOT)
|
||||
fChanged &= ~BASEFL_CHANGED_SOLID;
|
||||
if (movetype == MOVETYPE_NONE)
|
||||
fChanged &= ~BASEFL_CHANGED_MOVETYPE;
|
||||
if (m_iRenderMode == RM_NORMAL)
|
||||
fChanged &= ~BASEFL_CHANGED_RENDERMODE;
|
||||
}
|
||||
|
||||
/* don't network triggers unless provoked */
|
||||
/*if (cvar("developer") == 0 && m_iRenderMode == RM_TRIGGER)
|
||||
return (0);*/
|
||||
|
|
43
src/gs-entbase/shared/NSNavAI.h
Normal file
43
src/gs-entbase/shared/NSNavAI.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class
|
||||
NSNavAI:NSSurfacePropEntity
|
||||
{
|
||||
#ifdef SERVER
|
||||
/* pathfinding */
|
||||
int m_iNodes;
|
||||
int m_iCurNode;
|
||||
nodeslist_t *m_pRoute;
|
||||
vector m_vecLastNode;
|
||||
vector m_vecTurnAngle;
|
||||
#endif
|
||||
|
||||
void(void) NSNavAI;
|
||||
|
||||
#ifdef SERVER
|
||||
/* methods we'd like others to override */
|
||||
virtual bool(void) CanCrouch;
|
||||
|
||||
virtual vector(void) GetRouteMovevalues;
|
||||
virtual vector(void) GetRouteDirection;
|
||||
|
||||
virtual void(void) RouteEnded;
|
||||
virtual void(void) ClearRoute;
|
||||
virtual void(void) CheckRoute;
|
||||
virtual void(vector) NewRoute;
|
||||
#endif
|
||||
};
|
210
src/gs-entbase/shared/NSNavAI.qc
Normal file
210
src/gs-entbase/shared/NSNavAI.qc
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef SERVER
|
||||
bool
|
||||
NSNavAI::CanCrouch(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
NSNavAI::RouteEnded(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
NSNavAI::CheckRoute(void)
|
||||
{
|
||||
float flDist;
|
||||
vector evenpos;
|
||||
|
||||
if (!m_iNodes)
|
||||
return;
|
||||
|
||||
/* level out position/node stuff */
|
||||
if (m_iCurNode < 0) {
|
||||
evenpos = m_vecLastNode;
|
||||
evenpos[2] = origin[2];
|
||||
} else {
|
||||
evenpos = m_pRoute[m_iCurNode].dest;
|
||||
evenpos[2] = origin[2];
|
||||
}
|
||||
|
||||
flDist = floor(vlen(evenpos - origin));
|
||||
|
||||
if (flDist < 8) {
|
||||
NSLog("^2NSNavAI::^3CheckRoute^7: " \
|
||||
"%s reached node\n", this.targetname);
|
||||
m_iCurNode--;
|
||||
velocity = [0,0,0]; /* clamp friction */
|
||||
|
||||
/* we've still traveling and from this node we may be able to walk
|
||||
* directly to our end-destination */
|
||||
if (m_iCurNode > -1) {
|
||||
tracebox(origin, mins, maxs, m_vecLastNode, MOVE_NORMAL, this);
|
||||
|
||||
/* can we walk directly to our target destination? */
|
||||
if (trace_fraction == 1.0) {
|
||||
dprint("^2NSNavAI::^3CheckRoute^7: " \
|
||||
"Walking directly to last node\n");
|
||||
m_iCurNode = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_iCurNode < -1) {
|
||||
ClearRoute();
|
||||
RouteEnded();
|
||||
NSLog("^2NSNavAI::^3CheckRoute^7: %s reached end", this.targetname);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* crouch attempt */
|
||||
if (CanCrouch()) {
|
||||
vector src;
|
||||
bool shouldcrouch = false;
|
||||
|
||||
/* test up */
|
||||
src = origin + [0,0,24];
|
||||
makevectors(angles);
|
||||
traceline(src, src + v_forward * 128, MOVE_NORMAL, this);
|
||||
|
||||
/* we hit something */
|
||||
if (trace_fraction < 1.0) {
|
||||
src = origin + [0,0, -8];
|
||||
traceline(src, src + v_forward * 128, MOVE_NORMAL, this);
|
||||
|
||||
/* we can crouch here, so let's do it */
|
||||
if (trace_fraction >= 1.0)
|
||||
shouldcrouch = true;
|
||||
}
|
||||
|
||||
/* entire way-link needs to be crouched. that's the law of the land */
|
||||
if (shouldcrouch || Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_CROUCH)
|
||||
input_buttons |= INPUT_BUTTON8;
|
||||
}
|
||||
|
||||
/*if (flDist == m_flLastDist) {
|
||||
m_flNodeGiveup += frametime;
|
||||
} else {
|
||||
m_flNodeGiveup = bound(0, m_flNodeGiveup - frametime, 1.0);
|
||||
}
|
||||
|
||||
m_flLastDist = flDist;
|
||||
|
||||
if (m_flNodeGiveup >= 1.0f) {
|
||||
print(sprintf("NSNavAI::CheckNode: %s gave up route\n",
|
||||
this.netname));
|
||||
ClearRoute();
|
||||
}*/
|
||||
}
|
||||
|
||||
vector
|
||||
NSNavAI::GetRouteMovevalues(void)
|
||||
{
|
||||
vector vecDirection;
|
||||
|
||||
if (!m_iNodes)
|
||||
return [0,0,0];
|
||||
|
||||
if (m_iCurNode < 0)
|
||||
vecDirection = normalize(m_vecLastNode - origin);
|
||||
else
|
||||
vecDirection = normalize(m_pRoute[m_iCurNode].dest - origin);
|
||||
|
||||
makevectors(angles);
|
||||
return [v_forward * vecDirection, v_right * vecDirection, v_up * vecDirection];
|
||||
}
|
||||
|
||||
vector
|
||||
NSNavAI::GetRouteDirection(void)
|
||||
{
|
||||
if (!m_iNodes)
|
||||
return angles;
|
||||
|
||||
if (m_iCurNode < 0)
|
||||
return vectoangles(m_vecLastNode - origin);
|
||||
else
|
||||
return vectoangles(m_pRoute[m_iCurNode].dest - origin);
|
||||
}
|
||||
|
||||
void
|
||||
NSNavAI::NewRoute(vector destination)
|
||||
{
|
||||
/* engine calls this upon successfully creating a route */
|
||||
static void NewRoute_RouteCB(entity ent, vector dest, int numnodes, nodeslist_t *nodelist)
|
||||
{
|
||||
NSNavAI p = (NSNavAI)ent;
|
||||
p.m_iNodes = numnodes;
|
||||
p.m_iCurNode = numnodes - 1;
|
||||
p.m_pRoute = nodelist;
|
||||
|
||||
//traceline(p.origin, dest, MOVE_NORMAL, this);
|
||||
tracebox(p.origin, p.mins, p.maxs, dest, MOVE_NORMAL, this);
|
||||
|
||||
/* can we walk directly to our target destination? */
|
||||
if (trace_fraction == 1.0) {
|
||||
dprint("^2NSNavAI::^3NewRoute^7: " \
|
||||
"Walking directly to last node\n");
|
||||
p.m_iCurNode = -1;
|
||||
} else {
|
||||
dprint("^2NSNavAI::^3NewRoute^7: " \
|
||||
"Path obstructed, calculating route\n");
|
||||
|
||||
/* run through all nodes, mark the closest direct path possible */
|
||||
for (int i = 0; i < p.m_iNodes; i++) {
|
||||
vector vecDest = p.m_pRoute[i].dest;
|
||||
tracebox(p.origin, p.mins, p.maxs, vecDest, TRUE, p);
|
||||
//traceline(p.origin, vecDest, MOVE_NORMAL, this);
|
||||
|
||||
if (trace_fraction == 1.0) {
|
||||
p.m_iCurNode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_nodes_present)
|
||||
return;
|
||||
|
||||
ClearRoute();
|
||||
|
||||
if (!m_iNodes) {
|
||||
route_calculate(this, destination, 0, NewRoute_RouteCB);
|
||||
m_vecLastNode = destination;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NSNavAI::ClearRoute(void)
|
||||
{
|
||||
if (m_iNodes) {
|
||||
m_iNodes = 0;
|
||||
memfree(m_pRoute);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
NSNavAI::NSNavAI(void)
|
||||
{
|
||||
|
||||
}
|
|
@ -85,7 +85,7 @@ class NSTalkMonster:NSMonster
|
|||
virtual void(void) StartleAllies;
|
||||
virtual void(void) FollowPlayer;
|
||||
virtual void(void) FollowChain;
|
||||
virtual void(void) Physics;
|
||||
virtual void(void) RunAI;
|
||||
virtual void(void) OnPlayerUse;
|
||||
virtual void(void) PanicFrame;
|
||||
virtual void(void) Hide;
|
||||
|
|
|
@ -369,10 +369,10 @@ NSTalkMonster::TalkStopFollow(void)
|
|||
void
|
||||
NSTalkMonster::FollowPlayer(void)
|
||||
{
|
||||
v_angle = vectoangles(m_eFollowingChain.origin - origin);
|
||||
v_angle[0] = 0;
|
||||
v_angle[1] = Math_FixDelta(v_angle[1]);
|
||||
v_angle[2] = 0;
|
||||
input_angles = vectoangles(m_eFollowingChain.origin - origin);
|
||||
input_angles[0] = 0;
|
||||
input_angles[1] = Math_FixDelta(input_angles[1]);
|
||||
input_angles[2] = 0;
|
||||
|
||||
/* Give up after 1024 units */
|
||||
if (vlen(m_eFollowingChain.origin - origin) > 1024) {
|
||||
|
@ -385,10 +385,10 @@ NSTalkMonster::FollowPlayer(void)
|
|||
|
||||
/* Tracing failed, there's world geometry in the way */
|
||||
if (trace_fraction < 1.0f) {
|
||||
v_angle = vectoangles(m_vecLastUserPos - origin);
|
||||
v_angle[0] = 0;
|
||||
v_angle[1] = Math_FixDelta(v_angle[1]);
|
||||
v_angle[2] = 0;
|
||||
input_angles = vectoangles(m_vecLastUserPos - origin);
|
||||
input_angles[0] = 0;
|
||||
input_angles[1] = Math_FixDelta(input_angles[1]);
|
||||
input_angles[2] = 0;
|
||||
} else {
|
||||
m_vecLastUserPos = m_eFollowingChain.origin;
|
||||
}
|
||||
|
@ -424,10 +424,9 @@ NSTalkMonster::PanicFrame(void)
|
|||
}
|
||||
|
||||
if (m_flChangePath < time) {
|
||||
v_angle[1] -= 180 + ((random() - 0.5) * 90);
|
||||
v_angle[1] = Math_FixDelta(v_angle[1]);
|
||||
input_angles[1] -= 180 + ((random() - 0.5) * 90);
|
||||
input_angles[1] = Math_FixDelta(input_angles[1]);
|
||||
m_flChangePath = time + floor(random(2,10));
|
||||
angles = input_angles = v_angle;
|
||||
}
|
||||
|
||||
if (m_flNextSentence > time)
|
||||
|
@ -452,83 +451,24 @@ NSTalkMonster::FollowChain(void)
|
|||
}
|
||||
|
||||
void
|
||||
NSTalkMonster::Physics(void)
|
||||
NSTalkMonster::RunAI(void)
|
||||
{
|
||||
input_movevalues = [0,0,0];
|
||||
input_impulse = 0;
|
||||
input_buttons = 0;
|
||||
input_timelength = frametime;
|
||||
input_angles = v_angle;
|
||||
SeeThink();
|
||||
AttackThink();
|
||||
TalkPlayerGreet();
|
||||
FollowChain();
|
||||
|
||||
/* make sure we're forgetting about enemies and attack states in sequence */
|
||||
if (m_iSequenceState != SEQUENCESTATE_NONE) {
|
||||
m_eEnemy = __NULL__;
|
||||
SetState(MONSTER_IDLE);
|
||||
}
|
||||
|
||||
/* override whatever we did above with this */
|
||||
if (m_iSequenceState == SEQUENCESTATE_ENDING) {
|
||||
input_angles = v_angle = angles = m_vecSequenceAngle;
|
||||
SetFrame(m_flSequenceEnd);
|
||||
if (m_eFollowing != world) {
|
||||
FollowPlayer();
|
||||
} else if (m_iFlags & MONSTER_FEAR) {
|
||||
PanicFrame();
|
||||
} else {
|
||||
if (GetState() != MONSTER_DEAD) {
|
||||
if (m_iSequenceState == SEQUENCESTATE_NONE) {
|
||||
SeeThink();
|
||||
AttackThink();
|
||||
TalkPlayerGreet();
|
||||
FollowChain();
|
||||
|
||||
if (m_eFollowing != world) {
|
||||
FollowPlayer();
|
||||
input_angles = angles = v_angle;
|
||||
} else if (m_iFlags & MONSTER_FEAR) {
|
||||
PanicFrame();
|
||||
} else {
|
||||
if (random() < 0.5) {
|
||||
TalkPlayerAsk();
|
||||
} else {
|
||||
TalkPlayerIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimationUpdate();
|
||||
}
|
||||
CheckRoute();
|
||||
WalkRoute();
|
||||
hitcontentsmaski = CONTENTBITS_MONSTER;
|
||||
|
||||
if (CanCrouch())
|
||||
PMoveCustom_RunCrouchPhysics(this);
|
||||
else
|
||||
PMoveCustom_RunPlayerPhysics(this);
|
||||
|
||||
SetOrigin(origin);
|
||||
}
|
||||
|
||||
if (!(flags & FL_ONGROUND) && velocity[2] < -415) {
|
||||
if (!(m_iFlags & MSF_FALLING)) {
|
||||
FallNoise();
|
||||
}
|
||||
|
||||
m_iFlags |= MSF_FALLING;
|
||||
} else {
|
||||
m_iFlags &= ~MSF_FALLING;
|
||||
}
|
||||
|
||||
/* support for think/nextthink */
|
||||
if (think && nextthink > 0.0f) {
|
||||
if (nextthink < time) {
|
||||
nextthink = 0.0f;
|
||||
think();
|
||||
if (random() < 0.5) {
|
||||
TalkPlayerAsk();
|
||||
} else {
|
||||
TalkPlayerIdle();
|
||||
}
|
||||
}
|
||||
|
||||
m_flBaseTime = frame1time;
|
||||
frame1time += frametime;
|
||||
|
||||
processmodelevents(modelindex, frame, m_flBaseTime,
|
||||
frame1time, Game_ServerModelEvent);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -612,9 +552,9 @@ NSTalkMonster::SendEntity(entity ePEnt, float fChanged)
|
|||
WriteCoord(MSG_ENTITY, origin[2]);
|
||||
}
|
||||
if (fChanged & BASEFL_CHANGED_ANGLES) {
|
||||
WriteShort(MSG_ENTITY, 0);
|
||||
WriteShort(MSG_ENTITY, angles[0] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, angles[1] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, 0);
|
||||
WriteShort(MSG_ENTITY, angles[2] * 32767 / 360);
|
||||
}
|
||||
if (fChanged & BASEFL_CHANGED_MODELINDEX) {
|
||||
WriteShort(MSG_ENTITY, modelindex);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "NSPhysicsEntity.h"
|
||||
#include "NSBrushTrigger.h"
|
||||
#include "NSPointTrigger.h"
|
||||
#include "NSNavAI.h"
|
||||
#include "NSMonster.h"
|
||||
#include "NSTalkMonster.h"
|
||||
#include "NSProjectile.h"
|
||||
|
|
Loading…
Reference in a new issue