bot: refactor and clean up old code
fix: crash when saving old format pwf on hlds bot: moved sdk headers to separate submodule nav: improved unstuck and avoidance (thanks @commandcobra7) code bot: use correct path slashes depending on platform for all data cfg: removed simplified chines' translation, as it's too outdated
This commit is contained in:
parent
48e157c7b4
commit
7b58d51973
42 changed files with 365 additions and 3805 deletions
|
|
@ -765,6 +765,10 @@ void Bot::instantChatter (int type) {
|
|||
MessageWriter msg;
|
||||
int ownIndex = index ();
|
||||
|
||||
auto writeChatterSound = [&msg] (ChatterItem item) {
|
||||
msg.writeString (strings.format ("%s%s%s.wav", cv_chatter_path.str (), PATH_SEP, item.name));
|
||||
};
|
||||
|
||||
for (auto &client : util.getClients ()) {
|
||||
if (!(client.flags & ClientFlags::Used) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) {
|
||||
continue;
|
||||
|
|
@ -774,11 +778,11 @@ void Bot::instantChatter (int type) {
|
|||
|
||||
if (pev->deadflag & DEAD_DYING) {
|
||||
client.iconTimestamp[ownIndex] = game.time () + painSound.duration;
|
||||
msg.writeString (strings.format ("%s/%s.wav", cv_chatter_path.str (), painSound.name));
|
||||
writeChatterSound (painSound);
|
||||
}
|
||||
else if (!(pev->deadflag & DEAD_DEAD)) {
|
||||
else if (m_notKilled) {
|
||||
client.iconTimestamp[ownIndex] = game.time () + playbackSound.duration;
|
||||
msg.writeString (strings.format ("%s/%s.wav", cv_chatter_path.str (), playbackSound.name));
|
||||
writeChatterSound (playbackSound);
|
||||
}
|
||||
msg.writeShort (m_voicePitch).end ();
|
||||
client.iconFlags[ownIndex] |= ClientFlags::Icon;
|
||||
|
|
@ -1527,6 +1531,15 @@ void Bot::overrideConditions () {
|
|||
|
||||
// special handling for reloading
|
||||
if (!bots.isRoundOver () && getCurrentTaskId () == Task::Normal && m_reloadState != Reload::None && m_isReloading && !isDucking () && !isInNarrowPlace ()) {
|
||||
if (m_reloadState != Reload::None || m_isReloading) {
|
||||
const auto maxClip = conf.findWeaponById (m_currentWeapon).maxClip;
|
||||
const auto curClip = getAmmoInClip ();
|
||||
|
||||
// consider not reloading if full ammo in clip
|
||||
if (curClip >= maxClip) {
|
||||
m_isReloading = false;
|
||||
}
|
||||
}
|
||||
if (m_seeEnemyTime + 2.5f < game.time () && (m_states & (Sense::SuspectEnemy | Sense::HearingEnemy))) {
|
||||
m_moveSpeed = m_fearLevel > m_agressionLevel ? 0.0f : getShiftSpeed ();
|
||||
m_navTimeset = game.time ();
|
||||
|
|
@ -1550,7 +1563,7 @@ void Bot::syncUpdatePredictedIndex () {
|
|||
auto currentNodeIndex = m_currentNodeIndex;
|
||||
auto botOrigin = pev->origin;
|
||||
|
||||
if (lastEnemyOrigin.empty () || !vistab.isReady ()) {
|
||||
if (lastEnemyOrigin.empty () || !vistab.isReady () || !util.isAlive (m_enemy)) {
|
||||
wipePredict ();
|
||||
return;
|
||||
}
|
||||
|
|
@ -1583,6 +1596,9 @@ void Bot::syncUpdatePredictedIndex () {
|
|||
}
|
||||
|
||||
void Bot::updatePredictedIndex () {
|
||||
if (m_lastEnemyOrigin.empty ()) {
|
||||
return; // do not run task if no last enemy
|
||||
}
|
||||
worker.enqueue ([this] () {
|
||||
syncUpdatePredictedIndex ();
|
||||
});
|
||||
|
|
@ -2651,11 +2667,6 @@ void Bot::update () {
|
|||
kick ();
|
||||
return;
|
||||
}
|
||||
pev->button = 0;
|
||||
|
||||
m_moveSpeed = 0.0f;
|
||||
m_strafeSpeed = 0.0f;
|
||||
m_moveAngles = nullptr;
|
||||
|
||||
m_canChooseAimDirection = true;
|
||||
m_notKilled = util.isAlive (ent ());
|
||||
|
|
@ -2714,6 +2725,9 @@ void Bot::update () {
|
|||
else if (pev->maxspeed < 10.0f) {
|
||||
logicDuringFreezetime ();
|
||||
}
|
||||
else if (!botMovement) {
|
||||
resetMovement ();
|
||||
}
|
||||
runMovement ();
|
||||
|
||||
// delay next execution
|
||||
|
|
@ -2826,6 +2840,8 @@ void Bot::logic () {
|
|||
|
||||
float movedDistance = 2.0f; // length of different vector (distance bot moved)
|
||||
|
||||
resetMovement ();
|
||||
|
||||
// increase reaction time
|
||||
m_actualReactionTime += 0.3f;
|
||||
|
||||
|
|
@ -2977,6 +2993,7 @@ void Bot::logic () {
|
|||
|
||||
// save the previous speed (for checking if stuck)
|
||||
m_prevSpeed = cr::abs (m_moveSpeed);
|
||||
m_prevVelocity = cr::abs (pev->velocity.length2d ());
|
||||
m_lastDamageType = -1; // reset damage
|
||||
}
|
||||
|
||||
|
|
@ -3448,7 +3465,7 @@ Vector Bot::isBombAudible () {
|
|||
}
|
||||
|
||||
// we hear bomb if length greater than radius
|
||||
if (desiredRadius < pev->origin.distance2d (bombOrigin)) {
|
||||
if (cr::sqrf (desiredRadius) < pev->origin.distanceSq2d (bombOrigin)) {
|
||||
return bombOrigin;
|
||||
}
|
||||
return nullptr;
|
||||
|
|
@ -3457,7 +3474,7 @@ Vector Bot::isBombAudible () {
|
|||
uint8_t Bot::computeMsec () {
|
||||
// estimate msec to use for this command based on time passed from the previous command
|
||||
|
||||
return static_cast <uint8_t> ((game.time () - m_lastCommandTime) * 1000.0f);
|
||||
return static_cast <uint8_t> (cr::min (static_cast <int32_t> ((game.time () - m_lastCommandTime) * 1000.0f), 255));
|
||||
}
|
||||
|
||||
bool Bot::canRunHeavyWeight () {
|
||||
|
|
@ -3471,20 +3488,6 @@ bool Bot::canRunHeavyWeight () {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Bot::canSkipNextTrace (TraceChannel channel) {
|
||||
// for optmization purposes skip every second traceline fired, this doesn't affect ai
|
||||
// behaviour too much, but saves alot of cpu cycles.
|
||||
|
||||
constexpr auto kSkipTrace = 3;
|
||||
|
||||
// check if we're have to skip
|
||||
if ((++m_traceSkip[channel] % kSkipTrace) == 0) {
|
||||
m_traceSkip[channel] = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::runMovement () {
|
||||
// the purpose of this function is to compute, according to the specified computation
|
||||
// method, the msec value which will be passed as an argument of pfnRunPlayerMove. This
|
||||
|
|
@ -3761,7 +3764,7 @@ bool Bot::isBombDefusing (const Vector &bombOrigin) {
|
|||
}
|
||||
|
||||
float Bot::getShiftSpeed () {
|
||||
if (getCurrentTaskId () == Task::SeekCover || isDucking () || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK) || (m_currentTravelFlags & PathFlag::Jump) || (m_path != nullptr && m_pathFlags & NodeFlag::Ladder) || isOnLadder () || isInWater () || m_isStuck) {
|
||||
if (getCurrentTaskId () == Task::SeekCover || isDucking () || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK) || (m_currentTravelFlags & PathFlag::Jump) || (m_pathFlags & NodeFlag::Ladder) || isOnLadder () || isInWater () || m_isStuck) {
|
||||
return pev->maxspeed;
|
||||
}
|
||||
return pev->maxspeed * 0.4f;
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ bool Bot::checkBodyParts (edict_t *target) {
|
|||
else {
|
||||
spot.z = target->v.origin.z - standFeet;
|
||||
}
|
||||
game.testLineChannel (TraceChannel::Enemy, eyes, spot, ignoreFlags, self, result);
|
||||
game.testLine (eyes, spot, ignoreFlags, self, &result);
|
||||
|
||||
if (hitsTarget ()) {
|
||||
m_enemyParts |= Visibility::Other;
|
||||
|
|
@ -184,7 +184,7 @@ bool Bot::checkBodyParts (edict_t *target) {
|
|||
Vector perp (-dir.y, dir.x, 0.0f);
|
||||
spot = target->v.origin + Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0);
|
||||
|
||||
game.testLineChannel (TraceChannel::Enemy, eyes, spot, ignoreFlags, self, result);
|
||||
game.testLine (eyes, spot, ignoreFlags, self, &result);
|
||||
|
||||
if (hitsTarget ()) {
|
||||
m_enemyParts |= Visibility::Other;
|
||||
|
|
@ -194,7 +194,7 @@ bool Bot::checkBodyParts (edict_t *target) {
|
|||
}
|
||||
spot = target->v.origin - Vector (perp.x * edgeOffset, perp.y * edgeOffset, 0);
|
||||
|
||||
game.testLineChannel (TraceChannel::Enemy, eyes, spot, ignoreFlags, self, result);
|
||||
game.testLine (eyes, spot, ignoreFlags, self, &result);
|
||||
|
||||
if (hitsTarget ()) {
|
||||
m_enemyParts |= Visibility::Other;
|
||||
|
|
@ -719,12 +719,9 @@ bool Bot::isPenetrableObstacle2 (const Vector &dest) {
|
|||
bool Bot::isPenetrableObstacle3 (const Vector &dest) {
|
||||
// this function returns if enemy can be shoot through some obstacle
|
||||
|
||||
if (m_isUsingGrenade || m_difficulty < Difficulty::Normal || !conf.findWeaponById (m_currentWeapon).penetratePower) {
|
||||
return false;
|
||||
}
|
||||
auto power = conf.findWeaponById (m_currentWeapon).penetratePower;
|
||||
|
||||
if (power == 0) {
|
||||
if (m_isUsingGrenade || m_difficulty < Difficulty::Normal || !power) {
|
||||
return false;
|
||||
}
|
||||
TraceResult tr {};
|
||||
|
|
@ -1218,7 +1215,7 @@ void Bot::attackMovement () {
|
|||
};
|
||||
|
||||
auto strafeUpdateTime = [] () {
|
||||
return game.time () + rg.get (0.5f, 1.0f);
|
||||
return game.time () + rg.get (1.0f, 1.5f);
|
||||
};
|
||||
|
||||
// to start strafing, we have to first figure out if the target is on the left side or right side
|
||||
|
|
@ -1239,11 +1236,14 @@ void Bot::attackMovement () {
|
|||
m_strafeSetTime = strafeUpdateTime ();
|
||||
}
|
||||
|
||||
const bool wallOnRight = checkWallOnRight ();
|
||||
const bool wallOnLeft = checkWallOnLeft ();
|
||||
|
||||
if (m_combatStrafeDir == Dodge::Right) {
|
||||
if (!checkWallOnLeft ()) {
|
||||
if (!wallOnLeft) {
|
||||
m_strafeSpeed = -pev->maxspeed;
|
||||
}
|
||||
else if (!checkWallOnRight ()) {
|
||||
else if (!wallOnRight) {
|
||||
swapStrafeCombatDir ();
|
||||
m_strafeSetTime = strafeUpdateTime ();
|
||||
|
||||
|
|
@ -1255,10 +1255,10 @@ void Bot::attackMovement () {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if (!checkWallOnRight ()) {
|
||||
if (!wallOnRight) {
|
||||
m_strafeSpeed = pev->maxspeed;
|
||||
}
|
||||
else if (!checkWallOnLeft ()) {
|
||||
else if (!wallOnLeft) {
|
||||
swapStrafeCombatDir ();
|
||||
m_strafeSetTime = strafeUpdateTime ();
|
||||
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ void BotConfig::loadMainConfig (bool isFirstLoad) {
|
|||
String line;
|
||||
MemFile file;
|
||||
|
||||
// this is does the same as exec of engine, but not overwriting values of cvars spcified in cv_ignore_cvars_on_changelevel
|
||||
if (util.openConfig (strings.format ("%s.cfg", product.folder), "Bot main config file is not found.", &file, false)) {
|
||||
// this is does the same as exec of engine, but not overwriting values of cvars specified in cv_ignore_cvars_on_changelevel
|
||||
if (util.openConfig (product.nameLower, "Bot main config file is not found.", &file, false)) {
|
||||
while (file.getLine (line)) {
|
||||
line.trim ();
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ void BotConfig::loadNamesConfig () {
|
|||
MemFile file;
|
||||
|
||||
// naming initialization
|
||||
if (util.openConfig ("names.cfg", "Name configuration file not found.", &file, true)) {
|
||||
if (util.openConfig ("names", "Name configuration file not found.", &file, true)) {
|
||||
m_botNames.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
|
|
@ -172,7 +172,7 @@ void BotConfig::loadWeaponsConfig () {
|
|||
MemFile file;
|
||||
|
||||
// weapon data initialization
|
||||
if (util.openConfig ("weapon.cfg", "Weapon configuration file not found. Loading defaults.", &file)) {
|
||||
if (util.openConfig ("weapon", "Weapon configuration file not found. Loading defaults.", &file)) {
|
||||
while (file.getLine (line)) {
|
||||
line.trim ();
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ void BotConfig::loadChatterConfig () {
|
|||
MemFile file;
|
||||
|
||||
// chatter initialization
|
||||
if (game.is (GameFlags::HasBotVoice) && cv_radio_mode.int_ () == 2 && util.openConfig ("chatter.cfg", "Couldn't open chatter system configuration", &file)) {
|
||||
if (game.is (GameFlags::HasBotVoice) && cv_radio_mode.int_ () == 2 && util.openConfig ("chatter", "Couldn't open chatter system configuration", &file)) {
|
||||
m_chatter.clear ();
|
||||
|
||||
struct EventMap {
|
||||
|
|
@ -332,7 +332,7 @@ void BotConfig::loadChatterConfig () {
|
|||
items[1].trim ("(;)");
|
||||
|
||||
for (const auto &event : chatterEventMap) {
|
||||
if (event.str == items[0]) {
|
||||
if (event.str == items.first ()) {
|
||||
// this does common work of parsing comma-separated chatter line
|
||||
auto sentences = items[1].split (",");
|
||||
|
||||
|
|
@ -364,7 +364,7 @@ void BotConfig::loadChatConfig () {
|
|||
MemFile file;
|
||||
|
||||
// chat config initialization
|
||||
if (util.openConfig ("chat.cfg", "Chat file not found.", &file, true)) {
|
||||
if (util.openConfig ("chat", "Chat file not found.", &file, true)) {
|
||||
StringArray *chat = nullptr;
|
||||
|
||||
StringArray keywords {};
|
||||
|
|
@ -467,7 +467,7 @@ void BotConfig::loadLanguageConfig () {
|
|||
MemFile file;
|
||||
|
||||
// localizer inititalization
|
||||
if (util.openConfig ("lang.cfg", "Specified language not found.", &file, true)) {
|
||||
if (util.openConfig ("lang", "Specified language not found.", &file, true)) {
|
||||
String temp;
|
||||
Twin <String, String> lang;
|
||||
|
||||
|
|
@ -513,7 +513,7 @@ void BotConfig::loadAvatarsConfig () {
|
|||
MemFile file;
|
||||
|
||||
// avatars inititalization
|
||||
if (util.openConfig ("avatars.cfg", "Avatars config file not found. Avatars will not be displayed.", &file)) {
|
||||
if (util.openConfig ("avatars", "Avatars config file not found. Avatars will not be displayed.", &file)) {
|
||||
m_avatars.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
|
|
@ -577,7 +577,7 @@ void BotConfig::loadDifficultyConfig () {
|
|||
};
|
||||
|
||||
// avatars inititalization
|
||||
if (util.openConfig ("difficulty.cfg", "Difficulty config file not found. Loading defaults.", &file)) {
|
||||
if (util.openConfig ("difficulty", "Difficulty config file not found. Loading defaults.", &file)) {
|
||||
|
||||
while (file.getLine (line)) {
|
||||
if (isCommentLine (line) || line.length () < 3) {
|
||||
|
|
@ -612,10 +612,10 @@ void BotConfig::loadDifficultyConfig () {
|
|||
}
|
||||
|
||||
void BotConfig::loadMapSpecificConfig () {
|
||||
auto mapSpecificConfig = strings.format ("addons/%s/conf/maps/%s.cfg", product.folder, game.getMapName ());
|
||||
auto mapSpecificConfig = strings.joinPath (folders.addons, folders.bot, folders.config, "maps", strings.format ("%s.%s", game.getMapName (), kConfigExtension));
|
||||
|
||||
// check existence of file
|
||||
if (File::exists (strings.format ("%s/%s", game.getRunningModName (), mapSpecificConfig))) {
|
||||
if (plat.fileExists (strings.joinPath (game.getRunningModName (), mapSpecificConfig).chars ())) {
|
||||
game.serverCommand ("exec %s", mapSpecificConfig);
|
||||
|
||||
ctrl.msg ("Executed map-specific config: %s", mapSpecificConfig);
|
||||
|
|
@ -630,7 +630,7 @@ void BotConfig::loadCustomConfig () {
|
|||
m_custom["AMXParachuteCvar"] = "sv_parachute";
|
||||
|
||||
// custom inititalization
|
||||
if (util.openConfig ("custom.cfg", "Custom config file not found. Loading defaults.", &file)) {
|
||||
if (util.openConfig ("custom", "Custom config file not found. Loading defaults.", &file)) {
|
||||
m_custom.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
|
|
@ -642,7 +642,7 @@ void BotConfig::loadCustomConfig () {
|
|||
auto values = line.split ("=");
|
||||
|
||||
if (values.length () != 2) {
|
||||
logger.error ("Bad configuration for custom.cfg");
|
||||
logger.error ("Bad configuration for custom.%s", kConfigExtension);
|
||||
return;
|
||||
}
|
||||
auto kv = Twin <String, String> (values[0].trim (), values[1].trim ());
|
||||
|
|
@ -661,7 +661,7 @@ void BotConfig::loadLogosConfig () {
|
|||
MemFile file;
|
||||
|
||||
// logos inititalization
|
||||
if (util.openConfig ("logos.cfg", "Logos config file not found. Loading defaults.", &file)) {
|
||||
if (util.openConfig ("logos", "Logos config file not found. Loading defaults.", &file)) {
|
||||
m_logos.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
|
|
@ -688,7 +688,7 @@ void BotConfig::setupMemoryFiles () {
|
|||
};
|
||||
|
||||
if (setMemoryPointers) {
|
||||
MemFileStorage::instance ().initizalize (wrapLoadFile, wrapFreeFile);
|
||||
MemFileStorage::instance ().initizalize (cr::move (wrapLoadFile), cr::move (wrapFreeFile));
|
||||
setMemoryPointers = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,9 +201,9 @@ int BotControl::cmdCvars () {
|
|||
|
||||
File cfg;
|
||||
|
||||
// if save requested, dump cvars to yapb.cfg
|
||||
// if save requested, dump cvars to main config
|
||||
if (isSave) {
|
||||
cfg.open (strings.format ("%s/addons/%s/conf/%s.cfg", game.getRunningModName (), product.folder, product.folder), "wt");
|
||||
cfg.open (strings.joinPath (game.getRunningModName (), folders.addons, folders.bot, folders.config, strings.format ("%s.%s", product.nameLower, kConfigExtension)), "wt");
|
||||
cfg.puts ("// Configuration file for %s\n\n", product.name);
|
||||
}
|
||||
else {
|
||||
|
|
@ -496,7 +496,7 @@ int BotControl::cmdNodeSave () {
|
|||
|
||||
msg ("All nodes has been saved and written to disk (IGNORING QUALITY CONTROL).");
|
||||
}
|
||||
else if (strValue (option) == "old") {
|
||||
else if (strValue (option) == "old" || strValue (option) == "oldformat") {
|
||||
if (graph.length () >= 1024) {
|
||||
msg ("Unable to save POD-Bot Format waypoint file. Number of nodes exceeds 1024.");
|
||||
|
||||
|
|
|
|||
|
|
@ -250,35 +250,6 @@ void Game::testLine (const Vector &start, const Vector &end, int ignoreFlags, ed
|
|||
engfuncs.pfnTraceLine (start, end, engineFlags, ignoreEntity, ptr);
|
||||
}
|
||||
|
||||
bool Game::testLineChannel (TraceChannel channel, const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult &result) {
|
||||
// this function traces a line dot by dot, starting from vecStart in the direction of vecEnd,
|
||||
// ignoring or not monsters (depending on the value of IGNORE_MONSTERS, true or false), and stops
|
||||
// at the first obstacle encountered, returning the results of the trace in the TraceResult structure
|
||||
// ptr. Such results are (amongst others) the distance traced, the hit surface, the hit plane
|
||||
// vector normal, etc. See the TraceResult structure for details. This function allows to specify
|
||||
// whether the trace starts "inside" an entity's polygonal model, and if so, to specify that entity
|
||||
// in ignoreEntity in order to ignore it as a possible obstacle.
|
||||
|
||||
auto bot = bots[ignoreEntity];
|
||||
|
||||
// check if bot is firing trace line
|
||||
if (bot && bot->canSkipNextTrace (channel)) {
|
||||
result = bot->getLastTraceResult (channel); // set the result from bot stored one
|
||||
|
||||
// current call is skipped
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
testLine (start, end, ignoreFlags, ignoreEntity, &result);
|
||||
|
||||
// if we're still reaching here, save the last trace result
|
||||
if (bot) {
|
||||
bot->setLastTraceResult (channel, &result);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Game::testHull (const Vector &start, const Vector &end, int ignoreFlags, int hullNumber, edict_t *ignoreEntity, TraceResult *ptr) {
|
||||
// this function traces a hull dot by dot, starting from vecStart in the direction of vecEnd,
|
||||
// ignoring or not monsters (depending on the value of IGNORE_MONSTERS, true or
|
||||
|
|
@ -295,7 +266,7 @@ void Game::testHull (const Vector &start, const Vector &end, int ignoreFlags, in
|
|||
}
|
||||
|
||||
// helper class for reading wave header
|
||||
class WaveEndianessHelper : public DenyCopying {
|
||||
class WaveEndianessHelper : public NonCopyable {
|
||||
private:
|
||||
#if defined (CR_ARCH_CPU_BIG_ENDIAN)
|
||||
bool little { false };
|
||||
|
|
@ -321,7 +292,7 @@ public:
|
|||
};
|
||||
|
||||
float Game::getWaveLen (const char *fileName) {
|
||||
auto filePath = strings.format ("%s/%s.wav", cv_chatter_path.str (), fileName);
|
||||
auto filePath = strings.joinPath (cv_chatter_path.str (), strings.format ("%s.wav", fileName));
|
||||
|
||||
MemFile fp (filePath);
|
||||
|
||||
|
|
@ -666,7 +637,7 @@ void Game::addNewCvar (const char *name, const char *value, const char *info, bo
|
|||
reg.initial = static_cast <float> (atof (value));
|
||||
}
|
||||
|
||||
auto eflags = FCVAR_EXTDLL;
|
||||
int eflags = FCVAR_EXTDLL;
|
||||
|
||||
if (varType == Var::Normal) {
|
||||
eflags |= FCVAR_SERVER;
|
||||
|
|
@ -804,10 +775,10 @@ bool Game::loadCSBinary () {
|
|||
|
||||
// search the libraries inside game dlls directory
|
||||
for (const auto &lib : libs) {
|
||||
auto path = strings.format ("%s/dlls/%s.%s", modname, lib, DLL_SUFFIX);
|
||||
auto path = strings.joinPath (modname, "dlls", lib + DLL_SUFFIX);
|
||||
|
||||
// if we can't read file, skip it
|
||||
if (!File::exists (path)) {
|
||||
if (!plat.fileExists (path.chars ())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -871,10 +842,16 @@ bool Game::postload () {
|
|||
game.print (msg);
|
||||
});
|
||||
|
||||
auto ensureBotPathExists = [this] (StringRef dir1, StringRef dir2) {
|
||||
File::makePath (strings.joinPath (getRunningModName (), folders.addons, folders.bot, dir1, dir2).chars ());
|
||||
};
|
||||
|
||||
// ensure we're have all needed directories
|
||||
for (const auto &dir : StringArray { "conf/lang", "data/train", "data/graph", "data/logs", "data/pwf" }) {
|
||||
File::createPath (strings.format ("%s/addons/%s/%s", getRunningModName (), product.folder, dir));
|
||||
}
|
||||
ensureBotPathExists (folders.config, folders.lang);
|
||||
ensureBotPathExists (folders.data, folders.train);
|
||||
ensureBotPathExists (folders.data, folders.graph);
|
||||
ensureBotPathExists (folders.data, folders.logs);
|
||||
ensureBotPathExists (folders.data, folders.podbot);
|
||||
|
||||
// set out user agent for http stuff
|
||||
http.setUserAgent (strings.format ("%s/%s", product.name, product.version));
|
||||
|
|
@ -1250,7 +1227,7 @@ template <typename S, typename M> bool LightMeasure::recursiveLightPoint (const
|
|||
auto lightmap = surf->samples + dt * smax + ds;
|
||||
|
||||
// compute the lightmap color at a particular point
|
||||
for (int maps = 0u; maps < MAXLIGHTMAPS && surf->styles[maps] != 255u; ++maps) {
|
||||
for (int maps = 0u; maps < MAX_LIGHTMAPS && surf->styles[maps] != 255u; ++maps) {
|
||||
uint32_t scale = m_lightstyleValue[surf->styles[maps]];
|
||||
|
||||
m_point.red += lightmap->r * scale;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
// on other than win32/linux platforms i.e. arm we're using xash3d engine to run which exposes
|
||||
// nice interface to handle with linkents. if ever rehlds or hlds engine will ever run on ARM or
|
||||
// other platforms, and you wan't to run bot on it without metamod, consider enabling LINKENT_STATIC_THUNKS
|
||||
// other platforms, and you want to run bot on it without metamod, consider enabling LINKENT_STATIC_THUNKS
|
||||
// when compiling the bot, to get it supported.
|
||||
#if defined(LINKENT_STATIC_THUNKS)
|
||||
void forwardEntity_helper (EntityFunction &addr, const char *name, entvars_t *pev) {
|
||||
|
|
|
|||
|
|
@ -1648,8 +1648,20 @@ bool BotGraph::saveGraphData () {
|
|||
void BotGraph::saveOldFormat () {
|
||||
PODGraphHeader header {};
|
||||
|
||||
String editorName;
|
||||
|
||||
if (game.isNullEntity (m_editor) && !m_graphAuthor.empty ()) {
|
||||
editorName = m_graphAuthor;
|
||||
}
|
||||
else if (!game.isNullEntity (m_editor)) {
|
||||
editorName = m_editor->v.netname.chars ();
|
||||
}
|
||||
else {
|
||||
editorName = product.name;
|
||||
}
|
||||
|
||||
strings.copy (header.header, kPodbotMagic, sizeof (kPodbotMagic));
|
||||
strings.copy (header.author, m_editor->v.netname.chars (), cr::bufsize (header.author));
|
||||
strings.copy (header.author, editorName.chars (), cr::bufsize (header.author));
|
||||
strings.copy (header.mapName, game.getMapName (), cr::bufsize (header.mapName));
|
||||
|
||||
header.mapName[31] = 0;
|
||||
|
|
|
|||
|
|
@ -32,54 +32,6 @@ plugin_info_t Plugin_info = {
|
|||
PT_ANYTIME, // when unloadable
|
||||
};
|
||||
|
||||
void hook_ClientCommand (edict_t *ent, char const *format, ...) {
|
||||
// this function forces the client whose player entity is ent to issue a client command.
|
||||
// How it works is that clients all have a argv global string in their client DLL that
|
||||
// stores the command string; if ever that string is filled with characters, the client DLL
|
||||
// sends it to the engine as a command to be executed. When the engine has executed that
|
||||
// command, this argv string is reset to zero. Here is somehow a curious implementation of
|
||||
// ClientCommand: the engine sets the command it wants the client to issue in his argv, then
|
||||
// the client DLL sends it back to the engine, the engine receives it then executes the
|
||||
// command therein. Don't ask me why we need all this complicated crap. Anyhow since bots have
|
||||
// no client DLL, be certain never to call this function upon a bot entity, else it will just
|
||||
// make the server crash. Since hordes of uncautious, not to say stupid, programmers don't
|
||||
// even imagine some players on their servers could be bots, this check is performed less than
|
||||
// sometimes actually by their side, that's why we strongly recommend to check it here too. In
|
||||
// case it's a bot asking for a client command, we handle it like we do for bot commands
|
||||
|
||||
if (game.isNullEntity (ent)) {
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
RETURN_META (MRES_SUPERCEDE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
auto buffer = strings.chars ();
|
||||
|
||||
va_start (ap, format);
|
||||
vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap);
|
||||
va_end (ap);
|
||||
|
||||
if (util.isFakeClient (ent) || (ent->v.flags & FL_DORMANT)) {
|
||||
auto bot = bots[ent];
|
||||
|
||||
if (bot) {
|
||||
bot->issueCommand (buffer);
|
||||
}
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
RETURN_META (MRES_SUPERCEDE); // prevent bots to be forced to issue client commands
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
RETURN_META (MRES_IGNORED);
|
||||
}
|
||||
engfuncs.pfnClientCommand (ent, buffer);
|
||||
}
|
||||
|
||||
CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int) {
|
||||
// this function is called right after GiveFnptrsToDll() by the engine in the game DLL (or
|
||||
// what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can
|
||||
|
|
@ -496,7 +448,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) {
|
|||
}
|
||||
|
||||
if (ents.needsBypass () && !game.is (GameFlags::Metamod)) {
|
||||
table->pfnCreateNamedEntity = [] (int classname) -> edict_t *{
|
||||
table->pfnCreateNamedEntity = [] (string_t classname) -> edict_t *{
|
||||
|
||||
if (ents.isPaused ()) {
|
||||
ents.enable ();
|
||||
|
|
@ -764,9 +716,6 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) {
|
|||
}
|
||||
engfuncs.pfnSetClientMaxspeed (ent, newMaxspeed);
|
||||
};
|
||||
|
||||
table->pfnClientCommand = hook_ClientCommand;
|
||||
|
||||
return HLTrue;
|
||||
}
|
||||
|
||||
|
|
@ -946,6 +895,11 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *table, globalvars_t *glob) {
|
|||
memcpy (&engfuncs, table, sizeof (enginefuncs_t));
|
||||
globals = glob;
|
||||
|
||||
// set the global timer function
|
||||
timerStorage.setTimeFunction ([] () {
|
||||
return globals->time;
|
||||
});
|
||||
|
||||
if (game.postload ()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ BotManager::BotManager () {
|
|||
void BotManager::createKillerEntity () {
|
||||
// this function creates single trigger_hurt for using in Bot::kill, to reduce lags, when killing all the bots
|
||||
|
||||
m_killerEntity = engfuncs.pfnCreateNamedEntity (MAKE_STRING ("trigger_hurt"));
|
||||
m_killerEntity = engfuncs.pfnCreateNamedEntity ("trigger_hurt");
|
||||
|
||||
m_killerEntity->v.dmg = kInfiniteDistance;
|
||||
m_killerEntity->v.dmg_take = 1.0f;
|
||||
|
|
@ -128,12 +128,12 @@ void BotManager::touchKillerEntity (Bot *bot) {
|
|||
}
|
||||
const auto &prop = conf.getWeaponProp (bot->m_currentWeapon);
|
||||
|
||||
m_killerEntity->v.classname = MAKE_STRING (prop.classname.chars ());
|
||||
m_killerEntity->v.classname = prop.classname.chars ();
|
||||
m_killerEntity->v.dmg_inflictor = bot->ent ();
|
||||
m_killerEntity->v.dmg = (bot->pev->health + bot->pev->armorvalue) * 4.0f;
|
||||
|
||||
KeyValueData kv {};
|
||||
kv.szClassName = const_cast <char *> (prop.classname.chars ());
|
||||
kv.szClassName = prop.classname.chars ();
|
||||
kv.szKeyName = "damagetype";
|
||||
kv.szValue = strings.format ("%d", cr::bit (4));
|
||||
kv.fHandled = HLFalse;
|
||||
|
|
@ -146,7 +146,7 @@ void BotManager::execGameEntity (edict_t *ent) {
|
|||
// this function calls gamedll player() function, in case to create player entity in game
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
CALL_GAME_ENTITY (PLID, "player", &ent->v);
|
||||
MUTIL_CallGameEntity (PLID, "player", &ent->v);
|
||||
return;
|
||||
}
|
||||
ents.callPlayerFunction (ent);
|
||||
|
|
@ -214,7 +214,7 @@ BotCreateResult BotManager::create (StringRef name, int difficulty, int personal
|
|||
resultName = botName->name;
|
||||
}
|
||||
else {
|
||||
resultName.assignf ("%s_%d.%d", product.folder, rg.get (100, 10000), rg.get (100, 10000)); // just pick ugly random name
|
||||
resultName.assignf ("%s_%d.%d", product.nameLower, rg.get (100, 10000), rg.get (100, 10000)); // just pick ugly random name
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -793,8 +793,25 @@ void BotManager::listBots () {
|
|||
|
||||
ctrl.msg ("%-3.5s\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.4s\t%-3.4s\t%-3.5s\t%-3.8s", "index", "name", "personality", "team", "difficulty", "frags", "alive", "timeleft");
|
||||
|
||||
auto botTeam = [] (int32_t team) -> String {
|
||||
switch (team) {
|
||||
case Team::CT:
|
||||
return "CT";
|
||||
|
||||
case Team::Terrorist:
|
||||
return "TE";
|
||||
|
||||
case Team::Unassigned:
|
||||
default:
|
||||
return "UN";
|
||||
|
||||
case Team::Spectator:
|
||||
return "SP";
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto &bot : bots) {
|
||||
ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%-3.0f secs", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->m_team == Team::CT ? "CT" : "T", bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_notKilled ? "yes" : "no", cv_rotate_bots.bool_ () ? bot->m_stayTime - game.time () : 0.0f);
|
||||
ctrl.msg ("[%-3.1d]\t%-19.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%-3.0f secs", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", botTeam (bot->m_team), bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_notKilled ? "yes" : "no", cv_rotate_bots.bool_ () ? bot->m_stayTime - game.time () : 0.0f);
|
||||
}
|
||||
ctrl.msg ("%d bots", m_bots.length ());
|
||||
}
|
||||
|
|
@ -1160,6 +1177,12 @@ int BotManager::getPlayerPriority (edict_t *ent) {
|
|||
if (bot->m_hasC4 || bot->m_isVIP || bot->m_hasHostage || bot->m_healthValue < ent->v.health) {
|
||||
return bot->entindex () + highPrio;
|
||||
}
|
||||
auto task = bot->getCurrentTaskId ();
|
||||
|
||||
// higher priority if camping or hiding
|
||||
if (task == Task::Camp || task == Task::Hide) {
|
||||
return bot->entindex () + highPrio;
|
||||
}
|
||||
return bot->entindex ();
|
||||
}
|
||||
|
||||
|
|
@ -1189,7 +1212,7 @@ void BotManager::erase (Bot *bot) {
|
|||
}
|
||||
|
||||
if (!bot->m_kickedByRotation && cv_save_bots_names.bool_ ()) {
|
||||
m_saveBotNames.emplaceLast (bot->pev->netname.chars ());
|
||||
m_saveBotNames.emplaceLast (bot->pev->netname.str ());
|
||||
}
|
||||
bot->markStale ();
|
||||
|
||||
|
|
@ -1282,6 +1305,7 @@ void Bot::newRound () {
|
|||
|
||||
m_grenadeRequested = false;
|
||||
m_moveToC4 = false;
|
||||
m_defuseNotified = false;
|
||||
m_duckDefuse = false;
|
||||
m_duckDefuseCheckTime = 0.0f;
|
||||
|
||||
|
|
@ -1310,6 +1334,7 @@ void Bot::newRound () {
|
|||
m_askCheckTime = rg.get (30.0f, 90.0f);
|
||||
m_minSpeed = 260.0f;
|
||||
m_prevSpeed = 0.0f;
|
||||
m_prevVelocity = 0.0f;
|
||||
m_prevOrigin = Vector (kInfiniteDistance, kInfiniteDistance, kInfiniteDistance);
|
||||
m_prevTime = game.time ();
|
||||
m_lookUpdateTime = game.time ();
|
||||
|
|
@ -1431,6 +1456,7 @@ void Bot::newRound () {
|
|||
m_combatStrafeDir = Dodge::None;
|
||||
m_fightStyle = Fight::None;
|
||||
m_lastFightStyleCheck = 0.0f;
|
||||
m_stuckTimestamp = 0.0f;
|
||||
|
||||
m_checkWeaponSwitch = true;
|
||||
m_checkKnifeSwitch = true;
|
||||
|
|
@ -1454,20 +1480,9 @@ void Bot::newRound () {
|
|||
m_soundUpdateTime = 0.0f;
|
||||
m_heardSoundTime = game.time ();
|
||||
|
||||
// clear its message queue
|
||||
for (auto &msg : m_messageQueue) {
|
||||
msg = BotMsg::None;
|
||||
}
|
||||
m_msgQueue.clear ();
|
||||
m_nodeHistory.clear ();
|
||||
m_ignoredBreakable.clear ();
|
||||
|
||||
// clear last trace
|
||||
for (auto i = 0; i < TraceChannel::Num; ++i) {
|
||||
m_lastTrace[i] = {};
|
||||
m_traceSkip[i] = 0;
|
||||
}
|
||||
|
||||
// and put buying into its message queue
|
||||
pushMsgQueue (BotMsg::Buy);
|
||||
startTask (Task::Normal, TaskPri::Normal, kInvalidNodeIndex, 0.0f, true);
|
||||
|
|
@ -1673,7 +1688,7 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
|||
}
|
||||
}
|
||||
}
|
||||
Client &target = util.getClient (game.indexOfPlayer (ent));
|
||||
auto &target = util.getClient (game.indexOfPlayer (ent));
|
||||
|
||||
// check if this player alive, and issue something
|
||||
if ((target.flags & ClientFlags::Alive) && target.radio != 0 && strncmp (cmd, "menuselect", 10) == 0) {
|
||||
|
|
@ -1704,19 +1719,19 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
|||
void BotManager::notifyBombDefuse () {
|
||||
// notify all terrorists that CT is starting bomb defusing
|
||||
|
||||
if (!isBombPlanted ()) {
|
||||
return;
|
||||
}
|
||||
auto bombPos = graph.getBombOrigin ();
|
||||
const auto &bombPos = graph.getBombOrigin ();
|
||||
|
||||
for (const auto &bot : bots) {
|
||||
auto task = bot->getCurrentTaskId ();
|
||||
|
||||
if (bot->m_notKilled && task != Task::MoveToPosition && task != Task::DefuseBomb && task != Task::EscapeFromBomb) {
|
||||
if (bot->m_team == Team::CT || bot->pev->origin.distanceSq (bombPos) < cr::sqrf (384.0f)) {
|
||||
if (!bot->m_defuseNotified && bot->m_notKilled && task != Task::MoveToPosition && task != Task::DefuseBomb && task != Task::EscapeFromBomb) {
|
||||
if (bot->m_team == Team::Terrorist && bot->pev->origin.distanceSq (bombPos) < cr::sqrf (384.0f)) {
|
||||
bot->clearSearchNodes ();
|
||||
|
||||
bot->m_pathType = FindPath::Fast;
|
||||
bot->m_position = bombPos;
|
||||
bot->m_defuseNotified = true;
|
||||
|
||||
bot->startTask (Task::MoveToPosition, TaskPri::MoveToPosition, kInvalidNodeIndex, 0.0f, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ void MessageDispatcher::netMsgBarTime () {
|
|||
m_bot->m_hasProgressBar = true; // the progress bar on a hud
|
||||
|
||||
// notify bots about defusing has started
|
||||
if (game.mapIs (MapFlags::Demolition) && bots.isBombPlanted ()) {
|
||||
if (game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_bot->m_team == Team::CT) {
|
||||
bots.notifyBombDefuse ();
|
||||
}
|
||||
}
|
||||
|
|
@ -560,7 +560,7 @@ void MessageDispatcher::ensureMessages () {
|
|||
|
||||
// re-register our message
|
||||
m_wanted.foreach ([&] (const String &key, const int32_t &) {
|
||||
add (key, GET_USER_MSG_ID (PLID, key.chars (), nullptr));
|
||||
add (key, MUTIL_GetUserMsgID (PLID, key.chars (), nullptr));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
115
src/navigate.cpp
115
src/navigate.cpp
|
|
@ -8,11 +8,6 @@
|
|||
#include <yapb.h>
|
||||
|
||||
int Bot::findBestGoal () {
|
||||
auto pushToHistroy = [&] (int32_t goal) -> int32_t {
|
||||
m_nodeHistory.push (goal);
|
||||
return goal;
|
||||
};
|
||||
|
||||
if (m_isCreature) {
|
||||
if (!graph.m_terrorPoints.empty ()) {
|
||||
return graph.m_terrorPoints.random ();
|
||||
|
|
@ -110,7 +105,7 @@ int Bot::findBestGoal () {
|
|||
else if (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && bots.getRoundStartTime () + 10.0f < game.time ()) {
|
||||
// send some terrorists to guard planted bomb
|
||||
if (!m_defendedBomb && bots.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && getBombTimeleft () >= 15.0f) {
|
||||
return pushToHistroy (m_chosenGoalIndex = findDefendNode (graph.getBombOrigin ()));
|
||||
return m_chosenGoalIndex = findDefendNode (graph.getBombOrigin ());
|
||||
}
|
||||
}
|
||||
else if (game.mapIs (MapFlags::Escape)) {
|
||||
|
|
@ -149,7 +144,7 @@ int Bot::findBestGoal () {
|
|||
if (goalDesire > tacticChoice) {
|
||||
tactic = 3;
|
||||
}
|
||||
return pushToHistroy (findGoalPost (tactic, defensiveNodes, offensiveNodes));
|
||||
return findGoalPost (tactic, defensiveNodes, offensiveNodes);
|
||||
}
|
||||
|
||||
int Bot::findBestGoalWhenBombAction () {
|
||||
|
|
@ -365,12 +360,8 @@ void Bot::resetCollision () {
|
|||
void Bot::ignoreCollision () {
|
||||
resetCollision ();
|
||||
|
||||
m_prevTime = game.time () + 1.2f;
|
||||
m_lastCollTime = game.time () + 1.5f;
|
||||
m_isStuck = false;
|
||||
m_lastCollTime = game.time () + m_frameInterval * 4.0f;
|
||||
m_checkTerrain = false;
|
||||
m_prevSpeed = m_moveSpeed;
|
||||
m_prevOrigin = pev->origin;
|
||||
}
|
||||
|
||||
void Bot::doPlayerAvoidance (const Vector &normal) {
|
||||
|
|
@ -417,13 +408,13 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
if (game.isNullEntity (m_hindrance)) {
|
||||
return;
|
||||
}
|
||||
const float interval = m_frameInterval * (pev->velocity.lengthSq2d () > 0.0f ? 9.0f : 2.0f);
|
||||
const float interval = m_frameInterval * (pev->velocity.lengthSq2d () > 0.0f ? 7.5f : 2.0f);
|
||||
|
||||
// use our movement angles, try to predict where we should be next frame
|
||||
Vector right, forward;
|
||||
m_moveAngles.angleVectors (&forward, &right, nullptr);
|
||||
|
||||
Vector predict = pev->origin + forward * m_moveSpeed * interval;
|
||||
Vector predict = pev->origin + forward * pev->maxspeed * interval;
|
||||
|
||||
predict += right * m_strafeSpeed * interval;
|
||||
predict += pev->velocity * interval;
|
||||
|
|
@ -432,7 +423,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
auto nextFrameDistance = pev->origin.distanceSq (m_hindrance->v.origin + m_hindrance->v.velocity * interval);
|
||||
|
||||
// is player that near now or in future that we need to steer away?
|
||||
if (movedDistance <= cr::sqrf (48.0f) || (distance <= cr::sqrf (56.0f) && nextFrameDistance < distance)) {
|
||||
if (movedDistance <= cr::sqrf (64.0f) || (distance <= cr::sqrf (72.0f) && nextFrameDistance < distance)) {
|
||||
auto dir = (pev->origin - m_hindrance->v.origin).normalize2d_apx ();
|
||||
|
||||
// to start strafing, we have to first figure out if the target is on the left side or right side
|
||||
|
|
@ -443,7 +434,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
setStrafeSpeed (normal, -pev->maxspeed);
|
||||
}
|
||||
|
||||
if (distance < cr::sqrf (76.0f)) {
|
||||
if (distance < cr::sqrf (80.0f)) {
|
||||
if ((dir | forward.normalize2d_apx ()) < 0.0f) {
|
||||
m_moveSpeed = -pev->maxspeed;
|
||||
}
|
||||
|
|
@ -455,15 +446,12 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
|
||||
// if avoiding someone do not consider stuck
|
||||
TraceResult tr {};
|
||||
float checkSpeed = isDucking () ? 4.0f : 10.0f;
|
||||
|
||||
m_isStuck = false;
|
||||
|
||||
// standing still, no need to check?
|
||||
if ((m_moveSpeed >= checkSpeed || m_strafeSpeed >= checkSpeed) && m_lastCollTime < game.time ()) {
|
||||
|
||||
if (m_lastCollTime < game.time () && getCurrentTaskId () != Task::Attack) {
|
||||
// didn't we move enough previously?
|
||||
if (movedDistance < 2.0f && m_prevSpeed >= 20.0f) {
|
||||
if (movedDistance < 2.0f && (m_prevSpeed > 20.0f || m_prevVelocity < m_moveSpeed / 2)) {
|
||||
m_prevTime = game.time (); // then consider being stuck
|
||||
m_isStuck = true;
|
||||
|
||||
|
|
@ -474,7 +462,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
// not stuck yet
|
||||
else {
|
||||
// test if there's something ahead blocking the way
|
||||
if (cantMoveForward (dirNormal, &tr) && !isOnLadder ()) {
|
||||
if (!isOnLadder () && cantMoveForward (dirNormal, &tr)) {
|
||||
if (cr::fzero (m_firstCollideTime)) {
|
||||
m_firstCollideTime = game.time () + 0.2f;
|
||||
}
|
||||
|
|
@ -489,19 +477,24 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
|
||||
// not stuck?
|
||||
if (!m_isStuck) {
|
||||
if (m_probeTime + 0.5f < game.time ()) {
|
||||
if (m_probeTime + rg.get (0.5f, 1.0f) < game.time ()) {
|
||||
resetCollision (); // reset collision memory if not being stuck for 0.5 secs
|
||||
}
|
||||
else {
|
||||
// remember to keep pressing duck if it was necessary ago
|
||||
// remember to keep pressing stuff if it was necessary ago
|
||||
if (m_collideMoves[m_collStateIndex] == CollisionState::Duck && (isOnFloor () || isInWater ())) {
|
||||
pev->button |= IN_DUCK;
|
||||
}
|
||||
else if (m_collideMoves[m_collStateIndex] == CollisionState::StrafeLeft) {
|
||||
setStrafeSpeed (dirNormal, -pev->maxspeed);
|
||||
}
|
||||
else if (m_collideMoves[m_collStateIndex] == CollisionState::StrafeRight) {
|
||||
setStrafeSpeed (dirNormal, pev->maxspeed);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// bot is stuck, but not yet decided what to do?
|
||||
if (m_collisionState == CollisionState::Undecided) {
|
||||
uint32_t bits = 0;
|
||||
|
|
@ -549,7 +542,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
dirLeft = true;
|
||||
}
|
||||
const auto &testDir = m_moveSpeed > 0.0f ? forward : -forward;
|
||||
constexpr float blockDistance = 40.0f;
|
||||
constexpr float blockDistance = 32.0f;
|
||||
|
||||
// now check which side is blocked
|
||||
src = pev->origin + right * blockDistance;
|
||||
|
|
@ -784,6 +777,14 @@ void Bot::moveToGoal () {
|
|||
}
|
||||
}
|
||||
|
||||
void Bot::resetMovement () {
|
||||
pev->button = 0;
|
||||
|
||||
m_moveSpeed = 0.0f;
|
||||
m_strafeSpeed = 0.0f;
|
||||
m_moveAngles = nullptr;
|
||||
}
|
||||
|
||||
void Bot::translateInput () {
|
||||
if (m_duckTime >= game.time ()) {
|
||||
pev->button |= IN_DUCK;
|
||||
|
|
@ -1047,18 +1048,18 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
}
|
||||
|
||||
float desiredDistance = 8.0f;
|
||||
float nodeDistance = pev->origin.distance (m_pathOrigin);
|
||||
float desiredDistance = cr::sqrf (8.0f);
|
||||
float nodeDistance = pev->origin.distanceSq (m_pathOrigin);
|
||||
|
||||
// initialize the radius for a special node type, where the node is considered to be reached
|
||||
if (m_pathFlags & NodeFlag::Lift) {
|
||||
desiredDistance = 50.0f;
|
||||
desiredDistance = cr::sqrf (50.0f);
|
||||
}
|
||||
else if (isDucking () || (m_pathFlags & NodeFlag::Goal)) {
|
||||
desiredDistance = 25.0f;
|
||||
desiredDistance = cr::sqrf (25.0f);
|
||||
}
|
||||
else if (m_pathFlags & NodeFlag::Ladder) {
|
||||
desiredDistance = 24.0f;
|
||||
else if (isOnLadder () || (m_pathFlags & NodeFlag::Ladder)) {
|
||||
desiredDistance = cr::sqrf (24.0f);
|
||||
}
|
||||
else if (m_currentTravelFlags & PathFlag::Jump) {
|
||||
desiredDistance = 0.0f;
|
||||
|
|
@ -1067,13 +1068,13 @@ bool Bot::updateNavigation () {
|
|||
desiredDistance = 0.0f;
|
||||
}
|
||||
else if (isOccupiedNode (m_path->number)) {
|
||||
desiredDistance = 96.0f;
|
||||
desiredDistance = cr::sqrf (96.0f);
|
||||
}
|
||||
else {
|
||||
desiredDistance = m_path->radius;
|
||||
desiredDistance = cr::max (cr::sqrf (m_path->radius), desiredDistance);
|
||||
}
|
||||
|
||||
// check if node has a special travelflag, so they need to be reached more precisely
|
||||
// check if node has a special travel flags, so they need to be reached more precisely
|
||||
for (const auto &link : m_path->links) {
|
||||
if (link.flags != 0) {
|
||||
desiredDistance = 0.0f;
|
||||
|
|
@ -1082,11 +1083,11 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
// needs precise placement - check if we get past the point
|
||||
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= cr::sqrf (nodeDistance)) {
|
||||
if (desiredDistance < cr::sqrf (22.0f) && nodeDistance < cr::sqrf (30.0f) && m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= nodeDistance) {
|
||||
desiredDistance = nodeDistance + 1.0f;
|
||||
}
|
||||
|
||||
// this allows us to prevent stupid bot behaviour when he reaches almost end point of this route, but some one (other bot eg)
|
||||
// this allows us to prevent stupid bot behavior when he reaches almost end point of this route, but some one (other bot eg)
|
||||
// is sitting there, so the bot is unable to reach the node because of other player on it, and he starts to jumping and so on
|
||||
// here we're clearing task memory data (important!), since task executor may restart goal with one from memory, so this process
|
||||
// will go in cycle, and forcing bot to re-create new route.
|
||||
|
|
@ -1099,7 +1100,6 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
if (nodeDistance < desiredDistance) {
|
||||
|
||||
// did we reach a destination node?
|
||||
if (getTask ()->data == m_currentNodeIndex) {
|
||||
if (m_chosenGoalIndex != kInvalidNodeIndex) {
|
||||
|
|
@ -1126,9 +1126,9 @@ bool Bot::updateNavigation () {
|
|||
|
||||
// bot within 'hearable' bomb tick noises?
|
||||
if (!bombOrigin.empty ()) {
|
||||
float distance = bombOrigin.distance (graph[taskTarget].origin);
|
||||
float distance = bombOrigin.distanceSq (graph[taskTarget].origin);
|
||||
|
||||
if (distance > 512.0f) {
|
||||
if (distance > cr::sqrf (512.0f)) {
|
||||
if (rg.chance (50) && !graph.isVisited (taskTarget)) {
|
||||
pushRadioMessage (Radio::SectorClear);
|
||||
}
|
||||
|
|
@ -2334,7 +2334,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
};
|
||||
|
||||
// trace from the bot's eyes straight forward...
|
||||
traceResult = game.testLineChannel (TraceChannel::Body, src, forward, TraceIgnore::Monsters, ent (), *tr);
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (tr->flFraction < 1.0f) {
|
||||
|
|
@ -2349,7 +2349,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - right * -16.0f;
|
||||
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + right * 16.0f + normal * 24.0f;
|
||||
|
||||
traceResult = game.testLineChannel (TraceChannel::Body, src, forward, TraceIgnore::Monsters, ent (), *tr);
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (checkDoor (tr)) {
|
||||
|
|
@ -2361,7 +2361,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + right * 16.0f;
|
||||
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - right * -16.0f + normal * 24.0f;
|
||||
|
||||
traceResult = game.testLineChannel (TraceChannel::Body, src, forward, TraceIgnore::Monsters, ent (), *tr);
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (checkDoor (tr)) {
|
||||
|
|
@ -2373,7 +2373,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
src = pev->origin + Vector (0.0f, 0.0f, -19.0f + 19.0f);
|
||||
forward = src + Vector (0.0f, 0.0f, 10.0f) + normal * 24.0f;
|
||||
|
||||
traceResult = game.testLineChannel (TraceChannel::Body, src, forward, TraceIgnore::Monsters, ent (), *tr);
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (checkDoor (tr)) {
|
||||
|
|
@ -2382,7 +2382,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
src = pev->origin;
|
||||
forward = src + normal * 24.0f;
|
||||
|
||||
traceResult = game.testLineChannel (TraceChannel::Body, src, forward, TraceIgnore::Monsters, ent (), *tr);
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (checkDoor (tr)) {
|
||||
|
|
@ -2395,7 +2395,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) + right * 16.0f + normal * 24.0f;
|
||||
|
||||
// trace from the bot's waist straight forward...
|
||||
traceResult = game.testLineChannel (TraceChannel::Body, src, forward, TraceIgnore::Monsters, ent (), *tr);
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (checkDoor (tr)) {
|
||||
|
|
@ -2406,7 +2406,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
src = pev->origin + Vector (0.0f, 0.0f, -24.0f) + right * 16.0f;
|
||||
forward = pev->origin + Vector (0.0f, 0.0f, -24.0f) - right * -16.0f + normal * 24.0f;
|
||||
|
||||
traceResult = game.testLineChannel (TraceChannel::Body, src, forward, TraceIgnore::Monsters, ent (), *tr);
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (checkDoor (tr)) {
|
||||
|
|
@ -2717,7 +2717,7 @@ bool Bot::isBlockedRight () {
|
|||
|
||||
bool Bot::checkWallOnLeft () {
|
||||
TraceResult tr {};
|
||||
game.testLine (pev->origin, pev->origin - pev->angles.right () * 40.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||
game.testLine (pev->origin, pev->origin + -pev->angles.right () * 40.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (tr.flFraction < 1.0f) {
|
||||
|
|
@ -3040,25 +3040,6 @@ bool Bot::isReachableNode (int index) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Bot::isBannedNode (int index) {
|
||||
if (graph.exists (cv_debug_goal.int_ ()) || !graph.exists (index)) {
|
||||
return false;
|
||||
}
|
||||
const auto &bucket = graph.getNodesInBucket (graph[index].origin);
|
||||
|
||||
// too few nodes in bucket near location, do not ban anything
|
||||
if (bucket.length <int> () <= kMaxNodeLinks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &node : m_nodeHistory) {
|
||||
if (node == index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::findShortestPath (int srcIndex, int destIndex) {
|
||||
// this function finds the shortest path from source index to destination index
|
||||
|
||||
|
|
|
|||
|
|
@ -418,6 +418,9 @@ void DijkstraAlgo::init (const int length) {
|
|||
|
||||
m_distance.resize (length);
|
||||
m_parent.resize (length);
|
||||
|
||||
m_distance.shrink ();
|
||||
m_parent.shrink ();
|
||||
}
|
||||
|
||||
bool DijkstraAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, int *pathDistance) {
|
||||
|
|
@ -432,15 +435,15 @@ bool DijkstraAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, i
|
|||
m_distance[srcIndex] = 0;
|
||||
|
||||
while (!m_queue.empty ()) {
|
||||
auto p = cr::move (m_queue.pop ());
|
||||
auto current = p.second;
|
||||
auto &&route = cr::move (m_queue.pop ());
|
||||
auto current = route.second;
|
||||
|
||||
// finished search
|
||||
if (current == destIndex) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_distance[current] != p.first) {
|
||||
if (m_distance[current] != route.first) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ void BotPractice::setDamage (int32_t team, int32_t start, int32_t goal, int32_t
|
|||
|
||||
float BotPractice::plannerGetDamage (int32_t team, int32_t start, int32_t goal, bool addTeamHighestDamage) {
|
||||
if (!m_damageUpdateLock.tryLock ()) {
|
||||
return 1.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
ScopedUnlock <Mutex> unlock (m_damageUpdateLock);
|
||||
auto damage = static_cast <float> (getDamage (team, start, goal));
|
||||
|
|
|
|||
|
|
@ -302,13 +302,13 @@ String BotStorage::buildPath (int32_t file, bool isMemoryLoad) {
|
|||
using FilePath = Twin <String, String>;
|
||||
|
||||
static HashMap <int32_t, FilePath> paths = {
|
||||
{ BotFile::Vistable, FilePath ("train", "vis")},
|
||||
{ BotFile::Practice, FilePath ("train", "prc")},
|
||||
{ BotFile::Pathmatrix, FilePath ("train", "pmx")},
|
||||
{ BotFile::LogFile, FilePath ("logs", "txt")},
|
||||
{ BotFile::Graph, FilePath ("graph", "graph")},
|
||||
{ BotFile::PodbotPWF, FilePath ("pwf", "pwf")},
|
||||
{ BotFile::EbotEWP, FilePath ("ewp", "ewp")},
|
||||
{ BotFile::Vistable, FilePath (folders.train, "vis")},
|
||||
{ BotFile::Practice, FilePath (folders.train, "prc")},
|
||||
{ BotFile::Pathmatrix, FilePath (folders.train, "pmx")},
|
||||
{ BotFile::LogFile, FilePath (folders.logs, "txt")},
|
||||
{ BotFile::Graph, FilePath (folders.graph, "graph")},
|
||||
{ BotFile::PodbotPWF, FilePath (folders.podbot, "pwf")},
|
||||
{ BotFile::EbotEWP, FilePath (folders.ebot, "ewp")},
|
||||
};
|
||||
|
||||
static StringArray path;
|
||||
|
|
@ -320,11 +320,11 @@ String BotStorage::buildPath (int32_t file, bool isMemoryLoad) {
|
|||
}
|
||||
|
||||
// allways append addons/product
|
||||
path.emplace ("addons");
|
||||
path.emplace (product.folder);
|
||||
path.emplace (folders.addons);
|
||||
path.emplace (folders.bot);
|
||||
|
||||
// the datadir
|
||||
path.emplace ("data");
|
||||
path.emplace (folders.data);
|
||||
|
||||
// append real filepath
|
||||
path.emplace (paths[file].first);
|
||||
|
|
@ -338,19 +338,19 @@ String BotStorage::buildPath (int32_t file, bool isMemoryLoad) {
|
|||
auto timebuf = strings.chars ();
|
||||
|
||||
strftime (timebuf, StringBuffer::StaticBufferSize, "L%d%m%Y", &timeinfo);
|
||||
path.emplace (strings.format ("%s_%s.%s", product.folder, timebuf, paths[file].second));
|
||||
path.emplace (strings.format ("%s_%s.%s", folders.bot, timebuf, paths[file].second));
|
||||
}
|
||||
else {
|
||||
String mapName = game.getMapName ();
|
||||
path.emplace (strings.format ("%s.%s", mapName.lowercase (), paths[file].second));
|
||||
}
|
||||
|
||||
// finally use correct path separarators for us
|
||||
// finally use correct path separators for us
|
||||
return String::join (path, PATH_SEP);
|
||||
}
|
||||
|
||||
int32_t BotStorage::storageToBotFile (int32_t options) {
|
||||
// converts storage option to stroage filename
|
||||
// converts storage option to storage filename
|
||||
|
||||
if (options & StorageOption::Graph) {
|
||||
return BotFile::Graph;
|
||||
|
|
@ -380,7 +380,7 @@ void BotStorage::unlinkFromDisk () {
|
|||
unlinkable.emplace (buildPath (BotFile::Pathmatrix)); // corresponding to matrix
|
||||
|
||||
for (const auto &item : unlinkable) {
|
||||
if (File::exists (item)) {
|
||||
if (plat.fileExists (item.chars ())) {
|
||||
plat.removeFile (item.chars ());
|
||||
ctrl.msg ("File %s, has been deleted from the hard disk", item);
|
||||
}
|
||||
|
|
@ -388,7 +388,7 @@ void BotStorage::unlinkFromDisk () {
|
|||
logger.error ("Unable to open %s", item);
|
||||
}
|
||||
}
|
||||
graph.reset (); // re-intialize points
|
||||
graph.reset (); // re-initialize points
|
||||
}
|
||||
|
||||
#endif // BOT_STORAGE_EXPLICIT_INSTANTIATIONS
|
||||
|
|
|
|||
|
|
@ -233,31 +233,31 @@ bool BotSupport::isFakeClient (edict_t *ent) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool BotSupport::openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
|
||||
bool BotSupport::openConfig (StringRef fileName, StringRef errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
|
||||
if (*outFile) {
|
||||
outFile->close ();
|
||||
}
|
||||
|
||||
// save config dir
|
||||
auto configDir = strings.format ("addons/%s/conf", product.folder);
|
||||
auto configDir = strings.joinPath (folders.addons, folders.bot, folders.config);
|
||||
|
||||
if (languageDependant) {
|
||||
if (strcmp (fileName, "lang.cfg") == 0 && strcmp (cv_language.str (), "en") == 0) {
|
||||
if (fileName.startsWith ("lang") && strcmp (cv_language.str (), "en") == 0) {
|
||||
return false;
|
||||
}
|
||||
auto langConfig = strings.format ("%s/lang/%s_%s", configDir, cv_language.str (), fileName);
|
||||
auto langConfig = strings.joinPath (configDir, folders.lang, strings.format ("%s_%s.%s", cv_language.str (), fileName, kConfigExtension));
|
||||
|
||||
// check is file is exists for this language
|
||||
if (!outFile->open (langConfig)) {
|
||||
outFile->open (strings.format ("%s/lang/en_%s", configDir, fileName));
|
||||
outFile->open (strings.joinPath (configDir, folders.lang, strings.format ("en_%s.%s", fileName, kConfigExtension)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
outFile->open (strings.format ("%s/%s", configDir, fileName));
|
||||
outFile->open (strings.joinPath (configDir, strings.format ("%s.%s", fileName, kConfigExtension)));
|
||||
}
|
||||
|
||||
if (!*outFile) {
|
||||
logger.error (errorIfNotExists);
|
||||
logger.error (errorIfNotExists.chars ());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -473,9 +473,6 @@ void BotSupport::syncCalculatePings () {
|
|||
void BotSupport::emitPings (edict_t *to) {
|
||||
MessageWriter msg;
|
||||
|
||||
// missing from sdk
|
||||
constexpr int kGamePingSVC = 17;
|
||||
|
||||
auto isThirdpartyBot = [] (edict_t *ent) {
|
||||
return !bots[ent] && (ent->v.flags & FL_FAKECLIENT);
|
||||
};
|
||||
|
|
@ -490,7 +487,7 @@ void BotSupport::emitPings (edict_t *to) {
|
|||
client.ping = getPingBitmask (client.ent, rg.get (5, 10), rg.get (15, 40));
|
||||
}
|
||||
|
||||
msg.start (MSG_ONE_UNRELIABLE, kGamePingSVC, nullptr, to)
|
||||
msg.start (MSG_ONE_UNRELIABLE, SVC_PINGS, nullptr, to)
|
||||
.writeLong (client.ping)
|
||||
.end ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ void Bot::normal_ () {
|
|||
getTask ()->data = kInvalidNodeIndex;
|
||||
}
|
||||
|
||||
// reached the destination (goal) waypoint?
|
||||
// reached the destination (goal) node?
|
||||
if (updateNavigation ()) {
|
||||
// if we're reached the goal, and there is not enemies, notify the team
|
||||
if (!bots.isBombPlanted () && m_currentNodeIndex != kInvalidNodeIndex && (m_pathFlags & NodeFlag::Goal) && rg.chance (15) && numEnemiesNear (pev->origin, 650.0f) == 0) {
|
||||
|
|
@ -65,7 +65,7 @@ void Bot::normal_ () {
|
|||
}
|
||||
}
|
||||
|
||||
// reached waypoint is a camp waypoint
|
||||
// reached node is a camp node
|
||||
if ((m_pathFlags & NodeFlag::Camp) && !game.is (GameFlags::CSDM) && cv_camping_allowed.bool_ () && !isKnifeMode ()) {
|
||||
|
||||
// check if bot has got a primary weapon and hasn't camped before
|
||||
|
|
@ -184,22 +184,15 @@ void Bot::normal_ () {
|
|||
}
|
||||
// no more nodes to follow - search new ones (or we have a bomb)
|
||||
else if (!hasActiveGoal ()) {
|
||||
m_moveSpeed = pev->maxspeed;
|
||||
|
||||
clearSearchNodes ();
|
||||
ignoreCollision ();
|
||||
|
||||
// did we already decide about a goal before?
|
||||
auto currIndex = getTask ()->data;
|
||||
auto destIndex = graph.exists (currIndex) && !isBannedNode (currIndex) && m_prevGoalIndex != currIndex ? currIndex : findBestGoal ();
|
||||
auto destIndex = graph.exists (currIndex) ? currIndex : findBestGoal ();
|
||||
|
||||
// check for existance (this is failover, for i.e. csdm, this should be not true with normal gameplay, only when spawned outside of waypointed area)
|
||||
// check for existence (this is fail over, for i.e. csdm, this should be not true with normal game play, only when spawned outside of covered area)
|
||||
if (!graph.exists (destIndex)) {
|
||||
destIndex = graph.getFarest (pev->origin, 512.0f);
|
||||
}
|
||||
|
||||
if (!graph.exists (cv_debug_goal.int_ ()) && graph.exists (currIndex) && m_prevGoalIndex == currIndex && !(graph[currIndex].flags & NodeFlag::Goal)) {
|
||||
m_nodeHistory.push (currIndex);
|
||||
destIndex = graph.getFarest (pev->origin, 1024.0f);
|
||||
}
|
||||
m_prevGoalIndex = destIndex;
|
||||
|
||||
|
|
@ -210,7 +203,7 @@ void Bot::normal_ () {
|
|||
|
||||
// override with fast path
|
||||
if (game.mapIs (MapFlags::Demolition) && bots.isBombPlanted ()) {
|
||||
pathSearchType = rg.chance (60) ? FindPath::Fast : FindPath::Optimal;
|
||||
pathSearchType = rg.chance (80) ? FindPath::Fast : FindPath::Optimal;
|
||||
}
|
||||
ensureCurrentNodeIndex ();
|
||||
|
||||
|
|
@ -314,8 +307,6 @@ void Bot::huntEnemy_ () {
|
|||
|
||||
// do we need to calculate a new path?
|
||||
else if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
|
||||
int destIndex = kInvalidNodeIndex;
|
||||
int goal = getTask ()->data;
|
||||
|
||||
|
|
@ -410,7 +401,6 @@ void Bot::seekCover_ () {
|
|||
m_checkTerrain = false;
|
||||
}
|
||||
else if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
int destIndex = kInvalidNodeIndex;
|
||||
|
||||
if (getTask ()->data != kInvalidNodeIndex) {
|
||||
|
|
@ -518,7 +508,6 @@ void Bot::blind_ () {
|
|||
m_states |= Sense::SuspectEnemy;
|
||||
}
|
||||
else if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
ensureCurrentNodeIndex ();
|
||||
|
||||
m_prevGoalIndex = m_blindNodeIndex;
|
||||
|
|
@ -707,8 +696,7 @@ void Bot::moveToPos_ () {
|
|||
}
|
||||
|
||||
auto ensureDestIndexOK = [&] (int &index) {
|
||||
if (isOccupiedNode (index) || isBannedNode (index)) {
|
||||
m_nodeHistory.push (index);
|
||||
if (isOccupiedNode (index)) {
|
||||
index = findDefendNode (m_position);
|
||||
}
|
||||
};
|
||||
|
|
@ -723,8 +711,6 @@ void Bot::moveToPos_ () {
|
|||
|
||||
// didn't choose goal waypoint yet?
|
||||
else if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
|
||||
int destIndex = kInvalidNodeIndex;
|
||||
int goal = getTask ()->data;
|
||||
|
||||
|
|
@ -1050,8 +1036,6 @@ void Bot::followUser_ () {
|
|||
|
||||
// didn't choose goal waypoint yet?
|
||||
if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
|
||||
int destIndex = graph.getNearest (m_targetEntity->v.origin);
|
||||
auto points = graph.getNarestInRadius (200.0f, m_targetEntity->v.origin);
|
||||
|
||||
|
|
@ -1306,8 +1290,6 @@ void Bot::doublejump_ () {
|
|||
|
||||
// didn't choose goal waypoint yet?
|
||||
if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
|
||||
int destIndex = graph.getNearest (m_doubleJumpOrigin);
|
||||
|
||||
if (graph.exists (destIndex)) {
|
||||
|
|
@ -1359,8 +1341,6 @@ void Bot::escapeFromBomb_ () {
|
|||
|
||||
// didn't choose goal waypoint yet?
|
||||
else if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
|
||||
int bestIndex = kInvalidNodeIndex;
|
||||
|
||||
float safeRadius = rg.get (1513.0f, 2048.0f);
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ void Bot::updateAimDir () {
|
|||
auto doFailPredict = [this] () {
|
||||
m_aimFlags &= ~AimFlags::PredictPath;
|
||||
m_trackingEdict = nullptr;
|
||||
m_lookAt = m_destOrigin;
|
||||
};
|
||||
|
||||
if (changePredictedEnemy) {
|
||||
|
|
@ -147,7 +148,7 @@ void Bot::updateAimDir () {
|
|||
}
|
||||
}
|
||||
|
||||
if (graph.exists (predictNode) && pathLength < cv_max_nodes_for_predict.int_ ()) {
|
||||
if (predictNode != kInvalidNodeIndex && pathLength < cv_max_nodes_for_predict.int_ ()) {
|
||||
m_lookAt = graph[predictNode].origin;
|
||||
m_lookAtSafe = m_lookAt;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue