NSTalkMonster: pathfinding fixes that benefit following players around the maps. More failsafes.
This commit is contained in:
parent
7d9de3a2cc
commit
a623b1e301
5 changed files with 169 additions and 25 deletions
|
@ -14,8 +14,8 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
var float autocvar_ai_walkSpeed = 64;
|
||||
var float autocvar_ai_runSpeed = 364;
|
||||
var float autocvar_ai_walkSpeed = 150;
|
||||
var float autocvar_ai_runSpeed = 320;
|
||||
|
||||
void
|
||||
NSMonster::NSMonster(void)
|
||||
|
@ -97,6 +97,7 @@ NSMonster::NSMonster(void)
|
|||
m_flRunSpeed = autocvar_ai_runSpeed;
|
||||
m_flLeapDamage = 0;
|
||||
m_bLeapAttacked = false;
|
||||
maxspeed = 1024;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -781,14 +782,19 @@ NSMonster::GetYawSpeed(void)
|
|||
void
|
||||
NSMonster::_LerpTurnToYaw(vector turnYaw)
|
||||
{
|
||||
#if 1
|
||||
#if 0
|
||||
angles[1] = input_angles[1] = v_angle[1] = turnYaw[1];
|
||||
#else
|
||||
float turnSpeed = GetYawSpeed();
|
||||
vector vecWishAngle = turnYaw;
|
||||
float yawDiff = anglesub(turnYaw[1], v_angle[1]);
|
||||
|
||||
if (fabs(yawDiff) > 90) {
|
||||
/* anything but small turns? halt the player */
|
||||
if (fabs(yawDiff) > 5) {
|
||||
input_movevalues = g_vec_null;
|
||||
}
|
||||
|
||||
if (fabs(yawDiff) > 45) {
|
||||
velocity = g_vec_null;
|
||||
input_movevalues = g_vec_null;
|
||||
|
||||
|
@ -1238,13 +1244,13 @@ NSMonster::RouteEnded(void)
|
|||
m_iSequenceState = SEQUENCESTATE_ENDING;
|
||||
think = (m_iSequenceFlags & SSFL_NOSCRIPTMOVE) ? FreeState : FreeStateMoved;
|
||||
nextthink = time + duration;
|
||||
NSMonster_Log("^2%s::^3CheckRoute^7: %s overriding anim for %f seconds (modelindex %d, frame %d)", \
|
||||
NSMonster_Log("^2%s::^3RouteEnded^7: %s overriding anim for %f seconds (modelindex %d, frame %d)", \
|
||||
classname, this.targetname, duration, modelindex, m_flSequenceEnd);
|
||||
} else {
|
||||
/* we still need to trigger targets */
|
||||
think = (m_iSequenceFlags & SSFL_NOSCRIPTMOVE) ? FreeState : FreeStateMoved;
|
||||
nextthink = time;
|
||||
NSMonster_Log("^2%s::^3CheckRoute^7: %s has no anim, finished sequence", \
|
||||
NSMonster_Log("^2%s::^3RouteEnded^7: %s has no anim, finished sequence", \
|
||||
classname, this.targetname);
|
||||
}
|
||||
}
|
||||
|
@ -1252,6 +1258,7 @@ NSMonster::RouteEnded(void)
|
|||
void
|
||||
NSMonster::WalkRoute(void)
|
||||
{
|
||||
|
||||
/* we're busy shooting at something, don't walk */
|
||||
if (GetState() == MONSTER_AIMING && m_eEnemy) {
|
||||
input_angles = vectoangles(m_eEnemy.origin - origin);
|
||||
|
@ -1260,6 +1267,33 @@ NSMonster::WalkRoute(void)
|
|||
input_angles = GetRouteDirection();
|
||||
input_angles[0] = input_angles[2] = 0;
|
||||
input_movevalues = GetRouteMovevalues() * m_flSequenceSpeed;
|
||||
|
||||
/* is something in our way? */
|
||||
makevectors(input_angles);
|
||||
tracebox(origin, mins, maxs, origin + v_forward * 256, MOVE_NORMAL, this);
|
||||
|
||||
/* indeed it is */
|
||||
if (trace_fraction < 1.0f) {
|
||||
vector testOrg = origin + (v_right * 32);
|
||||
testOrg[2] += mins[2] + 18.0f; /* test at feet level */
|
||||
|
||||
traceline(testOrg, testOrg + v_forward * 256, MOVE_NORMAL, this);
|
||||
|
||||
/* is space free to the right? */
|
||||
if (trace_fraction == 1.0) {
|
||||
input_movevalues[1] = m_flSequenceSpeed * 0.25f;
|
||||
} else {
|
||||
|
||||
testOrg = origin - (v_right * 32);
|
||||
testOrg[2] += mins[2] + 18.0f; /* test at feet level */
|
||||
|
||||
traceline(testOrg, testOrg + v_forward * 256, MOVE_NORMAL, this);
|
||||
|
||||
/* is space free to the left? */
|
||||
if (trace_fraction == 1.0)
|
||||
input_movevalues[1] = -m_flSequenceSpeed * 0.25f;
|
||||
}
|
||||
}
|
||||
} else if (GetState() == MONSTER_CHASING && m_eEnemy) {
|
||||
/* we've got 'em in our sights, just need to walk closer */
|
||||
input_angles = vectoangles(m_eEnemy.origin - origin);
|
||||
|
@ -1268,6 +1302,11 @@ NSMonster::WalkRoute(void)
|
|||
} else
|
||||
return;
|
||||
|
||||
/* don't move while turning. */
|
||||
if (m_bTurning == true) {
|
||||
input_movevalues = [0,0,0];
|
||||
}
|
||||
|
||||
/* yaw interpolation */
|
||||
_LerpTurnToYaw(input_angles);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ private:
|
|||
vector m_vecTurnAngle;
|
||||
string m_pathTarget;
|
||||
NSEntity m_pathEntity;
|
||||
float _m_flRouteGiveUp;
|
||||
vector _m_vecRoutePrev;
|
||||
vector m_vecRouteEntity;
|
||||
entity m_eFollowing;
|
||||
|
||||
/* These are defined in side defs\*.def, ammo_types and ammo_names */
|
||||
int m_iAmmoTypes[MAX_AMMO_TYPES];
|
||||
|
|
|
@ -24,6 +24,8 @@ NSNavAI::NSNavAI(void)
|
|||
m_vecLastNode = [0,0,0];
|
||||
m_vecTurnAngle = [0,0,0];
|
||||
|
||||
_m_flRouteGiveUp = 0.0f;
|
||||
|
||||
for (int i = 0; i < MAX_AMMO_TYPES; i++)
|
||||
m_iAmmoTypes[i] = 0;
|
||||
#endif
|
||||
|
@ -108,6 +110,7 @@ void
|
|||
NSNavAI::CheckRoute(void)
|
||||
{
|
||||
float flDist;
|
||||
float flNodeRadius;
|
||||
vector evenpos;
|
||||
|
||||
if (m_pathTarget) {
|
||||
|
@ -118,18 +121,41 @@ NSNavAI::CheckRoute(void)
|
|||
if (!m_iNodes)
|
||||
return;
|
||||
|
||||
if (_m_flRouteGiveUp < time) {
|
||||
/* 50 units in 2 seconds is not good. */
|
||||
if (vlen(_m_vecRoutePrev - origin) < 50.0f) {
|
||||
/* HACK: for followers */
|
||||
if (m_eFollowing) {
|
||||
RouteToPosition(m_eFollowing.origin);
|
||||
NSNavAI_Log("^2%s::^3CheckRoute^7: " \
|
||||
"Giving up current route to follower, re-calculating", classname);
|
||||
} else {
|
||||
RouteToPosition(m_vecLastNode);
|
||||
NSNavAI_Log("^2%s::^3CheckRoute^7: " \
|
||||
"Giving up current route, re-calculating", classname);
|
||||
}
|
||||
}
|
||||
_m_flRouteGiveUp = time + 2.0f;
|
||||
_m_vecRoutePrev = origin;
|
||||
}
|
||||
|
||||
/* level out position/node stuff */
|
||||
if (m_iCurNode < 0) {
|
||||
evenpos = m_vecLastNode;
|
||||
evenpos[2] = origin[2];
|
||||
flNodeRadius = 8.0f;
|
||||
} else {
|
||||
evenpos = m_pRoute[m_iCurNode].dest;
|
||||
evenpos[2] = origin[2];
|
||||
flNodeRadius = m_pRoute[m_iCurNode].radius;
|
||||
|
||||
if (flNodeRadius <= 0.0)
|
||||
flNodeRadius = 8.0f;
|
||||
}
|
||||
|
||||
flDist = floor(vlen(evenpos - origin));
|
||||
|
||||
if (flDist < 8) {
|
||||
if (flDist < flNodeRadius) {
|
||||
NSNavAI_Log("^2%s::^3CheckRoute^7: " \
|
||||
"%s reached node", classname, targetname);
|
||||
m_iCurNode--;
|
||||
|
@ -149,12 +175,14 @@ NSNavAI::CheckRoute(void)
|
|||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
/* check if we can reach the node after the current one */
|
||||
if (m_iCurNode > 0 && m_iNodes > 3) { /* HACK: only bother when we have more than 3 nodes in the path... this works around an issue in c1a0d I'm unsure about */
|
||||
int iNextNode = (m_iCurNode - 1);
|
||||
vector vecNextNode = m_pRoute[iNextNode].dest;
|
||||
|
||||
tracebox(origin, mins, maxs, vecNextNode, MOVE_NORMAL, this);
|
||||
other = world;
|
||||
tracebox(origin, mins, maxs, vecNextNode, MOVE_OTHERONLY, this);
|
||||
|
||||
/* it's accessible */
|
||||
if (!trace_startsolid && trace_fraction == 1.0f) {
|
||||
|
@ -162,8 +190,10 @@ NSNavAI::CheckRoute(void)
|
|||
m_iCurNode = iNextNode;
|
||||
NSNavAI_Log("^2%s::^3CheckRoute^7: skipping to next node %i at '%v'", \
|
||||
classname, iNextNode, vecNextNode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* reached the end of the line */
|
||||
if (m_iCurNode < -1) {
|
||||
|
|
|
@ -67,6 +67,8 @@ They also can communicate with other NSTalkMonster based entities.
|
|||
- "talk_stop_follow" : SentenceDef to play for when they're being asked to unfollow someone.
|
||||
- "talk_deny_follow" : SentenceDef to play for when they're denying the follow request.
|
||||
- "follow_on_use" : Can be either 0 or 1, will decide if they can follow someone.
|
||||
- "follow_dist" : Distance between the it and the player its following.
|
||||
- "follow_maxdist" : Maximum distance between it and the player before giving up following them.
|
||||
|
||||
For more keys, see NSMonster.
|
||||
*/
|
||||
|
@ -102,6 +104,7 @@ public:
|
|||
virtual float SendEntity(entity,float);
|
||||
virtual void Save(float);
|
||||
virtual void Restore(string,string);
|
||||
virtual void Touch(entity);
|
||||
|
||||
/*virtual void(void) TalkAnswer;
|
||||
virtual void(void) TalkAsk;
|
||||
|
@ -158,7 +161,6 @@ private:
|
|||
float m_flNextSentence;
|
||||
int m_iFlags;
|
||||
|
||||
entity m_eFollowing;
|
||||
entity m_eFollowingChain;
|
||||
vector m_vecLastUserPos;
|
||||
float m_flChangePath;
|
||||
|
@ -167,6 +169,10 @@ private:
|
|||
float m_flFollowSpeed;
|
||||
bool m_bFollowOnUse;
|
||||
|
||||
float m_flFollowDistance;
|
||||
float m_flMaxFollowDistance;
|
||||
bool m_bFollowGrouping;
|
||||
|
||||
/* sentences identifiers */
|
||||
string m_talkAnswer; /* random answer to whenever a question is asked */
|
||||
string m_talkAsk; /* asks a random generic question */
|
||||
|
|
|
@ -51,6 +51,9 @@ NSTalkMonster::NSTalkMonster(void)
|
|||
m_talkStopFollow = __NULL__;
|
||||
m_talkDenyFollow = __NULL__;
|
||||
m_bFollowOnUse = false;
|
||||
m_flFollowDistance = 128.0f;
|
||||
m_flMaxFollowDistance = 2048.0f;
|
||||
m_bFollowGrouping = false;
|
||||
#else
|
||||
m_flSentenceTime = 0.0f;
|
||||
m_pSentenceQue = __NULL__;
|
||||
|
@ -102,6 +105,8 @@ NSTalkMonster::Save(float handle)
|
|||
SaveFloat(handle, "m_flFollowSpeedChanged", m_flFollowSpeedChanged);
|
||||
SaveFloat(handle, "m_flFollowSpeed", m_flFollowSpeed);
|
||||
SaveBool(handle, "m_bFollowOnUse", m_bFollowOnUse);
|
||||
SaveFloat(handle, "m_flFollowDistance", m_flFollowDistance);
|
||||
SaveFloat(handle, "m_flMaxFollowDistance", m_flMaxFollowDistance);
|
||||
|
||||
SaveString(handle, "m_talkAnswer", m_talkAnswer);
|
||||
SaveString(handle, "m_talkAsk", m_talkAsk);
|
||||
|
@ -164,6 +169,12 @@ NSTalkMonster::Restore(string strKey, string strValue)
|
|||
case "m_bFollowOnUse":
|
||||
m_bFollowOnUse = ReadBool(strValue);
|
||||
break;
|
||||
case "m_flFollowDistance":
|
||||
m_flFollowDistance = ReadFloat(strValue);
|
||||
break;
|
||||
case "m_flMaxFollowDistance":
|
||||
m_flMaxFollowDistance = ReadFloat(strValue);
|
||||
break;
|
||||
case "m_talkAnswer":
|
||||
m_talkAnswer = ReadString(strValue);
|
||||
break;
|
||||
|
@ -505,6 +516,22 @@ NSTalkMonster::TalkDenyFollow(void)
|
|||
m_flNextSentence = time + 10.0;
|
||||
}
|
||||
|
||||
void
|
||||
NSTalkMonster::Touch(entity eToucher)
|
||||
{
|
||||
if (eToucher == m_eFollowing) {
|
||||
makevectors(eToucher.angles);
|
||||
velocity = v_forward * 250.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
/* ugly hack */
|
||||
if (eToucher.classname == "func_door_rotating") {
|
||||
owner = eToucher;
|
||||
}
|
||||
|
||||
super::Touch(eToucher);
|
||||
}
|
||||
|
||||
void
|
||||
NSTalkMonster::FollowPlayer(void)
|
||||
|
@ -525,40 +552,70 @@ NSTalkMonster::FollowPlayer(void)
|
|||
flPlayerDist = vlen(vecParent - origin);
|
||||
|
||||
/* Give up after 1024 units */
|
||||
if (flPlayerDist > 1024) {
|
||||
if (flPlayerDist > m_flMaxFollowDistance) {
|
||||
m_eFollowing = world;
|
||||
} else if (flPlayerDist > 128) {
|
||||
NSMonster_Log("Maximum follow distance reached. Will stop following.");
|
||||
} else if (flPlayerDist >= m_flFollowDistance) {
|
||||
/* we only allow speed changes every second, avoid jitter */
|
||||
if (m_flFollowSpeedChanged < time) {
|
||||
float flNextSpeed = GetChaseSpeed();
|
||||
|
||||
/* if we're close enough, we ought to walk */
|
||||
if (flPlayerDist < 256)
|
||||
if (flPlayerDist < (m_flFollowDistance * 1.5f))
|
||||
flNextSpeed = GetWalkSpeed();
|
||||
|
||||
/* only update the timer when speed changed */
|
||||
if (flNextSpeed != m_flFollowSpeed) {
|
||||
m_flFollowSpeed = flNextSpeed;
|
||||
m_flFollowSpeedChanged = time + 1.0f;
|
||||
m_flFollowSpeedChanged = time + 5.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (DistanceFromYaw(vecParent) > 0.9f)
|
||||
input_movevalues[0] = m_flFollowSpeed;
|
||||
|
||||
other = world;
|
||||
traceline(origin, m_eFollowingChain.origin, MOVE_OTHERONLY, this);
|
||||
/* when we're in a chain... can we see the user any more? */
|
||||
if (m_eFollowingChain != m_eFollowing) {
|
||||
traceline(GetOrigin(), m_eFollowing.origin, MOVE_NORMAL, this);
|
||||
|
||||
/* the answer is no. */
|
||||
if (trace_fraction < 1.0 || trace_ent != m_eFollowing) {
|
||||
/* go straight to our user */
|
||||
NSEntity zamn = (NSEntity)m_eFollowing;
|
||||
vector endPos = zamn.GetNearbySpot();
|
||||
|
||||
if (endPos != g_vec_null) {
|
||||
RouteToPosition(endPos);
|
||||
m_flSequenceSpeed = GetChaseSpeed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
traceline(origin, m_eFollowingChain.origin, MOVE_NORMAL, this);
|
||||
|
||||
/* Tracing failed, there's world geometry in the way */
|
||||
if (trace_fraction < 1.0f) {
|
||||
if (trace_fraction < 1.0f && trace_ent != m_eFollowingChain) {
|
||||
/* are they still generally accessible? */
|
||||
traceline(m_vecLastUserPos, vecParent, MOVE_NORMAL, this);
|
||||
|
||||
if (trace_fraction < 1.0f && trace_ent != m_eFollowing) {
|
||||
RouteToPosition(m_eFollowing.origin); /* go directly to the source */
|
||||
m_flSequenceSpeed = GetChaseSpeed();
|
||||
return;
|
||||
} else {
|
||||
input_angles = vectoangles(m_vecLastUserPos - origin);
|
||||
input_angles[0] = 0;
|
||||
input_angles[1] = Math_FixDelta(input_angles[1]);
|
||||
input_angles[2] = 0;
|
||||
_LerpTurnToYaw(input_angles);
|
||||
} else {
|
||||
m_vecLastUserPos = m_eFollowingChain.origin;
|
||||
}
|
||||
} else {
|
||||
m_vecLastUserPos = vecParent;
|
||||
}
|
||||
|
||||
if (m_bFollowGrouping == false)
|
||||
return;
|
||||
|
||||
/* Trace again to see if another hostage is in our path and if so
|
||||
* follow them instead, this makes pathing easier */
|
||||
|
@ -578,8 +635,7 @@ void
|
|||
NSTalkMonster::PanicFrame(void)
|
||||
{
|
||||
m_iFlags |= MONSTER_METPLAYER;
|
||||
maxspeed = 240;
|
||||
input_movevalues = [maxspeed, 0, 0];
|
||||
input_movevalues = [240, 0, 0];
|
||||
|
||||
if (m_flTraceTime < time) {
|
||||
traceline(origin, origin + (v_forward * 64), FALSE, this);
|
||||
|
@ -625,7 +681,7 @@ NSTalkMonster::RunAI(void)
|
|||
TalkPlayerGreet();
|
||||
FollowChain();
|
||||
|
||||
if (m_eFollowing != world) {
|
||||
if (m_eFollowing != world && m_iNodes <= 0) {
|
||||
m_eLookAt = m_eFollowing;
|
||||
FollowPlayer();
|
||||
} else if (m_iFlags & MONSTER_FEAR) {
|
||||
|
@ -644,6 +700,7 @@ NSTalkMonster::Respawn(void)
|
|||
{
|
||||
super::Respawn();
|
||||
|
||||
RouteClear();
|
||||
m_eFollowing = world;
|
||||
m_eFollowingChain = world;
|
||||
PlayerUse = OnPlayerUse;
|
||||
|
@ -677,6 +734,7 @@ NSTalkMonster::OnPlayerUse(void)
|
|||
} else {
|
||||
TalkUnfollow();
|
||||
m_eFollowing = world;
|
||||
RouteClear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -761,6 +819,13 @@ NSTalkMonster::SpawnKey(string strKey, string strValue)
|
|||
case "follow_on_use":
|
||||
m_bFollowOnUse = ReadBool(strValue);
|
||||
break;
|
||||
|
||||
case "follow_dist":
|
||||
m_flFollowDistance = ReadFloat(strValue);
|
||||
break;
|
||||
case "follow_maxdist":
|
||||
m_flMaxFollowDistance = ReadFloat(strValue);
|
||||
break;
|
||||
default:
|
||||
super::SpawnKey(strKey, strValue);
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue