More fixes to optiomal node search.
Support for replying to UTF-8 chat messages. Minor refactoring.
This commit is contained in:
parent
1d3910d629
commit
a186f33ffb
18 changed files with 488 additions and 189 deletions
|
|
@ -165,12 +165,12 @@ void Bot::checkGrenadesThrow () {
|
|||
|
||||
// don't throw grenades at anything that isn't on the ground!
|
||||
if (!(m_lastEnemy->v.flags & FL_ONGROUND) && !m_lastEnemy->v.waterlevel && m_lastEnemyOrigin.z > pev->absmax.z) {
|
||||
distance = 9999.0f;
|
||||
distance = kInfiniteDistance;
|
||||
}
|
||||
|
||||
// too high to throw?
|
||||
if (m_lastEnemy->v.origin.z > pev->origin.z + 500.0f) {
|
||||
distance = 9999.0f;
|
||||
distance = kInfiniteDistance;
|
||||
}
|
||||
|
||||
// enemy within a good throw distance?
|
||||
|
|
@ -823,7 +823,7 @@ void Bot::getCampDirection (Vector *dest) {
|
|||
if (tempIndex == kInvalidNodeIndex || enemyIndex == kInvalidNodeIndex) {
|
||||
return;
|
||||
}
|
||||
float minDistance = 99999.0f;
|
||||
float minDistance = kInfiniteDistance;
|
||||
|
||||
int lookAtWaypoint = kInvalidNodeIndex;
|
||||
Path &path = graph[tempIndex];
|
||||
|
|
@ -1288,7 +1288,7 @@ void Bot::buyStuff () {
|
|||
assert (*pref < kNumWeapons);
|
||||
|
||||
selectedWeapon = &tab[*pref];
|
||||
count++;
|
||||
++count;
|
||||
|
||||
if (selectedWeapon->buyGroup == 1) {
|
||||
continue;
|
||||
|
|
@ -1474,7 +1474,7 @@ void Bot::buyStuff () {
|
|||
assert (*pref < kNumWeapons);
|
||||
|
||||
selectedWeapon = &tab[*pref];
|
||||
count++;
|
||||
++count;
|
||||
|
||||
if (selectedWeapon->buyGroup != 1) {
|
||||
continue;
|
||||
|
|
@ -1609,7 +1609,7 @@ void Bot::buyStuff () {
|
|||
break;
|
||||
}
|
||||
|
||||
m_buyState++;
|
||||
++m_buyState;
|
||||
pushMsgQueue (BotMsg::Buy);
|
||||
}
|
||||
|
||||
|
|
@ -1731,7 +1731,7 @@ void Bot::setConditions () {
|
|||
pushChatterMessage (Chatter::SniperKilled);
|
||||
}
|
||||
else {
|
||||
switch (numEnemiesNear (pev->origin, 99999.0f)) {
|
||||
switch (numEnemiesNear (pev->origin, kInfiniteDistance)) {
|
||||
case 0:
|
||||
if (rg.chance (50)) {
|
||||
pushChatterMessage (Chatter::NoEnemiesLeft);
|
||||
|
|
@ -2198,7 +2198,7 @@ void Bot::checkRadioQueue () {
|
|||
for (const auto &bot : bots) {
|
||||
if (bot->m_notKilled) {
|
||||
if (bot->m_targetEntity == m_radioEntity) {
|
||||
numFollowers++;
|
||||
++numFollowers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2450,7 +2450,7 @@ void Bot::checkRadioQueue () {
|
|||
|
||||
// if bot has no enemy
|
||||
if (m_lastEnemyOrigin.empty ()) {
|
||||
float nearestDistance = 99999.0f;
|
||||
float nearestDistance = kInfiniteDistance;
|
||||
|
||||
// take nearest enemy to ordering player
|
||||
for (const auto &client : util.getClients ()) {
|
||||
|
|
@ -2556,7 +2556,7 @@ void Bot::checkRadioQueue () {
|
|||
|
||||
// check if it's a ct command
|
||||
if (game.getTeam (m_radioEntity) == Team::CT && m_team == Team::CT && util.isFakeClient (m_radioEntity) && bots.getPlantedBombSearchTimestamp () < game.timebase ()) {
|
||||
float minDistance = 99999.0f;
|
||||
float minDistance = kInfiniteDistance;
|
||||
int bombPoint = kInvalidNodeIndex;
|
||||
|
||||
// find nearest bomb waypoint to player
|
||||
|
|
@ -2605,7 +2605,7 @@ void Bot::checkRadioQueue () {
|
|||
|
||||
// if bot has no enemy
|
||||
if (m_lastEnemyOrigin.empty ()) {
|
||||
float nearestDistance = 99999.0f;
|
||||
float nearestDistance = kInfiniteDistance;
|
||||
|
||||
// take nearest enemy to ordering player
|
||||
for (const auto &client : util.getClients ()) {
|
||||
|
|
@ -2767,15 +2767,15 @@ void Bot::updateAimDir () {
|
|||
void Bot::checkDarkness () {
|
||||
|
||||
// do not check for darkness at the start of the round
|
||||
if (m_spawnTime + 5.0f > game.timebase () || !graph.exists (m_currentNodeIndex)) {
|
||||
if (m_spawnTime + 5.0f > game.timebase () || !graph.exists (m_currentNodeIndex) || cr::fzero (m_path->light)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not check every frame
|
||||
if (m_checkDarkTime + 2.5f > game.timebase ()) {
|
||||
if (m_checkDarkTime + 5.0f > game.timebase ()) {
|
||||
return;
|
||||
}
|
||||
float skyColor = illum.getSkyColor ();
|
||||
auto skyColor = illum.getSkyColor ();
|
||||
|
||||
if (mp_flashlight.bool_ () && !m_hasNVG) {
|
||||
auto task = Task ();
|
||||
|
|
@ -2873,7 +2873,7 @@ void Bot::fastFrame () {
|
|||
}
|
||||
edict_t *killer = game.entityOfIndex (m_lastVoteKick);
|
||||
|
||||
killer->v.frags++;
|
||||
++killer->v.frags;
|
||||
MDLL_ClientKill (killer);
|
||||
}
|
||||
|
||||
|
|
@ -2898,8 +2898,8 @@ void Bot::frame () {
|
|||
if (m_slowFrameTimestamp > game.timebase ()) {
|
||||
return;
|
||||
}
|
||||
m_numFriendsLeft = numFriendsNear (pev->origin, 99999.0f);
|
||||
m_numEnemiesLeft = numEnemiesNear (pev->origin, 99999.0f);
|
||||
m_numFriendsLeft = numFriendsNear (pev->origin, kInfiniteDistance);
|
||||
m_numEnemiesLeft = numEnemiesNear (pev->origin, kInfiniteDistance);
|
||||
|
||||
if (bots.isBombPlanted () && m_team == Team::CT && m_notKilled) {
|
||||
const Vector &bombPosition = graph.getBombPos ();
|
||||
|
|
@ -3521,7 +3521,7 @@ void Bot::camp_ () {
|
|||
campPoints[numFoundPoints] = i;
|
||||
distances[numFoundPoints] = distance;
|
||||
|
||||
numFoundPoints++;
|
||||
++numFoundPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4261,7 +4261,7 @@ void Bot::escapeFromBomb_ () {
|
|||
else if (!hasActiveGoal ()) {
|
||||
clearSearchNodes ();
|
||||
|
||||
int lastSelectedGoal = kInvalidNodeIndex, minPathDistance = 99999;
|
||||
int lastSelectedGoal = kInvalidNodeIndex, minPathDistance = kInfiniteDistanceLong;
|
||||
float safeRadius = rg.float_ (1248.0f, 2048.0f);
|
||||
|
||||
for (int i = 0; i < graph.length (); ++i) {
|
||||
|
|
@ -5627,7 +5627,10 @@ bool Bot::isOutOfBombTimer () {
|
|||
|
||||
void Bot::updateHearing () {
|
||||
int hearEnemyIndex = kInvalidNodeIndex;
|
||||
float minDistance = 99999.0f;
|
||||
float minDistance = kInfiniteDistance;
|
||||
|
||||
// setup potential visibility set from engine
|
||||
auto set = game.getVisibilitySet (this, false);
|
||||
|
||||
// loop through all enemy clients to check for hearable stuff
|
||||
for (int i = 0; i < game.maxClients (); ++i) {
|
||||
|
|
@ -5636,6 +5639,10 @@ void Bot::updateHearing () {
|
|||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent () || client.team == m_team || client.timeSoundLasting < game.timebase ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game.checkVisibility (client.ent, set)) {
|
||||
continue;
|
||||
}
|
||||
float distance = (client.sound - pev->origin).length ();
|
||||
|
||||
if (distance > client.hearingDistance) {
|
||||
|
|
|
|||
|
|
@ -256,12 +256,7 @@ void Bot::prepareChatMessage (const String &message) {
|
|||
|
||||
// chat reply
|
||||
case 's':
|
||||
if (m_sayTextBuffer.entityIndex != -1) {
|
||||
m_chatBuffer.replace ("%s", humanizedName (m_sayTextBuffer.entityIndex));
|
||||
}
|
||||
else {
|
||||
m_chatBuffer.replace ("%s", getHighfragPlayer ());
|
||||
}
|
||||
m_chatBuffer.replace ("%s", m_sayTextBuffer.entityIndex != -1 ? humanizedName (m_sayTextBuffer.entityIndex) : getHighfragPlayer ());
|
||||
break;
|
||||
|
||||
// last bot victim
|
||||
|
|
@ -284,7 +279,7 @@ void Bot::prepareChatMessage (const String &message) {
|
|||
m_chatBuffer.replace ("%e", getPlayerAlive (true));
|
||||
break;
|
||||
};
|
||||
replaceCounter++;
|
||||
++replaceCounter;
|
||||
}
|
||||
finishPreparation ();
|
||||
}
|
||||
|
|
@ -292,8 +287,7 @@ void Bot::prepareChatMessage (const String &message) {
|
|||
bool Bot::checkChatKeywords (String &reply) {
|
||||
// this function parse chat buffer, and prepare buffer to keyword searching
|
||||
|
||||
String message = m_sayTextBuffer.sayText;
|
||||
return util.checkKeywords (message.uppercase (), reply);
|
||||
return util.checkKeywords (utf8tools.strToUpper (m_sayTextBuffer.sayText), reply);
|
||||
}
|
||||
|
||||
bool Bot::isReplyingToChat () {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ int Bot::numFriendsNear (const Vector &origin, float radius) {
|
|||
}
|
||||
|
||||
if ((client.origin - origin).lengthSq () < cr::square (radius)) {
|
||||
count++;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
@ -39,7 +39,7 @@ int Bot::numEnemiesNear (const Vector &origin, float radius) {
|
|||
}
|
||||
|
||||
if ((client.origin - origin).lengthSq () < cr::square (radius)) {
|
||||
count++;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
@ -210,6 +210,9 @@ bool Bot::lookupEnemies () {
|
|||
m_visibility = 0;
|
||||
m_enemyOrigin= nullvec;
|
||||
|
||||
// setup potential visibility set from engine
|
||||
auto set = game.getVisibilitySet (this, true);
|
||||
|
||||
if (!game.isNullEntity (m_enemy)) {
|
||||
player = m_enemy;
|
||||
|
||||
|
|
@ -232,7 +235,13 @@ bool Bot::lookupEnemies () {
|
|||
}
|
||||
player = client.ent;
|
||||
|
||||
if ((player->v.button & (IN_ATTACK | IN_ATTACK2)) && m_viewDistance < m_maxViewDistance) {
|
||||
// check the engine PVS
|
||||
if (!game.checkVisibility (player, set)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// extra skill player can see thru smoke... if beeing attacked
|
||||
if ((player->v.button & (IN_ATTACK | IN_ATTACK2)) && m_viewDistance < m_maxViewDistance && yb_whose_your_daddy.bool_ ()) {
|
||||
nearestDistance = cr::square (m_maxViewDistance);
|
||||
}
|
||||
|
||||
|
|
@ -625,14 +634,14 @@ bool Bot::isPenetrableObstacle2 (const Vector &dest) {
|
|||
game.testLine (source, dest, TraceIgnore::Everything, ent (), &tr);
|
||||
|
||||
while (tr.flFraction != 1.0f && numHits < 3) {
|
||||
numHits++;
|
||||
thikness++;
|
||||
++numHits;
|
||||
++thikness;
|
||||
|
||||
point = tr.vecEndPos + direction;
|
||||
|
||||
while (engfuncs.pfnPointContents (point) == CONTENTS_SOLID && thikness < 98) {
|
||||
point = point + direction;
|
||||
thikness++;
|
||||
++thikness;
|
||||
}
|
||||
game.testLine (point, dest, TraceIgnore::Everything, ent (), &tr);
|
||||
}
|
||||
|
|
@ -720,7 +729,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
if (tab[choosen].id == id) {
|
||||
break;
|
||||
}
|
||||
choosen++;
|
||||
++choosen;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -884,7 +893,7 @@ void Bot::fireWeapons () {
|
|||
choosenWeapon = selectIndex;
|
||||
}
|
||||
}
|
||||
selectIndex++;
|
||||
++selectIndex;
|
||||
}
|
||||
selectId = tab[choosenWeapon].id;
|
||||
|
||||
|
|
@ -914,7 +923,7 @@ void Bot::fireWeapons () {
|
|||
return;
|
||||
}
|
||||
}
|
||||
selectIndex++;
|
||||
++selectIndex;
|
||||
}
|
||||
selectId = Weapon::Knife; // no available ammo, use knife!
|
||||
}
|
||||
|
|
@ -1236,8 +1245,8 @@ bool Bot::usesRifle () {
|
|||
if (m_currentWeapon == tab->id) {
|
||||
break;
|
||||
}
|
||||
tab++;
|
||||
count++;
|
||||
++tab;
|
||||
++count;
|
||||
}
|
||||
|
||||
if (tab->id && count > 13) {
|
||||
|
|
@ -1255,8 +1264,8 @@ bool Bot::usesPistol () {
|
|||
if (m_currentWeapon == tab->id) {
|
||||
break;
|
||||
}
|
||||
tab++;
|
||||
count++;
|
||||
++tab;
|
||||
++count;
|
||||
}
|
||||
|
||||
if (tab->id && count < 7) {
|
||||
|
|
@ -1300,7 +1309,7 @@ int Bot::bestPrimaryCarried () {
|
|||
if (weapons & cr::bit (weaponTab[*pref].id)) {
|
||||
weaponIndex = i;
|
||||
}
|
||||
pref++;
|
||||
++pref;
|
||||
}
|
||||
return weaponIndex;
|
||||
}
|
||||
|
|
@ -1326,7 +1335,7 @@ int Bot::bestSecondaryCarried () {
|
|||
weaponIndex = i;
|
||||
break;
|
||||
}
|
||||
pref++;
|
||||
++pref;
|
||||
}
|
||||
return weaponIndex;
|
||||
}
|
||||
|
|
@ -1357,7 +1366,7 @@ bool Bot::rateGroundWeapon (edict_t *ent) {
|
|||
groundIndex = i;
|
||||
break;
|
||||
}
|
||||
pref++;
|
||||
++pref;
|
||||
}
|
||||
int hasWeapon = 0;
|
||||
|
||||
|
|
@ -1396,7 +1405,7 @@ void Bot::selectBestWeapon () {
|
|||
while (tab[selectIndex].id) {
|
||||
// is the bot NOT carrying this weapon?
|
||||
if (!(pev->weapons & cr::bit (tab[selectIndex].id))) {
|
||||
selectIndex++; // skip to next weapon
|
||||
++selectIndex; // skip to next weapon
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1417,7 +1426,7 @@ void Bot::selectBestWeapon () {
|
|||
if (ammoLeft) {
|
||||
chosenWeaponIndex = selectIndex;
|
||||
}
|
||||
selectIndex++;
|
||||
++selectIndex;
|
||||
}
|
||||
|
||||
chosenWeaponIndex %= kNumWeapons + 1;
|
||||
|
|
@ -1456,7 +1465,7 @@ int Bot::bestWeaponCarried () {
|
|||
num = i;
|
||||
}
|
||||
++i;
|
||||
tab++;
|
||||
++tab;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
|
@ -1580,7 +1589,7 @@ void Bot::checkReload () {
|
|||
}
|
||||
|
||||
if (weapons == 0) {
|
||||
m_reloadState++;
|
||||
++m_reloadState;
|
||||
|
||||
if (m_reloadState > Reload::Secondary) {
|
||||
m_reloadState = Reload::None;
|
||||
|
|
@ -1613,7 +1622,7 @@ void Bot::checkReload () {
|
|||
m_reloadState = Reload::None;
|
||||
return;
|
||||
}
|
||||
m_reloadState++;
|
||||
++m_reloadState;
|
||||
|
||||
if (m_reloadState > Reload::Secondary) {
|
||||
m_reloadState = Reload::None;
|
||||
|
|
|
|||
|
|
@ -1172,31 +1172,31 @@ int BotControl::menuGraphPage2 (int item) {
|
|||
Path &path = graph[i];
|
||||
|
||||
if (path.flags & NodeFlag::TerroristOnly) {
|
||||
terrPoints++;
|
||||
++terrPoints;
|
||||
}
|
||||
|
||||
if (path.flags & NodeFlag::CTOnly) {
|
||||
ctPoints++;
|
||||
++ctPoints;
|
||||
}
|
||||
|
||||
if (path.flags & NodeFlag::Goal) {
|
||||
goalPoints++;
|
||||
++goalPoints;
|
||||
}
|
||||
|
||||
if (path.flags & NodeFlag::Rescue) {
|
||||
rescuePoints++;
|
||||
++rescuePoints;
|
||||
}
|
||||
|
||||
if (path.flags & NodeFlag::Camp) {
|
||||
campPoints++;
|
||||
++campPoints;
|
||||
}
|
||||
|
||||
if (path.flags & NodeFlag::Sniper) {
|
||||
sniperPoints++;
|
||||
++sniperPoints;
|
||||
}
|
||||
|
||||
if (path.flags & NodeFlag::NoHostage) {
|
||||
noHostagePoints++;
|
||||
++noHostagePoints;
|
||||
}
|
||||
}
|
||||
msg ("Nodes: %d - T Points: %d\n"
|
||||
|
|
@ -1620,7 +1620,7 @@ void BotControl::showMenu (int id) {
|
|||
// make menus looks like we need only once
|
||||
if (!s_menusParsed) {
|
||||
for (auto &parsed : m_menus) {
|
||||
const String &translated = game.translate (parsed.text.chars ());
|
||||
const String &translated = conf.translate (parsed.text.chars ());
|
||||
|
||||
// translate all the things
|
||||
parsed.text = translated;
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
|||
ent->v.renderamt = 127; // set its transparency amount
|
||||
ent->v.effects |= EF_NODRAW;
|
||||
|
||||
m_spawnCount[Team::CT]++;
|
||||
++m_spawnCount[Team::CT];
|
||||
}
|
||||
else if (strcmp (classname, "info_player_deathmatch") == 0) {
|
||||
engfuncs.pfnSetModel (ent, ENGINE_STR ("models/player/terror/terror.mdl"));
|
||||
|
|
@ -113,7 +113,7 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
|||
ent->v.renderamt = 127; // set its transparency amount
|
||||
ent->v.effects |= EF_NODRAW;
|
||||
|
||||
m_spawnCount[Team::Terrorist]++;
|
||||
++m_spawnCount[Team::Terrorist];
|
||||
}
|
||||
|
||||
else if (strcmp (classname, "info_vip_start") == 0) {
|
||||
|
|
@ -341,6 +341,49 @@ void Game::playSound (edict_t *ent, const char *sound) {
|
|||
engfuncs.pfnEmitSound (ent, CHAN_WEAPON, sound, 1.0f, ATTN_NORM, 0, 100);
|
||||
}
|
||||
|
||||
bool Game::checkVisibility (edict_t *ent, uint8 *set) {
|
||||
if (!set) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ent->headnode < 0) {
|
||||
for (int i = 0; i < ent->num_leafs; ++i) {
|
||||
auto leaf = ent->leafnums[i];
|
||||
|
||||
if (set[leaf >> 3] & cr::bit (leaf & 7)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 48; ++i) {
|
||||
auto leaf = ent->leafnums[i];
|
||||
if (leaf == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (set[leaf >> 3] & cr::bit (leaf & 7)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return engfuncs.pfnCheckVisibility (ent, set) > 0;
|
||||
}
|
||||
|
||||
uint8 *Game::getVisibilitySet (Bot *bot, bool pvs) {
|
||||
if (is (GameFlags::Xash3D)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto eyes = bot->getEyesPos ();
|
||||
|
||||
if (bot->pev->flags & FL_DUCKING) {
|
||||
eyes += VEC_HULL_MIN - VEC_DUCK_HULL_MIN;
|
||||
}
|
||||
float org[3] { eyes.x, eyes.y, eyes.z };
|
||||
|
||||
return pvs ? engfuncs.pfnSetFatPVS (org) : engfuncs.pfnSetFatPAS (org);
|
||||
}
|
||||
|
||||
void Game::sendClientMessage (bool console, edict_t *ent, const char *message) {
|
||||
// helper to sending the client message
|
||||
|
||||
|
|
@ -498,20 +541,6 @@ void Game::registerCvars (bool gameVars) {
|
|||
}
|
||||
}
|
||||
|
||||
const char *Game::translate (const char *input) {
|
||||
// this function translate input string into needed language
|
||||
|
||||
if (isDedicated ()) {
|
||||
return input;
|
||||
}
|
||||
static String result;
|
||||
|
||||
if (m_language.find (input, result)) {
|
||||
return result.chars ();
|
||||
}
|
||||
return input; // nothing found
|
||||
}
|
||||
|
||||
void Game::processMessages (void *ptr) {
|
||||
if (m_msgBlock.msg == NetMsg::None) {
|
||||
return;
|
||||
|
|
@ -993,7 +1022,7 @@ void Game::processMessages (void *ptr) {
|
|||
default:
|
||||
logger.error ("Network message handler error. Call to unrecognized message id (%d).\n", m_msgBlock.msg);
|
||||
}
|
||||
m_msgBlock.state++; // and finally update network message state
|
||||
++m_msgBlock.state; // and finally update network message state
|
||||
}
|
||||
|
||||
bool Game::loadCSBinary () {
|
||||
|
|
@ -1029,16 +1058,16 @@ bool Game::loadCSBinary () {
|
|||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
// search the libraries inside game dlls directory
|
||||
for (const auto &lib : libs) {
|
||||
auto *path = strings.format ("%s/dlls/%s", modname, lib.chars ());
|
||||
auto path = strings.format ("%s/dlls/%s", modname, lib.chars ());
|
||||
|
||||
// if we can't read file, skip it
|
||||
if (!File::exists (path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// special case, czero is always detected first, as it's has custom directory
|
||||
if (strcmp (modname, "czero") == 0) {
|
||||
m_gameFlags |= (GameFlags::ConditionZero | GameFlags::HasBotVoice | GameFlags::HasFakePings);
|
||||
|
|
@ -1075,7 +1104,7 @@ bool Game::loadCSBinary () {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (entity != nullptr) {
|
||||
m_gameFlags |= (GameFlags::Modern | GameFlags::HasBotVoice | GameFlags::HasFakePings);
|
||||
}
|
||||
|
|
@ -1211,15 +1240,15 @@ void Game::slowFrame () {
|
|||
}
|
||||
ctrl.maintainAdminRights ();
|
||||
|
||||
// calculate light levels for all waypoints if needed
|
||||
graph.initLightLevels ();
|
||||
|
||||
// update bot difficulties to newly selected from cvar
|
||||
bots.updateBotDifficulties ();
|
||||
|
||||
// update client pings
|
||||
util.calculatePings ();
|
||||
|
||||
// initialize light levels
|
||||
graph.initLightLevels ();
|
||||
|
||||
// detect csdm
|
||||
detectDeathmatch ();
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ int BotGraph::clearConnections (int index) {
|
|||
if (bots.hasBotsOnline ()) {
|
||||
bots.kickEveryone (true);
|
||||
}
|
||||
const int kInfiniteDistance = 99999;
|
||||
|
||||
struct Connection {
|
||||
int index;
|
||||
|
|
@ -68,7 +67,7 @@ int BotGraph::clearConnections (int index) {
|
|||
void reset () {
|
||||
index = kInvalidNodeIndex;
|
||||
number = kInvalidNodeIndex;
|
||||
distance = kInfiniteDistance;
|
||||
distance = kInfiniteDistanceLong;
|
||||
angles = 0.0f;
|
||||
}
|
||||
};
|
||||
|
|
@ -86,7 +85,7 @@ int BotGraph::clearConnections (int index) {
|
|||
cur.distance = link.distance;
|
||||
|
||||
if (cur.index == kInvalidNodeIndex) {
|
||||
cur.distance = kInfiniteDistance;
|
||||
cur.distance = kInfiniteDistanceLong;
|
||||
}
|
||||
|
||||
if (cur.distance < top.distance) {
|
||||
|
|
@ -117,7 +116,7 @@ int BotGraph::clearConnections (int index) {
|
|||
// calculate angles related to the angle of the closeset connected node
|
||||
for (auto &cur : sorted) {
|
||||
if (cur.index == kInvalidNodeIndex) {
|
||||
cur.distance = kInfiniteDistance;
|
||||
cur.distance = kInfiniteDistanceLong;
|
||||
cur.angles = 360.0f;
|
||||
}
|
||||
else if (exists (cur.index)) {
|
||||
|
|
@ -415,7 +414,7 @@ void BotGraph::addPath (int addIndex, int pathIndex, float distance) {
|
|||
}
|
||||
|
||||
// there wasn't any free space. try exchanging it with a long-distance path
|
||||
int maxDistance = -9999;
|
||||
int maxDistance = -kInfiniteDistanceLong;
|
||||
int slot = kInvalidNodeIndex;
|
||||
|
||||
for (int i = 0; i < kMaxNodeLinks; ++i) {
|
||||
|
|
@ -710,7 +709,7 @@ void BotGraph::add (int type, const Vector &pos) {
|
|||
|
||||
// Ladder nodes need careful connections
|
||||
if (path->flags & NodeFlag::Ladder) {
|
||||
float minDistance = 9999.0f;
|
||||
float minDistance = kInfiniteDistance;
|
||||
int destIndex = kInvalidNodeIndex;
|
||||
|
||||
TraceResult tr;
|
||||
|
|
@ -1960,7 +1959,7 @@ void BotGraph::frame () {
|
|||
m_editor->v.movetype = MOVETYPE_NOCLIP;
|
||||
}
|
||||
|
||||
float nearestDistance = 99999.0f;
|
||||
float nearestDistance = kInfiniteDistance;
|
||||
int nearestIndex = kInvalidNodeIndex;
|
||||
|
||||
// check if it's time to add jump node
|
||||
|
|
@ -2011,7 +2010,7 @@ void BotGraph::frame () {
|
|||
m_facingAtIndex = getFacingIndex ();
|
||||
|
||||
// reset the minimal distance changed before
|
||||
nearestDistance = 999999.0f;
|
||||
nearestDistance = kInfiniteDistance;
|
||||
|
||||
// now iterate through all nodes in a map, and draw required ones
|
||||
for (auto &path : m_paths) {
|
||||
|
|
@ -2234,8 +2233,8 @@ void BotGraph::frame () {
|
|||
|
||||
// show the information about that point
|
||||
graphMessage.assignf ("\n\n\n\n Graph Information:\n\n"
|
||||
" Node %d of %d, Radius: %.1f\n"
|
||||
" Flags: %s\n\n", nearestIndex, m_paths.length () - 1, path.radius, getFlagsAsStr (nearestIndex));
|
||||
" Node %d of %d, Radius: %.1f, Light: %.1f\n"
|
||||
" Flags: %s\n\n", nearestIndex, m_paths.length () - 1, path.radius, path.light, getFlagsAsStr (nearestIndex));
|
||||
|
||||
// if node is not changed display experience also
|
||||
if (!m_hasChanged) {
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
|
|||
|
||||
// keep bot number up to date
|
||||
bots.maintainQuota ();
|
||||
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
RETURN_META (MRES_IGNORED);
|
||||
}
|
||||
|
|
@ -435,7 +435,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
|
|||
|
||||
functionTable->pfnCmdStart = [] (const edict_t *player, usercmd_t *cmd, unsigned int random_seed) {
|
||||
auto ent = const_cast <edict_t *> (player);
|
||||
|
||||
|
||||
// if we're handle pings for bots and clients, clear IN_SCORE button so SV_ShouldUpdatePing engine function return false
|
||||
// and SV_EmitPings will not overwrite our results
|
||||
if (game.is (GameFlags::HasFakePings) && yb_show_latency.int_ () == 2) {
|
||||
|
|
@ -534,10 +534,6 @@ CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, int *interfaceV
|
|||
|
||||
auto api_GetNewDLLFunctions = game.lib ().resolve <int (*) (newgamefuncs_t *, int *)> (__FUNCTION__);
|
||||
|
||||
if (api_GetNewDLLFunctions == nullptr) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!api_GetNewDLLFunctions || !api_GetNewDLLFunctions (functionTable, interfaceVersion)) {
|
||||
logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__);
|
||||
return FALSE;
|
||||
|
|
|
|||
|
|
@ -66,12 +66,12 @@ void BotManager::createKillerEntity () {
|
|||
|
||||
m_killerEntity = engfuncs.pfnCreateNamedEntity (MAKE_STRING ("trigger_hurt"));
|
||||
|
||||
m_killerEntity->v.dmg = 9999.0f;
|
||||
m_killerEntity->v.dmg = kInfiniteDistance;
|
||||
m_killerEntity->v.dmg_take = 1.0f;
|
||||
m_killerEntity->v.dmgtime = 2.0f;
|
||||
m_killerEntity->v.effects |= EF_NODRAW;
|
||||
|
||||
engfuncs.pfnSetOrigin (m_killerEntity, Vector (-99999.0f, -99999.0f, -99999.0f));
|
||||
engfuncs.pfnSetOrigin (m_killerEntity, Vector (-kInfiniteDistance, -kInfiniteDistance, -kInfiniteDistance));
|
||||
MDLL_Spawn (m_killerEntity);
|
||||
}
|
||||
|
||||
|
|
@ -224,12 +224,13 @@ Bot *BotManager::findBotByIndex (int index) {
|
|||
if (index < 0 || index >= kGameMaxPlayers) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto &bot : m_bots) {
|
||||
if (bot->m_index == index) {
|
||||
return bot.get ();
|
||||
}
|
||||
}
|
||||
return nullptr; // no bot
|
||||
return nullptr; // no bot``
|
||||
}
|
||||
|
||||
Bot *BotManager::findBotByEntity (edict_t *ent) {
|
||||
|
|
@ -501,12 +502,10 @@ void BotManager::serverFill (int selection, int personality, int difficulty, int
|
|||
void BotManager::kickEveryone (bool instant, bool zeroQuota) {
|
||||
// this function drops all bot clients from server (this function removes only yapb's)`q
|
||||
|
||||
if (!hasBotsOnline () || !yb_quota.bool_ ()) {
|
||||
return;
|
||||
if (yb_quota.bool_ ()) {
|
||||
ctrl.msg ("Bots are removed from server.");
|
||||
}
|
||||
|
||||
ctrl.msg ("Bots are removed from server.");
|
||||
|
||||
if (zeroQuota) {
|
||||
decrementQuota (0);
|
||||
}
|
||||
|
|
@ -592,7 +591,7 @@ bool BotManager::kickRandom (bool decQuota, Team fromTeam) {
|
|||
|
||||
// if no dead bots found try to find one with lowest amount of frags
|
||||
Bot *selected = nullptr;
|
||||
float score = 9999.0f;
|
||||
float score = kInfiniteDistance;
|
||||
|
||||
// search bots in this team
|
||||
for (const auto &bot : m_bots) {
|
||||
|
|
@ -702,10 +701,10 @@ Twin <int, int> BotManager::countTeamPlayers () {
|
|||
for (const auto &client : util.getClients ()) {
|
||||
if (client.flags & ClientFlags::Used) {
|
||||
if (client.team2 == Team::Terrorist) {
|
||||
ts++;
|
||||
++ts;
|
||||
}
|
||||
else if (client.team2 == Team::CT) {
|
||||
cts++;
|
||||
++cts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -748,9 +747,9 @@ void BotManager::updateTeamEconomics (int team, bool setTrue) {
|
|||
for (const auto &bot : m_bots) {
|
||||
if (bot->m_team == team) {
|
||||
if (bot->m_moneyAmount <= econLimit[EcoLimit::PrimaryGreater]) {
|
||||
numPoorPlayers++;
|
||||
++numPoorPlayers;
|
||||
}
|
||||
numTeamPlayers++; // update count of team
|
||||
++numTeamPlayers; // update count of team
|
||||
}
|
||||
}
|
||||
m_economicsGood[team] = true;
|
||||
|
|
@ -920,7 +919,7 @@ int BotManager::getHumansCount (bool ignoreSpectators) {
|
|||
if (ignoreSpectators && client.team2 != Team::Terrorist && client.team2 != Team::CT) {
|
||||
continue;
|
||||
}
|
||||
count++;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
@ -933,7 +932,7 @@ int BotManager::getAliveHumansCount () {
|
|||
|
||||
for (const auto &client : util.getClients ()) {
|
||||
if ((client.flags & (ClientFlags::Used | ClientFlags::Alive)) && bots[client.ent] == nullptr && !(client.ent->v.flags & FL_FAKECLIENT)) {
|
||||
count++;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
@ -952,7 +951,7 @@ bool BotManager::isTeamStacked (int team) {
|
|||
|
||||
for (const auto &client : util.getClients ()) {
|
||||
if ((client.flags & ClientFlags::Used) && client.team2 != Team::Unassigned && client.team2 != Team::Spectator) {
|
||||
teamCount[client.team2]++;
|
||||
++teamCount[client.team2];
|
||||
}
|
||||
}
|
||||
return teamCount[team] + 1 > teamCount[team == Team::CT ? Team::Terrorist : Team::CT] + limitTeams;
|
||||
|
|
@ -1036,7 +1035,7 @@ void Bot::newRound () {
|
|||
m_askCheckTime = rg.float_ (30.0f, 90.0f);
|
||||
m_minSpeed = 260.0f;
|
||||
m_prevSpeed = 0.0f;
|
||||
m_prevOrigin = Vector (9999.0f, 9999.0f, 9999.0f);
|
||||
m_prevOrigin = Vector (kInfiniteDistance, kInfiniteDistance, kInfiniteDistance);
|
||||
m_prevTime = game.timebase ();
|
||||
m_lookUpdateTime = game.timebase ();
|
||||
m_aimErrorTime = game.timebase ();
|
||||
|
|
@ -1962,9 +1961,11 @@ void BotConfig::loadChatConfig () {
|
|||
keywords.clear ();
|
||||
replies.clear ();
|
||||
}
|
||||
|
||||
keywords.clear ();
|
||||
keywords = cr::move (line.substr (4).split (","));
|
||||
|
||||
for (const auto &key : line.substr (4).split (",")) {
|
||||
keywords.emplace (utf8tools.strToUpper (key));
|
||||
}
|
||||
|
||||
for (auto &keyword : keywords) {
|
||||
keyword.trim ().trim ("\"");
|
||||
|
|
@ -2007,7 +2008,7 @@ void BotConfig::loadLanguageConfig () {
|
|||
Twin <String, String> lang;
|
||||
|
||||
// clear all the translations before new load
|
||||
game.clearTranslation ();
|
||||
m_language.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
if (isCommentLine (line)) {
|
||||
|
|
@ -2020,7 +2021,7 @@ void BotConfig::loadLanguageConfig () {
|
|||
}
|
||||
|
||||
if (!lang.second.empty () && !lang.first.empty ()) {
|
||||
game.addTranslation (lang.first.trim (), lang.second.trim ());
|
||||
m_language.push (lang.first.trim (), lang.second.trim ());
|
||||
}
|
||||
}
|
||||
else if (line.startsWith ("[TRANSLATED]") && !temp.empty ()) {
|
||||
|
|
@ -2179,3 +2180,18 @@ WeaponInfo &BotConfig::findWeaponById (const int id) {
|
|||
}
|
||||
return m_weapons.at (0);
|
||||
}
|
||||
|
||||
|
||||
const char *BotConfig::translate (const char *input) {
|
||||
// this function translate input string into needed language
|
||||
|
||||
if (game.isDedicated ()) {
|
||||
return input;
|
||||
}
|
||||
static String result;
|
||||
|
||||
if (m_language.find (input, result)) {
|
||||
return result.chars ();
|
||||
}
|
||||
return input; // nothing found
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
|
|||
{
|
||||
// force bomber to select closest goal, if round-start goal was reset by something
|
||||
if (m_hasC4 && bots.getRoundStartTime () + 20.0f < game.timebase ()) {
|
||||
float minDist = 9999999.0f;
|
||||
float minDist = kInfiniteDistance;
|
||||
int count = 0;
|
||||
|
||||
for (auto &point : graph.m_goalPoints) {
|
||||
|
|
@ -236,7 +236,7 @@ void Bot::postprocessGoals (const IntArray &goals, int *result) {
|
|||
if (index > 0) {
|
||||
index--;
|
||||
}
|
||||
searchCount++;
|
||||
++searchCount;
|
||||
continue;
|
||||
}
|
||||
result[index] = rand;
|
||||
|
|
@ -350,9 +350,6 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||
m_isStuck = false;
|
||||
|
||||
if (doPlayerAvoidance (dirNormal) || m_avoidTime > game.timebase ()) {
|
||||
return;
|
||||
}
|
||||
TraceResult tr;
|
||||
|
||||
// Standing still, no need to check?
|
||||
|
|
@ -625,6 +622,11 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// avoid players if not already stuck
|
||||
if (!m_isStuck) {
|
||||
doPlayerAvoidance (dirNormal);
|
||||
}
|
||||
}
|
||||
|
||||
bool Bot::updateNavigation () {
|
||||
|
|
@ -1536,7 +1538,7 @@ bool Bot::findBestNearestNode () {
|
|||
|
||||
int busy = kInvalidNodeIndex;
|
||||
|
||||
float lessDist[3] = { 9999.0f, 9999.0f , 9999.0f };
|
||||
float lessDist[3] = { kInfiniteDistance, kInfiniteDistance, kInfiniteDistance };
|
||||
int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex , kInvalidNodeIndex };
|
||||
|
||||
auto &bucket = graph.getNodesInBucket (pev->origin);
|
||||
|
|
@ -1695,7 +1697,7 @@ void Bot::findValidNode () {
|
|||
}
|
||||
else {
|
||||
findBestNearestNode ();
|
||||
m_rechoiceGoalCount++;
|
||||
++m_rechoiceGoalCount;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1809,7 +1811,7 @@ int Bot::findBombNode () {
|
|||
}
|
||||
|
||||
int goal = 0, count = 0;
|
||||
float lastDistance = 999999.0f;
|
||||
float lastDistance = kInfiniteDistance;
|
||||
|
||||
// find nearest goal node either to bomb (if "heard" or player)
|
||||
for (auto &point : goals) {
|
||||
|
|
@ -2811,7 +2813,7 @@ int Bot::findCampingDirection () {
|
|||
distTab[count] = (pev->origin - path.origin).lengthSq ();
|
||||
visibility[count] = path.vis.crouch + path.vis.stand;
|
||||
|
||||
count++;
|
||||
++count;
|
||||
}
|
||||
else {
|
||||
float distance = (pev->origin - path.origin).lengthSq ();
|
||||
|
|
@ -3029,6 +3031,11 @@ bool Bot::isOccupiedPoint (int index) {
|
|||
if (!(client.flags & (ClientFlags::Used | ClientFlags::Alive)) || client.team != m_team || client.ent == ent ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not check clients far away from us
|
||||
if ((pev->origin - client.origin).lengthSq () > cr::square (320.0f)) {
|
||||
continue;
|
||||
}
|
||||
auto bot = bots[client.ent];
|
||||
|
||||
if (bot == this) {
|
||||
|
|
@ -3058,7 +3065,7 @@ edict_t *Bot::lookupButton (const char *targetName) {
|
|||
if (util.isEmptyStr (targetName)) {
|
||||
return nullptr;
|
||||
}
|
||||
float nearestDistance = 99999.0f;
|
||||
float nearestDistance = kInfiniteDistance;
|
||||
edict_t *searchEntity = nullptr, *foundEntity = nullptr;
|
||||
|
||||
// find the nearest button which can open our target
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ void BotUtils::attachSoundsToClients (edict_t *ent, const char *sample, float vo
|
|||
int index = game.indexOfPlayer (ent);
|
||||
|
||||
if (index < 0 || index >= game.maxClients ()) {
|
||||
float nearestDistance = 99999.0f;
|
||||
float nearestDistance = kInfiniteDistance;
|
||||
|
||||
// loop through all players
|
||||
for (int i = 0; i < game.maxClients (); ++i) {
|
||||
|
|
@ -523,7 +523,7 @@ void BotUtils::calculatePings () {
|
|||
client.ping = getPingBitmask (client.ent, loss, ping > 0 ? ping / 2 : rg.int_ (8, 16)); // getting player ping sometimes fails
|
||||
client.pingUpdate = true; // force resend ping
|
||||
|
||||
numHumans++;
|
||||
++numHumans;
|
||||
|
||||
average.first += ping;
|
||||
average.second += loss;
|
||||
|
|
@ -578,7 +578,7 @@ void BotUtils::sendPings (edict_t *to) {
|
|||
if (!client.ping) {
|
||||
client.ping = getPingBitmask (client.ent, rg.int_ (5, 10), rg.int_ (15, 40));
|
||||
}
|
||||
|
||||
|
||||
msg.start (MSG_ONE_UNRELIABLE, kGamePingSVC, nullvec, to)
|
||||
.writeLong (client.ping)
|
||||
.end ();
|
||||
|
|
@ -635,7 +635,7 @@ int32 BotUtils::sendTo (int socket, const void *message, size_t length, int flag
|
|||
buffer.skip <uint8> (); // protocol
|
||||
|
||||
// skip server name, folder, map game
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
buffer.skipString ();
|
||||
}
|
||||
buffer.skip <short> (); // steam app id
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue