bot: improved logic when in freezetime
fix: full reset entvars of fakeclient upon entity creation fix: high skilled bots can't shoot with sniper weapons on short distances (ref #582) fix: fill server will not overflow number of bots anymore fix: do not treat dormant entites as valid clients for bots mgr: : single bot kick is now working in non-sequential order Co-Authored-By: Max <161382234+dyspose@users.noreply.github.com>
This commit is contained in:
parent
bd9286cf87
commit
4aa6aff8b6
9 changed files with 195 additions and 68 deletions
|
|
@ -230,6 +230,9 @@ public:
|
|||
// ensure prosperous gaming environment as per: https://github.com/yapb/yapb/issues/575
|
||||
void ensureHealthyGameEnvironment ();
|
||||
|
||||
// creates a fake client's a nd resets all the entvars
|
||||
edict_t *createFakeClient (StringRef name);
|
||||
|
||||
// public inlines
|
||||
public:
|
||||
// get the current time on server
|
||||
|
|
|
|||
|
|
@ -377,7 +377,8 @@ private:
|
|||
int bestPrimaryCarried ();
|
||||
int bestSecondaryCarried ();
|
||||
int bestGrenadeCarried ();
|
||||
int bestWeaponCarried ();
|
||||
int getBestOwnedWeapon ();
|
||||
int getBestOwnedPistol ();
|
||||
int changeNodeIndex (int index);
|
||||
int numEnemiesNear (const Vector &origin, const float radius);
|
||||
int numFriendsNear (const Vector &origin, const float radius);
|
||||
|
|
@ -416,6 +417,7 @@ private:
|
|||
bool reactOnEnemy ();
|
||||
bool selectBestNextNode ();
|
||||
bool hasAnyWeapons ();
|
||||
bool hasAnyAmmoInClip ();
|
||||
bool isKnifeMode ();
|
||||
bool isGrenadeWar ();
|
||||
bool isDeadlyMove (const Vector &to);
|
||||
|
|
@ -506,6 +508,7 @@ private:
|
|||
void refreshCreatureStatus (char *infobuffer);
|
||||
void updateRightRef ();
|
||||
void donateC4ToHuman ();
|
||||
void clearAmmoInfo ();
|
||||
|
||||
void completeTask ();
|
||||
void executeTasks ();
|
||||
|
|
|
|||
|
|
@ -427,8 +427,8 @@ void Bot::updatePickups () {
|
|||
pickupType = Pickup::Weapon;
|
||||
|
||||
if (cv_pickup_ammo_and_kits) {
|
||||
const int primaryWeaponCarried = bestPrimaryCarried ();
|
||||
const int secondaryWeaponCarried = bestSecondaryCarried ();
|
||||
const int primaryWeaponCarried = getBestOwnedWeapon ();
|
||||
const int secondaryWeaponCarried = getBestOwnedPistol ();
|
||||
|
||||
const auto &config = conf.getWeapons ();
|
||||
const auto &primary = config[primaryWeaponCarried];
|
||||
|
|
@ -1872,13 +1872,13 @@ void Bot::setConditions () {
|
|||
}
|
||||
}
|
||||
else {
|
||||
auto currentTime = game.time();
|
||||
auto currentTime = game.time ();
|
||||
|
||||
m_killsInterval = currentTime - m_lastVictimTime;
|
||||
if (m_killsInterval <= 5) {
|
||||
m_killsCount++;
|
||||
if (m_killsCount > 2) {
|
||||
pushChatterMessage(Chatter::OnARoll);
|
||||
pushChatterMessage (Chatter::OnARoll);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -1961,7 +1961,7 @@ void Bot::filterTasks () {
|
|||
float tempFear = m_fearLevel;
|
||||
float tempAgression = m_agressionLevel;
|
||||
|
||||
// decrease fear if teammates near
|
||||
// decrease fear if players near
|
||||
int friendlyNum = 0;
|
||||
|
||||
if (!m_lastEnemyOrigin.empty ()) {
|
||||
|
|
@ -3020,7 +3020,13 @@ void Bot::update () {
|
|||
}
|
||||
|
||||
void Bot::logicDuringFreezetime () {
|
||||
pev->button = 0;
|
||||
if (m_isStale) {
|
||||
return;
|
||||
}
|
||||
pev->button &= ~IN_DUCK;
|
||||
|
||||
updateLookAngles ();
|
||||
runMovement ();
|
||||
|
||||
if (m_changeViewTime > game.time ()) {
|
||||
return;
|
||||
|
|
@ -3030,20 +3036,50 @@ void Bot::logicDuringFreezetime () {
|
|||
pev->button |= IN_JUMP;
|
||||
m_jumpTime = game.time ();
|
||||
}
|
||||
static Array <Bot *> teammates {};
|
||||
teammates.clear ();
|
||||
static Array <edict_t *> players {};
|
||||
players.clear ();
|
||||
|
||||
for (const auto &bot : bots) {
|
||||
if (bot->m_isAlive && bot->m_team == m_team && bot.get () != this && seesEntity (bot->pev->origin)) {
|
||||
teammates.push (bot.get ());
|
||||
// search for visible enemies
|
||||
for (const auto &client : util.getClients ()) {
|
||||
if (!(client.flags & ClientFlags::Used)
|
||||
|| !(client.flags & ClientFlags::Alive)
|
||||
|| client.team == m_team
|
||||
|| client.ent == ent ()
|
||||
|| !client.ent
|
||||
|| !seesEntity (client.origin)) {
|
||||
continue;
|
||||
}
|
||||
players.push (client.ent);
|
||||
}
|
||||
|
||||
if (!teammates.empty ()) {
|
||||
auto bot = teammates.random ();
|
||||
// use teammates
|
||||
if (players.empty ()) {
|
||||
for (const auto &client : util.getClients ()) {
|
||||
if (!(client.flags & ClientFlags::Used)
|
||||
|| !(client.flags & ClientFlags::Alive)
|
||||
|| client.team != m_team
|
||||
|| client.ent == ent ()
|
||||
|| !client.ent
|
||||
|| !seesEntity (client.origin)) {
|
||||
continue;
|
||||
}
|
||||
players.push (client.ent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
selectBestWeapon ();
|
||||
}
|
||||
|
||||
if (bot) {
|
||||
m_lookAt = bot->pev->origin + bot->pev->view_ofs;
|
||||
if (!players.empty ()) {
|
||||
auto ent = players.random ();
|
||||
|
||||
if (ent) {
|
||||
m_lookAt = ent->v.origin + ent->v.view_ofs;
|
||||
|
||||
if (m_buyingFinished) {
|
||||
m_enemy = ent;
|
||||
m_enemyOrigin = ent->v.origin;
|
||||
}
|
||||
}
|
||||
|
||||
// good time too greet everyone
|
||||
|
|
@ -3067,6 +3103,10 @@ void Bot::executeTasks () {
|
|||
void Bot::checkSpawnConditions () {
|
||||
// this function is called instead of ai when buying finished, but freezetime is not yet left.
|
||||
|
||||
if (!game.isNullEntity (m_enemy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// switch to knife if time to do this
|
||||
if (m_checkKnifeSwitch && m_buyingFinished && m_spawnTime + rg (5.0f, 7.5f) < game.time ()) {
|
||||
if (rg (1, 100) < 2 && cv_spraypaints) {
|
||||
|
|
@ -3203,7 +3243,7 @@ void Bot::logic () {
|
|||
}
|
||||
}
|
||||
|
||||
// if bomb planted warn teammates !
|
||||
// if bomb planted warn players !
|
||||
if (bots.hasBombSay (BombPlantedSay::Chatter) && bots.isBombPlanted () && m_team == Team::CT) {
|
||||
pushChatterMessage (Chatter::GottaFindC4);
|
||||
bots.clearBombSay (BombPlantedSay::Chatter);
|
||||
|
|
@ -3881,7 +3921,7 @@ bool Bot::isOutOfBombTimer () {
|
|||
}
|
||||
bool hasTeammatesWithDefuserKit = false;
|
||||
|
||||
// check if our teammates has defusal kit
|
||||
// check if our players has defusal kit
|
||||
for (const auto &bot : bots) {
|
||||
// search players with defuse kit
|
||||
if (bot.get () != this && bot->m_team == Team::CT && bot->m_hasDefuser && bombOrigin.distanceSq (bot->pev->origin) < cr::sqrf (512.0f)) {
|
||||
|
|
@ -4246,4 +4286,3 @@ void Bot::donateC4ToHuman () {
|
|||
MDLL_Touch (bomb, recipient);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ void Bot::prepareChatMessage (StringRef message) {
|
|||
};
|
||||
|
||||
// get bot's victim
|
||||
auto getMyVictim = [&] () -> String {;
|
||||
auto getMyVictim = [&] () -> String {
|
||||
return humanizedName (game.indexOfPlayer (m_lastVictim));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1167,8 +1167,7 @@ void Bot::fireWeapons () {
|
|||
|
||||
// is the bot carrying this weapon?
|
||||
if (weapons & cr::bit (wid)) {
|
||||
if (getAmmo (wid) >= tab[selectIndex].minPrimaryAmmo) {
|
||||
|
||||
if (getAmmo (wid) >= tab[selectIndex].minPrimaryAmmo && wid == m_currentWeapon) {
|
||||
// available ammo found, reload weapon
|
||||
if (m_reloadState == Reload::None || m_reloadCheckTime > game.time ()) {
|
||||
m_isReloading = true;
|
||||
|
|
@ -1201,12 +1200,12 @@ bool Bot::isWeaponBadAtDistance (int weaponIndex, float distance) {
|
|||
}
|
||||
const auto weaponType = info[weaponIndex].type;
|
||||
|
||||
if (weaponType == WeaponType::Melee) {
|
||||
if (weaponType == WeaponType::Melee || !(weaponType == WeaponType::Shotgun || weaponType == WeaponType::Sniper)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check is ammo available for secondary weapon
|
||||
if (m_ammoInClip[info[bestSecondaryCarried ()].id] >= 3) {
|
||||
if (m_ammoInClip[info[getBestOwnedPistol ()].id] <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1270,6 +1269,11 @@ void Bot::focusEnemy () {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fire anyway at close distance
|
||||
if (distanceSq < cr::sqrf (90.0f)) {
|
||||
m_wantsToFire = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1650,8 +1654,27 @@ bool Bot::hasAnyWeapons () {
|
|||
return !!(pev->weapons & (kPrimaryWeaponMask | kSecondaryWeaponMask));
|
||||
}
|
||||
|
||||
bool Bot::hasAnyAmmoInClip () {
|
||||
bool hasAmmo = false;
|
||||
|
||||
if (!hasAnyWeapons ()) {
|
||||
return false;
|
||||
}
|
||||
const auto pri = getBestOwnedWeapon ();
|
||||
const auto sec = getBestOwnedPistol ();
|
||||
|
||||
if (pri > 0 || sec > 0) {
|
||||
const auto &info = conf.getWeapons ();
|
||||
hasAmmo = pri > 0 && m_ammoInClip[info[pri].id] > 0 || sec > 0 && m_ammoInClip[info[sec].id] > 0;
|
||||
}
|
||||
return hasAmmo;
|
||||
}
|
||||
|
||||
bool Bot::isKnifeMode () {
|
||||
return cv_jasonmode || (usesKnife () && !hasAnyWeapons ()) || m_isCreature;
|
||||
return cv_jasonmode ||
|
||||
(usesKnife () && !hasAnyWeapons ())
|
||||
|| m_isCreature
|
||||
|| ((m_states & Sense::SeeingEnemy) && usesKnife () && !hasAnyAmmoInClip ());
|
||||
}
|
||||
|
||||
bool Bot::isGrenadeWar () {
|
||||
|
|
@ -1737,7 +1760,7 @@ void Bot::selectSecondary () {
|
|||
pev->weapons = oldWeapons;
|
||||
}
|
||||
|
||||
int Bot::bestWeaponCarried () {
|
||||
int Bot::getBestOwnedWeapon () {
|
||||
auto tab = conf.getRawWeapons ();
|
||||
|
||||
int weapons = pev->weapons;
|
||||
|
|
@ -1756,6 +1779,29 @@ int Bot::bestWeaponCarried () {
|
|||
return num;
|
||||
}
|
||||
|
||||
int Bot::getBestOwnedPistol () {
|
||||
auto tab = conf.getRawWeapons ();
|
||||
|
||||
int weapons = pev->weapons;
|
||||
int num = 0;
|
||||
int i = 0;
|
||||
|
||||
// loop through all the weapons until terminator is found...
|
||||
while (tab->id) {
|
||||
// is the bot carrying this weapon?
|
||||
if (weapons & cr::bit (tab->id)) {
|
||||
num = i;
|
||||
}
|
||||
++i;
|
||||
++tab;
|
||||
|
||||
if (i > kPrimaryWeaponMinIndex - 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
void Bot::decideFollowUser () {
|
||||
// this function forces bot to follow user
|
||||
static Array <edict_t *> users {};
|
||||
|
|
|
|||
|
|
@ -1198,6 +1198,28 @@ void Game::ensureHealthyGameEnvironment () {
|
|||
}
|
||||
}
|
||||
|
||||
edict_t *Game::createFakeClient (StringRef name) {
|
||||
auto ent = engfuncs.pfnCreateFakeClient (name.chars ());
|
||||
|
||||
if (game.isNullEntity (ent)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto netname = ent->v.netname;
|
||||
ent->v = {}; // reset entire the entvars structure (fix from regamedll)
|
||||
|
||||
// restore containing entity, name and client flags
|
||||
ent->v.pContainingEntity = ent;
|
||||
ent->v.flags = FL_FAKECLIENT | FL_CLIENT;
|
||||
ent->v.netname = netname;
|
||||
|
||||
if (ent->pvPrivateData != nullptr) {
|
||||
engfuncs.pfnFreeEntPrivateData (ent);
|
||||
}
|
||||
ent->pvPrivateData = nullptr;
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
void LightMeasure::initializeLightstyles () {
|
||||
// this function initializes lighting information...
|
||||
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ BotCreateResult BotManager::create (StringRef name, int difficulty, int personal
|
|||
// buffer has been modified, copy to real name
|
||||
resultName = cr::move (prefixed);
|
||||
}
|
||||
bot = engfuncs.pfnCreateFakeClient (resultName.chars ());
|
||||
bot = game.createFakeClient (resultName);
|
||||
|
||||
if (game.isNullEntity (bot)) {
|
||||
return BotCreateResult::MaxPlayersReached;
|
||||
|
|
@ -622,8 +622,15 @@ void BotManager::serverFill (int selection, int personality, int difficulty, int
|
|||
else {
|
||||
selection = 5;
|
||||
}
|
||||
const auto maxToAdd = maxClients - (getHumansCount () + getBotCount ());
|
||||
|
||||
constexpr char kTeams[6][12] = { "", {"Terrorists"}, {"CTs"}, "", "", {"Random"}, };
|
||||
const auto toAdd = numToAdd == -1 ? maxClients - (getHumansCount () + getBotCount ()) : numToAdd;
|
||||
auto toAdd = numToAdd == -1 ? maxToAdd : numToAdd;
|
||||
|
||||
// limit manually added count as well
|
||||
if (toAdd > maxToAdd - 1) {
|
||||
toAdd = maxToAdd - 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= toAdd; ++i) {
|
||||
addbot ("", difficulty, personality, selection, -1, true);
|
||||
|
|
@ -763,12 +770,23 @@ bool BotManager::kickRandom (bool decQuota, Team fromTeam) {
|
|||
|
||||
return true;
|
||||
}
|
||||
static Array <Bot *> kickable;
|
||||
kickable.clear ();
|
||||
|
||||
// worst case, just kick some random bot
|
||||
for (const auto &bot : m_bots) {
|
||||
|
||||
// is this slot used?
|
||||
if (belongsTeam (bot.get ())) {
|
||||
kickable.push (bot.get ());
|
||||
}
|
||||
}
|
||||
|
||||
// kick random from collected
|
||||
if (!kickable.empty ()) {
|
||||
auto bot = kickable.random ();
|
||||
|
||||
if (bot) {
|
||||
updateQuota ();
|
||||
bot->kick ();
|
||||
|
||||
|
|
@ -1083,13 +1101,6 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) {
|
|||
const int clientIndex = game.indexOfEntity (bot);
|
||||
pev = &bot->v;
|
||||
|
||||
if (bot->pvPrivateData != nullptr) {
|
||||
engfuncs.pfnFreeEntPrivateData (bot);
|
||||
}
|
||||
|
||||
bot->pvPrivateData = nullptr;
|
||||
bot->v.frags = 0;
|
||||
|
||||
// create the player entity by calling MOD's player function
|
||||
bots.execGameEntity (bot);
|
||||
|
||||
|
|
@ -1191,9 +1202,7 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) {
|
|||
m_baseFearLevel = rg (0.4f, 0.7f);
|
||||
break;
|
||||
}
|
||||
|
||||
plat.bzero (&m_ammoInClip, sizeof (m_ammoInClip));
|
||||
plat.bzero (&m_ammo, sizeof (m_ammo));
|
||||
clearAmmoInfo ();
|
||||
|
||||
m_currentWeapon = 0; // current weapon is not assigned at start
|
||||
m_weaponType = WeaponType::None; // current weapon type is not assigned at start
|
||||
|
|
@ -1227,6 +1236,12 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) {
|
|||
newRound ();
|
||||
}
|
||||
|
||||
|
||||
void Bot::clearAmmoInfo () {
|
||||
plat.bzero (&m_ammoInClip, sizeof (m_ammoInClip));
|
||||
plat.bzero (&m_ammo, sizeof (m_ammo));
|
||||
}
|
||||
|
||||
float Bot::getConnectionTime () {
|
||||
const auto current = plat.seconds ();
|
||||
|
||||
|
|
@ -1565,8 +1580,7 @@ void Bot::newRound () {
|
|||
|
||||
// if bot died, clear all weapon stuff and force buying again
|
||||
if (!m_isAlive) {
|
||||
plat.bzero (&m_ammoInClip, sizeof (m_ammoInClip));
|
||||
plat.bzero (&m_ammo, sizeof (m_ammo));
|
||||
clearAmmoInfo ();
|
||||
|
||||
m_currentWeapon = 0;
|
||||
m_weaponType = 0;
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ void BotSupport::updateClients () {
|
|||
edict_t *player = game.playerOfIndex (i);
|
||||
Client &client = m_clients[i];
|
||||
|
||||
if (!game.isNullEntity (player) && (player->v.flags & FL_CLIENT)) {
|
||||
if (!game.isNullEntity (player) && (player->v.flags & FL_CLIENT) && !(player->v.flags & FL_DORMANT)) {
|
||||
client.ent = player;
|
||||
client.flags |= ClientFlags::Used;
|
||||
|
||||
|
|
|
|||
|
|
@ -946,7 +946,7 @@ void Bot::defuseBomb_ () {
|
|||
|| timeToBlowUp < fullDefuseTime + 7.0f
|
||||
|| ((getAmmoInClip () > 8 && m_reloadState == Reload::Primary) || (getAmmoInClip () > 5 && m_reloadState == Reload::Secondary))) {
|
||||
|
||||
const int weaponIndex = bestWeaponCarried ();
|
||||
const int weaponIndex = getBestOwnedWeapon ();
|
||||
|
||||
// just select knife and then select weapon
|
||||
selectWeaponById (Weapon::Knife);
|
||||
|
|
@ -1530,7 +1530,7 @@ void Bot::pickupItem_ () {
|
|||
}
|
||||
else {
|
||||
// primary weapon
|
||||
int weaponIndex = bestWeaponCarried ();
|
||||
int weaponIndex = getBestOwnedWeapon ();
|
||||
|
||||
const bool niceWeapon = rateGroundWeapon (m_pickupItem);
|
||||
const auto tab = conf.getRawWeapons ();
|
||||
|
|
@ -1564,7 +1564,7 @@ void Bot::pickupItem_ () {
|
|||
// near to shield?
|
||||
else if (itemDistanceSq < cr::sqrf (50.0f)) {
|
||||
// get current best weapon to check if it's a primary in need to be dropped
|
||||
int weaponIndex = bestWeaponCarried ();
|
||||
int weaponIndex = getBestOwnedWeapon ();
|
||||
|
||||
if (weaponIndex > 6) {
|
||||
selectWeaponByIndex (weaponIndex);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue