BotLib: for goals and goalitem route search, use WorldSpaceCenter() and not their origin to deal with brush triggers and non-existant origins properly

This commit is contained in:
Marco Cawthorne 2023-10-13 08:22:31 -07:00
parent 900cf43bd0
commit ab9d66b2e4
Signed by: eukara
GPG key ID: CE2032F0A2882A22
4 changed files with 116 additions and 116 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016-2022 Vera Visions LLC. * Copyright (c) 2016-2023 Vera Visions LLC.
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -69,11 +69,10 @@ bot::BrainThink(int enemyvisible, int enemydistant)
void void
bot::UseButton(void) bot::UseButton(void)
{ {
#if 1 float bestDist;
float best; func_button bestButton = __NULL__;
func_button best_button = __NULL__;
best = COST_INFINITE; bestDist = COST_INFINITE;
for (entity e = world; (e = find(e, ::classname, "func_button"));) { for (entity e = world; (e = find(e, ::classname, "func_button"));) {
float dist; float dist;
vector pos; vector pos;
@ -82,54 +81,36 @@ bot::UseButton(void)
pos[2] = e.absmin[2] + (0.5 * (e.absmax[2] - e.absmin[2])); pos[2] = e.absmin[2] + (0.5 * (e.absmax[2] - e.absmin[2]));
dist = vlen(origin - pos); dist = vlen(origin - pos);
if (dist < best) { if (dist < bestDist) {
best = dist; bestDist = dist;
best_button = (func_button)e; bestButton = (func_button)e;
} }
} }
if (best_button == __NULL__) if (bestButton == __NULL__) {
return; return;
best_button.Trigger(this, TRIG_TOGGLE);
sound(this, CHAN_ITEM, "common/wpn_select.wav", 0.25, ATTN_IDLE);
#else
float best;
vector foo;
best = COST_INFINITE;
for (entity e = world; (e = find(e, ::classname, "func_button"));) {
float dist;
vector pos;
pos[0] = e.absmin[0] + (0.5 * (e.absmax[0] - e.absmin[0]));
pos[1] = e.absmin[1] + (0.5 * (e.absmax[1] - e.absmin[1]));
pos[2] = e.absmin[2] + (0.5 * (e.absmax[2] - e.absmin[2]));
dist = vlen(origin - pos);
if (dist < best) {
best = dist;
foo = pos;
}
} }
v_angle = vectoangles(origin - foo); bestButton.Trigger(this, TRIG_TOGGLE);
Player_UseDown(); sound(this, CHAN_ITEM, "common/wpn_select.wav", 0.25, ATTN_IDLE);
#endif
} }
void void
bot::SeeThink(void) bot::SeeThink(void)
{ {
CGameRules rules = (CGameRules)g_grMode; NSGameRules rules = (NSGameRules)g_grMode;
/*if (m_eTarget) /*if (m_eTarget)
return; */ return; */
if (m_flSeeTime > time) if (m_flSeeTime > time) {
return; return;
}
if (autocvar_bot_pacifist) /* DEVELOPER CVAR: don't attack */
if (autocvar_bot_dont_shoot) {
return; return;
}
/* reaction time, in a way */ /* reaction time, in a way */
switch (cvar("bot_skill")) { switch (cvar("bot_skill")) {
@ -158,7 +139,7 @@ bot::SeeThink(void)
continue; continue;
/* ain't go hurt our brothers and sisters */ /* ain't go hurt our brothers and sisters */
if (rules.IsTeamplay() == TRUE) if (rules.IsTeamplay() == true)
if (team == w.team) if (team == w.team)
continue; continue;
@ -274,7 +255,7 @@ bot::CheckRoute(void)
input_buttons |= INPUT_BUTTON2; input_buttons |= INPUT_BUTTON2;
} else { } else {
/* entire way-link needs to be crouched. that's the law of the land */ /* entire way-link needs to be crouched. that's the law of the land */
if (Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_CROUCH) if (Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_CROUCH || autocvar_bot_crouch)
input_buttons |= INPUT_BUTTON8; input_buttons |= INPUT_BUTTON8;
} }
} }
@ -288,8 +269,8 @@ bot::CreateObjective(void)
void void
bot::RunAI(void) bot::RunAI(void)
{ {
vector aimdir, aimpos; vector aimDir, aimPos;
int enemyvisible, enemydistant; bool enemyVisible, enemyDistant;
float flLerp; float flLerp;
/* reset input frame */ /* reset input frame */
@ -298,15 +279,15 @@ bot::RunAI(void)
input_angles = [0,0,0]; input_angles = [0,0,0];
/* attempt to respawn when dead */ /* attempt to respawn when dead */
if (health <= 0) { if (IsDead() == true) {
RouteClear(); RouteClear();
WeaponAttack(); WeaponAttack();
SetEnemy(__NULL__); SetEnemy(__NULL__);
return; return;
} }
/* freeze the bot */ /* DEVELOPER CVAR: freeze the bot */
if (autocvar_bot_wait) if (autocvar_bot_stop)
return; return;
/* create our first route */ /* create our first route */
@ -335,66 +316,67 @@ bot::RunAI(void)
m_flEnemyDist = -1; m_flEnemyDist = -1;
} }
enemyvisible = FALSE; enemyVisible = false;
enemydistant = FALSE; enemyDistant = false;
if (m_eTarget != __NULL__) { if (m_eTarget != __NULL__) {
traceline(origin + view_ofs, m_eTarget.origin, TRUE, this); traceline(GetEyePos(), m_eTarget.origin, MOVE_NORMAL, this);
/* is it 'visible'? can we 'see' them? */ /* is it 'visible'? can we 'see' them? */
enemyvisible = (trace_ent == m_eTarget || trace_fraction == 1.0f); enemyVisible = (trace_ent == m_eTarget || trace_fraction == 1.0f);
/* if they're distant, remember that */ /* if they're distant, remember that */
if (m_flEnemyDist > 1024) { if (m_flEnemyDist > 1024.0f) {
enemydistant = TRUE; enemyDistant = true;
} }
/* attack if visible! */ /* attack if visible! */
if (enemyvisible) { if (enemyVisible == true) {
WeaponAttack(); WeaponAttack();
} }
} else if (m_flForceWeaponAttack > time) } else if (m_flForceWeaponAttack > time) {
WeaponAttack(); WeaponAttack();
}
BrainThink(enemyvisible, enemydistant); BrainThink(enemyVisible, enemyDistant);
CheckRoute(); CheckRoute();
aimpos = [0,0,0]; aimPos = [0,0,0];
/* if we've got a path (we always should) move the bot */ /* if we've got a path (we always should) move the bot */
if (m_iNodes) { if (m_iNodes) {
float goroute = 0; bool goRoute = false;
vector vecNewAngles; vector vecNewAngles;
vector vecDirection; vector vecDirection;
/* no enemy, or it isn't visible... then stare at nodes! */ /* no enemy, or it isn't visible... then stare at nodes! */
if (!m_eTarget || !enemyvisible) { if (!m_eTarget || enemyVisible == false) {
/* aim at the next node */ /* aim at the next node */
if (m_iCurNode == BOTROUTE_DESTINATION) if (m_iCurNode == BOTROUTE_DESTINATION)
aimpos = m_vecLastNode; aimPos = m_vecLastNode;
else { else {
if (m_iCurNode > 0 && !(Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_AIM)) if (m_iCurNode > 0 && !(Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_AIM))
aimpos = m_pRoute[m_iCurNode - 1].dest; aimPos = m_pRoute[m_iCurNode - 1].dest;
else else
aimpos = m_pRoute[m_iCurNode].dest; aimPos = m_pRoute[m_iCurNode].dest;
} }
} else { } else {
/* aim towards the enemy */ /* aim towards the enemy */
aimpos = m_eTarget.origin; aimPos = m_eTarget.origin;
} }
/* force bot to fire at a position if desired */ /* force bot to fire at a position if desired */
if (m_flForceWeaponAttack > time) if (m_flForceWeaponAttack > time)
aimpos = m_vecForceWeaponAttackPos; aimPos = m_vecForceWeaponAttackPos;
/* aim ahead if aimpos is somehow invalid */ /* aim ahead if aimPos is somehow invalid */
if (aimpos == [0,0,0]) { if (aimPos == [0,0,0]) {
makevectors(angles); makevectors(angles);
aimpos = origin + v_forward * 128; aimPos = origin + v_forward * 128;
} }
/* lerping speed, faster when we've got a target */ /* lerping speed, faster when we've got a target */
if (m_eTarget && enemyvisible) if (m_eTarget && enemyVisible == true)
flLerp = bound(0.0f, frametime * 45, 1.0f); flLerp = bound(0.0f, frametime * 45, 1.0f);
else else
flLerp = bound(0.0f, frametime * 30, 1.0f); flLerp = bound(0.0f, frametime * 30, 1.0f);
@ -403,9 +385,9 @@ bot::RunAI(void)
makevectors(v_angle); makevectors(v_angle);
vecNewAngles = v_forward; vecNewAngles = v_forward;
/* aimdir = new final angle */ /* aimDir = new final angle */
aimdir = vectoangles(aimpos - origin); aimDir = vectoangles(aimPos - origin);
makevectors(aimdir); makevectors(aimDir);
/* slowly lerp towards the final angle */ /* slowly lerp towards the final angle */
vecNewAngles[0] = Math_Lerp(vecNewAngles[0], v_forward[0], flLerp); vecNewAngles[0] = Math_Lerp(vecNewAngles[0], v_forward[0], flLerp);
@ -422,53 +404,53 @@ bot::RunAI(void)
angles[2] = Math_FixDelta(v_angle[2]); angles[2] = Math_FixDelta(v_angle[2]);
input_angles = v_angle; input_angles = v_angle;
float shouldwalk = 0; bool shouldWalk = autocvar_bot_walk;
if (m_wtWeaponType == WPNTYPE_RANGED) { if (m_wtWeaponType == WPNTYPE_RANGED) {
other = world;
if (m_eTarget) { if (m_eTarget) {
other = world;
tracebox(origin, m_eTarget.origin, mins, maxs, MOVE_OTHERONLY, this); tracebox(origin, m_eTarget.origin, mins, maxs, MOVE_OTHERONLY, this);
/* walk _directly_ towards the enemy if we're less than 512 units away */ /* walk _directly_ towards the enemy if we're less than 512 units away */
if (trace_fraction >= 1.0 && m_eTarget && enemyvisible && m_eTarget.health < 50 && m_flEnemyDist < 512) { if (trace_fraction >= 1.0 && m_eTarget && enemyVisible && m_eTarget.health < 50 && m_flEnemyDist < 512) {
aimpos = m_eTarget.origin; aimPos = m_eTarget.origin;
goroute = 1; goRoute = true;
} else { } else {
goroute = 1; goRoute = true;
} }
} else { } else {
goroute = 1; goRoute = true;
} }
/* we should probably walk we're distant enough to be more accurate */ /* we should probably walk we're distant enough to be more accurate */
if ((m_eTarget && enemyvisible && m_flEnemyDist < 512)) if ((m_eTarget && enemyVisible && m_flEnemyDist < 512))
shouldwalk = 1; shouldWalk = true;
} else if (m_wtWeaponType == WPNTYPE_CLOSE) { } else if (m_wtWeaponType == WPNTYPE_CLOSE) {
/* move directly towards the enemy if we're 256 units away */ /* move directly towards the enemy if we're 256 units away */
if (m_eTarget && enemyvisible && m_flEnemyDist < 256) { if (m_eTarget && enemyVisible && m_flEnemyDist < 256) {
/* we are far away, inch closer */ /* we are far away, inch closer */
aimpos = m_eTarget.origin; aimPos = m_eTarget.origin;
//printf("going to target\n"); //printf("going to target\n");
} else { } else {
goroute = 1; goRoute = true;
} }
} else if (m_wtWeaponType == WPNTYPE_THROW) { } else if (m_wtWeaponType == WPNTYPE_THROW) {
if ((m_eTarget && enemyvisible && !enemydistant) && m_flEnemyDist < 512) { if ((m_eTarget && enemyVisible && !enemyDistant) && m_flEnemyDist < 512) {
aimpos = m_eTarget.origin; aimPos = m_eTarget.origin;
//printf("going to target\n"); //printf("going to target\n");
} else { } else {
goroute = 1; goRoute = true;
} }
} else { } else {
goroute = 1; goRoute = true;
} }
if (goroute) { if (goRoute) {
if (m_iCurNode <= BOTROUTE_DESTINATION) { if (m_iCurNode <= BOTROUTE_DESTINATION) {
aimpos = m_vecLastNode; aimPos = m_vecLastNode;
//printf("going to last node\n"); //printf("going to last node\n");
} else { } else {
aimpos = m_pRoute[m_iCurNode].dest; aimPos = m_pRoute[m_iCurNode].dest;
//printf("going to next node\n"); //printf("going to next node\n");
} }
} else { } else {
@ -476,7 +458,7 @@ bot::RunAI(void)
} }
/* if there's a breakable in the way... */ /* if there's a breakable in the way... */
traceline(origin, aimpos, MOVE_NORMAL, this); traceline(origin, aimPos, MOVE_NORMAL, this);
/* Hackish: If there's a func_breakable in the way... */ /* Hackish: If there's a func_breakable in the way... */
if (trace_ent.classname == "func_breakable") { if (trace_ent.classname == "func_breakable") {
@ -485,10 +467,10 @@ bot::RunAI(void)
} }
/* now we'll set the movevalues relative to the input_angle */ /* now we'll set the movevalues relative to the input_angle */
if ((m_iCurNode >= 0 && Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_WALK) || shouldwalk) if ((m_iCurNode >= 0 && Route_GetNodeFlags(&m_pRoute[m_iCurNode]) & LF_WALK) || shouldWalk)
vecDirection = normalize(aimpos - origin) * GetWalkSpeed(); vecDirection = normalize(aimPos - origin) * GetWalkSpeed();
else else
vecDirection = normalize(aimpos - origin) * GetRunSpeed(); vecDirection = normalize(aimPos - origin) * GetRunSpeed();
makevectors(input_angles); makevectors(input_angles);
input_movevalues = [v_forward * vecDirection, v_right * vecDirection, v_up * vecDirection]; input_movevalues = [v_forward * vecDirection, v_right * vecDirection, v_up * vecDirection];
@ -496,7 +478,7 @@ bot::RunAI(void)
#if 1 #if 1
/* duck and stand still when our enemy seems strong */ /* duck and stand still when our enemy seems strong */
if (m_eTarget && enemyvisible && m_eTarget.health >= 75) { if (m_eTarget && enemyVisible && m_eTarget.health >= 75) {
if (m_wtWeaponType == WPNTYPE_RANGED) { if (m_wtWeaponType == WPNTYPE_RANGED) {
input_buttons |= INPUT_BUTTON8; input_buttons |= INPUT_BUTTON8;
input_movevalues = [0,0,0]; input_movevalues = [0,0,0];

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016-2022 Vera Visions LLC. * Copyright (c) 2016-2023 Vera Visions LLC.
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -17,7 +17,7 @@
void void
bot::Pain(void) bot::Pain(void)
{ {
CGameRules rules = g_grMode; NSGameRules rules = g_grMode;
super::Pain(); super::Pain();
@ -33,11 +33,13 @@ bot::Pain(void)
float enemydist = vlen(origin - m_eTarget.origin); float enemydist = vlen(origin - m_eTarget.origin);
float newdist = vlen(origin - g_dmg_eAttacker.origin); float newdist = vlen(origin - g_dmg_eAttacker.origin);
if (m_eTarget) if (m_eTarget) {
if (newdist < enemydist) if (newdist < enemydist) {
SetEnemy(g_dmg_eAttacker); SetEnemy(g_dmg_eAttacker);
else }
} else {
SetEnemy(g_dmg_eAttacker); SetEnemy(g_dmg_eAttacker);
}
} }
} }
@ -46,10 +48,11 @@ bot::SetEnemy(entity en)
{ {
m_eTarget = en; m_eTarget = en;
if (m_eTarget) if (m_eTarget) {
m_flEnemyDist = vlen(origin - m_eTarget.origin); m_flEnemyDist = vlen(origin - m_eTarget.origin);
else } else {
m_flEnemyDist = -1; m_flEnemyDist = -1;
}
} }
void void
@ -59,9 +62,11 @@ bot::WeaponThink(void)
/* clip empty, but the whole weapon isn't */ /* clip empty, but the whole weapon isn't */
if (r == 0 && a_ammo1 <= 0) { if (r == 0 && a_ammo1 <= 0) {
/* stop fire, tap reload */
input_buttons &= ~INPUT_BUTTON0; input_buttons &= ~INPUT_BUTTON0;
input_buttons |= INPUT_BUTTON4; input_buttons |= INPUT_BUTTON4;
} else if (r == 1) { } else if (r == 1) {
/* if empty, switch to the next best weapon */
Weapons_SwitchBest(this, activeweapon); Weapons_SwitchBest(this, activeweapon);
} }
@ -71,27 +76,27 @@ bot::WeaponThink(void)
void void
bot::WeaponAttack(void) bot::WeaponAttack(void)
{ {
int should_attack = 0; bool shouldAttack = false;
/* only attack when the type's suggested distance makes sense to */ /* only attack when the type's suggested distance makes sense to */
if (m_wtWeaponType == WPNTYPE_RANGED) { if (m_wtWeaponType == WPNTYPE_RANGED) {
if (m_flEnemyDist <= 1024) if (m_flEnemyDist <= 1024)
should_attack = 1; shouldAttack = true;
} else if (m_wtWeaponType == WPNTYPE_THROW) { } else if (m_wtWeaponType == WPNTYPE_THROW) {
if (m_flEnemyDist <= 512) if (m_flEnemyDist <= 512)
should_attack = 1; shouldAttack = true;
} else if (m_wtWeaponType == WPNTYPE_CLOSE) { } else if (m_wtWeaponType == WPNTYPE_CLOSE) {
if (m_flEnemyDist <= 128) if (m_flEnemyDist <= 128)
should_attack = 1; shouldAttack = true;
} else { } else {
if (m_flEnemyDist <= 1024) if (m_flEnemyDist <= 1024)
should_attack = 1; shouldAttack = true;
} }
if (m_flForceWeaponAttack > time) if (m_flForceWeaponAttack > time)
should_attack = 1; shouldAttack = true;
if (should_attack && m_flAttackTime < time) { if (shouldAttack && m_flAttackTime < time) {
if (!m_iAttackMode) { if (!m_iAttackMode) {
input_buttons |= INPUT_BUTTON0; input_buttons |= INPUT_BUTTON0;
} }
@ -136,7 +141,7 @@ var float g_botalert_timer;
void void
BotLib_Alert(vector pos, float radius, float t) BotLib_Alert(vector pos, float radius, float t)
{ {
CGameRules rules = g_grMode; NSGameRules rules = g_grMode;
/* sometimes many alert-sounds happen at once... we don't really want /* sometimes many alert-sounds happen at once... we don't really want
* that */ * that */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016-2022 Vera Visions LLC. * Copyright (c) 2016-2023 Vera Visions LLC.
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -14,13 +14,22 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
var int autocvar_bot_pacifist = FALSE;
var int autocvar_bot_wait = FALSE;
var int autocvar_bot_aimless = FALSE; var int autocvar_bot_aimless = FALSE;
var int autocvar_nav_linksize = 256; var int autocvar_nav_linksize = 256;
var int autocvar_nav_radius = 8; var int autocvar_nav_radius = 8;
var bool autocvar_bot_crouch = false;
var bool autocvar_bot_walk = false;
var bool autocvar_bot_stop = false;
var bool autocvar_bot_dont_shoot = false;
var bool autocvar_bot_join_after_player = false;
var float autocvar_bot_join_delay = 0.0f;
var int autocvar_bot_quota = 0i;
var string autocvar_bot_quota_mode = "normal";
var string autocvar_bot_chatter = "normal";
typedef enum typedef enum
{ {
BOTSKILL_EASY = 1, BOTSKILL_EASY = 1,
@ -28,6 +37,6 @@ typedef enum
BOTSKILL_HARD BOTSKILL_HARD
} botskill_t; } botskill_t;
var botskill_t autocvar_bot_skill = 2; var botskill_t autocvar_bot_skill = BOTSKILL_MEDIUM;
var string autocvar_bot_prefix = ""; var string autocvar_bot_prefix = "";

View file

@ -94,10 +94,11 @@ Route_SelectNearestTeam(float type, vector org, float tt)
entity dest = __NULL__; entity dest = __NULL__;
for (temp = world; (temp = findfloat(temp, ::botinfo, type));) { for (temp = world; (temp = findfloat(temp, ::botinfo, type));) {
NSEntity tempEnt = (NSEntity)temp;
if (temp.team != tt) if (temp.team != tt)
continue; continue;
range = vlen(temp.origin - org); range = vlen(tempEnt.WorldSpaceCenter() - org);
if ((range < bestrange) && (temp.solid != SOLID_NOT)) { if ((range < bestrange) && (temp.solid != SOLID_NOT)) {
bestrange = range; bestrange = range;
@ -118,10 +119,11 @@ Route_SelectNearestEnemyTeam(float type, vector org, float tt)
entity dest = __NULL__; entity dest = __NULL__;
for (temp = world; (temp = findfloat(temp, ::botinfo, type));) { for (temp = world; (temp = findfloat(temp, ::botinfo, type));) {
NSEntity tempEnt = (NSEntity)temp;
if (temp.team == tt) if (temp.team == tt)
continue; continue;
range = vlen(temp.origin - org); range = vlen(tempEnt.WorldSpaceCenter() - org);
if ((range < bestrange) && (temp.solid != SOLID_NOT)) { if ((range < bestrange) && (temp.solid != SOLID_NOT)) {
bestrange = range; bestrange = range;
@ -169,7 +171,7 @@ Route_SelectDestination(bot target)
CGameRules rules; CGameRules rules;
rules = (CGameRules)g_grMode; rules = (CGameRules)g_grMode;
entity dest = world; NSEntity dest = __NULL__;
if (rules.IsTeamplay()) { if (rules.IsTeamplay()) {
/* we have the goal item, so capture it */ /* we have the goal item, so capture it */
@ -187,10 +189,12 @@ Route_SelectDestination(bot target)
} }
if (dest != __NULL__) { if (dest != __NULL__) {
target.m_vecLastPOI = dest.origin; target.m_vecLastPOI = dest.WorldSpaceCenter();
return dest.origin + [0,0,32]; return dest.WorldSpaceCenter() + [0,0,32];
} }
/* by now, they need something else to do involving goal items probably */
print(sprintf("%s can't figure out where to go for the goal\n", target.netname)); print(sprintf("%s can't figure out where to go for the goal\n", target.netname));
} }
@ -200,7 +204,7 @@ Route_SelectDestination(bot target)
dest = Route_SelectNearest(BOTINFO_HEALTH, target.origin, target.m_vecLastPOI); dest = Route_SelectNearest(BOTINFO_HEALTH, target.origin, target.m_vecLastPOI);
if (dest != __NULL__) { if (dest != __NULL__) {
target.m_vecLastPOI = dest.origin; target.m_vecLastPOI = dest.WorldSpaceCenter();
return dest.origin + [0,0,32]; return dest.origin + [0,0,32];
} }