Do not throw grenades in narrow places.

Removed displaying of "CROSSING" flag for nodes, as it's not used anymore.
Do not aim last / predict enemy in narrow places (like tunnels, when crouching).
This commit is contained in:
jeefo 2020-01-08 18:29:28 +03:00
commit 2aba34a24b
9 changed files with 121 additions and 16 deletions

2
.gitignore vendored
View file

@ -34,8 +34,8 @@ Icon
# Files that might appear on external disk # Files that might appear on external disk
.Spotlight-V100 .Spotlight-V100
.Trashes .Trashes
/project/#Verone
/project/.vs /project/.vs
/project/enc_temp_folder
/.vs /.vs
*.pdb *.pdb

View file

@ -19,6 +19,7 @@ CR_DECLARE_SCOPED_ENUM (NodeFlag,
Camp = cr::bit (7), // node is a camping point Camp = cr::bit (7), // node is a camping point
NoHostage = cr::bit (8), // only use this node if no hostage NoHostage = cr::bit (8), // only use this node if no hostage
DoubleJump = cr::bit (9), // bot help's another bot (requster) to get somewhere (using djump) DoubleJump = cr::bit (9), // bot help's another bot (requster) to get somewhere (using djump)
Narrow = cr::bit (10), // node is inside some small space (corridor or such)
Sniper = cr::bit (28), // it's a specific sniper point Sniper = cr::bit (28), // it's a specific sniper point
TerroristOnly = cr::bit (29), // it's a specific terrorist point TerroristOnly = cr::bit (29), // it's a specific terrorist point
CTOnly = cr::bit (30), // it's a specific ct point CTOnly = cr::bit (30), // it's a specific ct point
@ -255,6 +256,7 @@ private:
bool m_jumpLearnNode; bool m_jumpLearnNode;
bool m_hasChanged; bool m_hasChanged;
bool m_needsVisRebuild; bool m_needsVisRebuild;
bool m_narrowChecked;
Vector m_learnVelocity; Vector m_learnVelocity;
Vector m_learnPosition; Vector m_learnPosition;
@ -321,6 +323,7 @@ public:
void loadVisibility (); void loadVisibility ();
void initNodesTypes (); void initNodesTypes ();
void initLightLevels (); void initLightLevels ();
void initNarrowPlaces ();
void addPath (int addIndex, int pathIndex, float distance); void addPath (int addIndex, int pathIndex, float distance);
void add (int type, const Vector &pos = nullptr); void add (int type, const Vector &pos = nullptr);
void erase (int target); void erase (int target);

View file

@ -872,6 +872,10 @@ private:
return pev->waterlevel >= 2; return pev->waterlevel >= 2;
} }
bool isInNarrowPlace () const {
return (m_pathFlags & NodeFlag::Narrow);
}
public: public:
entvars_t *pev; entvars_t *pev;

View file

@ -117,14 +117,14 @@ bool Bot::seesEntity (const Vector &dest, bool fromBody) {
void Bot::checkGrenadesThrow () { void Bot::checkGrenadesThrow () {
// do not check cancel if we have grenade in out hands // do not check cancel if we have grenade in out hands
bool checkTasks = getCurrentTaskId () == Task::PlantBomb || getCurrentTaskId () == Task::DefuseBomb; bool preventibleTasks = getCurrentTaskId () == Task::PlantBomb || getCurrentTaskId () == Task::DefuseBomb;
auto clearThrowStates = [] (uint32 &states) { auto clearThrowStates = [] (uint32 &states) {
states &= ~(Sense::ThrowExplosive | Sense::ThrowFlashbang | Sense::ThrowSmoke); states &= ~(Sense::ThrowExplosive | Sense::ThrowFlashbang | Sense::ThrowSmoke);
}; };
// check if throwing a grenade is a good thing to do... // check if throwing a grenade is a good thing to do...
if (checkTasks || yb_ignore_enemies.bool_ () || m_isUsingGrenade || m_grenadeRequested || m_isReloading || yb_jasonmode.bool_ () || m_grenadeCheckTime >= game.time ()) { if (preventibleTasks || isInNarrowPlace () || yb_ignore_enemies.bool_ () || m_isUsingGrenade || m_grenadeRequested || m_isReloading || yb_jasonmode.bool_ () || m_grenadeCheckTime >= game.time ()) {
clearThrowStates (m_states); clearThrowStates (m_states);
return; return;
} }
@ -2692,7 +2692,11 @@ void Bot::updateAimDir () {
// don't allow bot to look at danger positions under certain circumstances // don't allow bot to look at danger positions under certain circumstances
if (!(flags & (AimFlags::Grenade | AimFlags::Enemy | AimFlags::Entity))) { if (!(flags & (AimFlags::Grenade | AimFlags::Enemy | AimFlags::Entity))) {
if (isOnLadder () || isInWater () || (m_pathFlags & NodeFlag::Ladder) || (m_currentTravelFlags & PathFlag::Jump)) {
// check if narrow place and we're duck, do not predict enemies in that situation
const bool duckedInNarrowPlace = isInNarrowPlace () && ((m_pathFlags & NodeFlag::Crouch) || (pev->button & IN_DUCK));
if (duckedInNarrowPlace || isOnLadder () || isInWater () || (m_pathFlags & NodeFlag::Ladder) || (m_currentTravelFlags & PathFlag::Jump)) {
flags &= ~(AimFlags::LastEnemy | AimFlags::PredictPath); flags &= ~(AimFlags::LastEnemy | AimFlags::PredictPath);
m_canChooseAimDirection = false; m_canChooseAimDirection = false;
} }
@ -2761,6 +2765,7 @@ void Bot::updateAimDir () {
if (!m_camp.empty ()) { if (!m_camp.empty ()) {
m_lookAt = m_camp; m_lookAt = m_camp;
} }
m_timeNextTracking = game.time () + 2.5f;
} }
} }
else { else {
@ -2771,8 +2776,15 @@ void Bot::updateAimDir () {
m_lookAt = m_camp; m_lookAt = m_camp;
} }
else if (flags & AimFlags::Nav) { else if (flags & AimFlags::Nav) {
if (m_moveToGoal && !m_isStuck && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & NodeFlag::Ladder) && m_pathWalk.hasNext () && (pev->origin - m_destOrigin).lengthSq () < cr::square (52.0f)) { if (m_moveToGoal && !m_isStuck && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && (pev->origin - m_destOrigin).lengthSq () < cr::square (52.0f)) {
m_lookAt = graph[m_pathWalk.next ()].origin + pev->view_ofs; int nextPath = m_pathWalk.next ();
if (graph.isVisible (m_currentNodeIndex, nextPath)) {
m_lookAt = graph[nextPath].origin + pev->view_ofs;
}
else {
m_lookAt = m_destOrigin;
}
} }
else { else {
m_lookAt = m_destOrigin; m_lookAt = m_destOrigin;

View file

@ -529,6 +529,9 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) {
result = -4.5f; result = -4.5f;
} }
} }
else {
result = -5.6f;
}
return result; return result;
} }

View file

@ -848,6 +848,9 @@ void Game::slowFrame () {
// initialize light levels // initialize light levels
graph.initLightLevels (); graph.initLightLevels ();
// initialize corridors
graph.initNarrowPlaces ();
// detect csdm // detect csdm
applyGameModes (); applyGameModes ();

View file

@ -25,6 +25,7 @@ void BotGraph::initGraph () {
m_arrowDisplayTime = 0.0f; m_arrowDisplayTime = 0.0f;
m_autoPathDistance = 250.0f; m_autoPathDistance = 250.0f;
m_hasChanged = false; m_hasChanged = false;
m_narrowChecked = false;
// reset highest recorded damage // reset highest recorded damage
for (int team = Team::Terrorist; team < kGameTeamNum; ++team) { for (int team = Team::Terrorist; team < kGameTeamNum; ++team) {
@ -1296,6 +1297,83 @@ void BotGraph::initLightLevels () {
} }
// disable lightstyle animations on finish (will be auto-enabled on mapchange) // disable lightstyle animations on finish (will be auto-enabled on mapchange)
illum.enableAnimation (false); illum.enableAnimation (false);
}
void BotGraph::initNarrowPlaces () {
// this function checks all nodes if they are inside narrow places. this is used to prevent
// bots to track hidden enemies in narrow places and prevent bots from throwing flashbangs or
// other grenades inside bad places.
// no nodes ?
if (m_paths.empty () || m_narrowChecked) {
return;
}
TraceResult tr;
const auto distance = 178.0f;
const auto worldspawn = game.getStartEntity ();
const auto offset = Vector (0.0f, 0.0f, 16.0f);
// check olny paths that have not too much connections
for (auto &path : m_paths) {
// skip any goals and camp points
if (path.flags & (NodeFlag::Camp | NodeFlag::Goal)) {
continue;
}
int linkCount = 0;
for (const auto &link : path.links) {
if (link.index == kInvalidNodeIndex || link.index == path.number) {
continue;
}
if (++linkCount > kMaxNodeLinks / 2) {
break;
}
}
// skip nodes with too much connections, this indicated we're not in narrow place
if (linkCount > kMaxNodeLinks / 2) {
continue;
}
int accumWeight = 0;
// we could use this one!
for (const auto &link : path.links) {
if (link.index == kInvalidNodeIndex || link.index == path.number) {
continue;
}
const Vector &ang = ((path.origin - m_paths[link.index].origin).normalize () * distance).angles ();
Vector forward, right, upward;
ang.angleVectors (&forward, &right, &upward);
// helper lambda
auto directionCheck = [&] (const Vector &to, int weight) {
game.testLine (path.origin + offset, to, TraceIgnore::None, nullptr, &tr);
// check if we're hit worldspawn entity
if (tr.pHit == worldspawn && tr.flFraction < 1.0f) {
accumWeight += weight;
}
};
directionCheck (-forward * distance, 1);
directionCheck (right * distance, 1);
directionCheck (-right * distance, 1);
directionCheck (upward * distance, 1);
}
path.flags &= ~NodeFlag::Narrow;
if (accumWeight > 1) {
path.flags |= NodeFlag::Narrow;
}
accumWeight = 0;
}
m_narrowChecked = true;
} }
void BotGraph::initNodesTypes () { void BotGraph::initNodesTypes () {
@ -1336,7 +1414,8 @@ bool BotGraph::convertOldFormat () {
plat.bzero (&header, sizeof (header)); plat.bzero (&header, sizeof (header));
// save for faster access // save for faster access
const char *map = game.getMapName (); auto map = game.getMapName ();
if (fp) { if (fp) {
if (fp.read (&header, sizeof (header)) == 0) { if (fp.read (&header, sizeof (header)) == 0) {
@ -2216,7 +2295,7 @@ void BotGraph::frame () {
} }
static String buffer; static String buffer;
buffer.assignf ("%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (path.flags == 0 && !jumpPoint) ? " (none)" : "", (path.flags & NodeFlag::Lift) ? " LIFT" : "", (path.flags & NodeFlag::Crouch) ? " CROUCH" : "", (path.flags & NodeFlag::Crossing) ? " CROSSING" : "", (path.flags & NodeFlag::Camp) ? " CAMP" : "", (path.flags & NodeFlag::TerroristOnly) ? " TERRORIST" : "", (path.flags & NodeFlag::CTOnly) ? " CT" : "", (path.flags & NodeFlag::Sniper) ? " SNIPER" : "", (path.flags & NodeFlag::Goal) ? " GOAL" : "", (path.flags & NodeFlag::Ladder) ? " LADDER" : "", (path.flags & NodeFlag::Rescue) ? " RESCUE" : "", (path.flags & NodeFlag::DoubleJump) ? " JUMPHELP" : "", (path.flags & NodeFlag::NoHostage) ? " NOHOSTAGE" : "", jumpPoint ? " JUMP" : ""); buffer.assignf ("%s%s%s%s%s%s%s%s%s%s%s%s%s", (path.flags == 0 && !jumpPoint) ? " (none)" : "", (path.flags & NodeFlag::Lift) ? " LIFT" : "", (path.flags & NodeFlag::Crouch) ? " CROUCH" : "", (path.flags & NodeFlag::Camp) ? " CAMP" : "", (path.flags & NodeFlag::TerroristOnly) ? " TERRORIST" : "", (path.flags & NodeFlag::CTOnly) ? " CT" : "", (path.flags & NodeFlag::Sniper) ? " SNIPER" : "", (path.flags & NodeFlag::Goal) ? " GOAL" : "", (path.flags & NodeFlag::Ladder) ? " LADDER" : "", (path.flags & NodeFlag::Rescue) ? " RESCUE" : "", (path.flags & NodeFlag::DoubleJump) ? " JUMPHELP" : "", (path.flags & NodeFlag::NoHostage) ? " NOHOSTAGE" : "", jumpPoint ? " JUMP" : "");
// return the message buffer // return the message buffer
return buffer.chars (); return buffer.chars ();

View file

@ -642,9 +642,9 @@ bool Bot::updateNavigation () {
findValidNode (); findValidNode ();
m_pathOrigin = m_path->origin; m_pathOrigin = m_path->origin;
// if wayzone radios non zero vary origin a bit depending on the body angles // if wayzone radius non zero vary origin a bit depending on the body angles
if (m_path->radius > 0.0f) { if (m_path->radius > 0.0f) {
m_pathOrigin = m_pathOrigin + Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f).forward () * rg.float_ (0, m_path->radius); m_pathOrigin = m_pathOrigin + Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f).forward () * rg.float_ (0.0f, m_path->radius);
} }
m_navTimeset = game.time (); m_navTimeset = game.time ();
} }

View file

@ -652,10 +652,12 @@ int32 BotUtils::sendTo (int socket, const void *message, size_t length, int flag
const auto send = [&] (const Twin <const uint8 *, size_t> &msg) -> int32 { const auto send = [&] (const Twin <const uint8 *, size_t> &msg) -> int32 {
return Socket::sendto (socket, msg.first, msg.second, flags, dest, destLength); return Socket::sendto (socket, msg.first, msg.second, flags, dest, destLength);
}; };
auto packet = reinterpret_cast <const uint8 *> (message); auto packet = reinterpret_cast <const uint8 *> (message);
// player replies response // player replies response
if (length > 5 && packet[0] == 0xff && packet[1] == 0xff && packet[2] == 0xff && packet[3] == 0xff) { if (length > 5 && packet[0] == 0xff && packet[1] == 0xff && packet[2] == 0xff && packet[3] == 0xff) {
if (packet[4] == 'D') { if (packet[4] == 'D') {
QueryBuffer buffer (packet, length, 5); QueryBuffer buffer (packet, length, 5);
auto count = buffer.read <uint8> (); auto count = buffer.read <uint8> ();
@ -681,7 +683,6 @@ int32 BotUtils::sendTo (int socket, const void *message, size_t length, int flag
buffer.skipString (); buffer.skipString ();
} }
buffer.skip <short> (); // steam app id buffer.skip <short> (); // steam app id
buffer.skip <uint8> (); // players buffer.skip <uint8> (); // players
buffer.skip <uint8> (); // maxplayers buffer.skip <uint8> (); // maxplayers
buffer.skip <uint8> (); // bots buffer.skip <uint8> (); // bots