Bots are now able to destroy random breakables around him, without touching them.
Added cvars descriptions and yapb.cfg generation. Finally fixed bomb-defuse problems. Fixed unaligned access in bot compression/decompression. Fixed low-fps aim code falure.
This commit is contained in:
parent
ef1faabfe6
commit
91c4d9ce1f
21 changed files with 523 additions and 293 deletions
|
|
@ -10,7 +10,7 @@
|
|||
#include <yapb.h>
|
||||
|
||||
// until hook code will be compatible with ARM, it's here
|
||||
#if defined (CR_ANDROID)
|
||||
#if defined (CR_ANDROID) && defined(CR_ARCH_ARM)
|
||||
void android_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev) {
|
||||
if (!addr) {
|
||||
addr = game.lib ().resolve <EntityFunction> (name);
|
||||
|
|
|
|||
|
|
@ -9,25 +9,26 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_debug ("yb_debug", "0");
|
||||
ConVar yb_debug_goal ("yb_debug_goal", "-1");
|
||||
ConVar yb_user_follow_percent ("yb_user_follow_percent", "20");
|
||||
ConVar yb_user_max_followers ("yb_user_max_followers", "1");
|
||||
ConVar yb_debug ("yb_debug", "0", "Enables or disables useful messages about bot states. Not required for end users", true, 0.0f, 4.0f);
|
||||
ConVar yb_debug_goal ("yb_debug_goal", "-1", "Forces all alive bots to build path and go to the specified here graph node.", true, -1.0f, kMaxNodes);
|
||||
ConVar yb_user_follow_percent ("yb_user_follow_percent", "20", "Specifies the percent of bots, than can follow leader on each round start.", true, 0.0f, 100.0f);
|
||||
ConVar yb_user_max_followers ("yb_user_max_followers", "1", "Specifies how many bots can follow a single user.", true, 0.0f, static_cast <float> (kGameMaxPlayers / 2));
|
||||
|
||||
ConVar yb_jasonmode ("yb_jasonmode", "0");
|
||||
ConVar yb_radio_mode ("yb_radio_mode", "2");
|
||||
ConVar yb_economics_rounds ("yb_economics_rounds", "1");
|
||||
ConVar yb_walking_allowed ("yb_walking_allowed", "1");
|
||||
ConVar yb_camping_allowed ("yb_camping_allowed", "1");
|
||||
ConVar yb_jasonmode ("yb_jasonmode", "0", "If enabled, all bots will be forced only the knife, skipping weapon buying routines.");
|
||||
ConVar yb_radio_mode ("yb_radio_mode", "2", "Allows bots to use radio or chattter.\nAllowed values: '0', '1', '2'.\nIf '0', radio and chatter is disabled.\nIf '1', only radio allowed.\nIf '2' chatter and radio allowed.", true, 0.0f, 2.0f);
|
||||
|
||||
ConVar yb_tkpunish ("yb_tkpunish", "1");
|
||||
ConVar yb_freeze_bots ("yb_freeze_bots", "0");
|
||||
ConVar yb_spraypaints ("yb_spraypaints", "1");
|
||||
ConVar yb_botbuy ("yb_botbuy", "1");
|
||||
ConVar yb_economics_rounds ("yb_economics_rounds", "1", "Specifies whether bots able to use team economics, like do not buy any weapons for whole team to keep money for better guns.");
|
||||
ConVar yb_walking_allowed ("yb_walking_allowed", "1", "Sepcifies whether bots able to use 'shift' if they thinks that enemy is near.");
|
||||
ConVar yb_camping_allowed ("yb_camping_allowed", "1", "Allows or disallows bots to camp. Doesn't affects bomb/hostage defending tasks");
|
||||
|
||||
ConVar yb_chatter_path ("yb_chatter_path", "sound/radio/bot");
|
||||
ConVar yb_restricted_weapons ("yb_restricted_weapons", "");
|
||||
ConVar yb_best_weapon_picker_type ("yb_best_weapon_picker_type", "0");
|
||||
ConVar yb_tkpunish ("yb_tkpunish", "1", "Allows or disallows bots to take revenge of teamkillers / team attacks.");
|
||||
ConVar yb_freeze_bots ("yb_freeze_bots", "0", "If enables bots think function is disabled, so bots will not move anywhere from their spawn spots.");
|
||||
ConVar yb_spraypaints ("yb_spraypaints", "1", "Allows or disallows the use of spay paints.");
|
||||
ConVar yb_botbuy ("yb_botbuy", "1", "Allows or disallows bots weapon buying routines.");
|
||||
ConVar yb_destroy_breakables_around ("yb_destroy_breakables_around", "1", "Allows bots to destroy breakables around him, even without touching with them.");
|
||||
|
||||
ConVar yb_chatter_path ("yb_chatter_path", "sound/radio/bot", "Specifies the paths for the bot chatter sound files.", false);
|
||||
ConVar yb_restricted_weapons ("yb_restricted_weapons", "", "Specifies semicolon separated list of weapons that are not allowed to buy / pickup.", false);
|
||||
|
||||
// game console variables
|
||||
ConVar mp_c4timer ("mp_c4timer", nullptr, Var::NoRegister);
|
||||
|
|
@ -334,7 +335,7 @@ void Bot::avoidGrenades () {
|
|||
if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == 4 && strcmp (model, "flashbang.mdl") == 0) {
|
||||
// don't look at flash bang
|
||||
if (!(m_states & Sense::SeeingEnemy)) {
|
||||
pev->v_angle.y = cr::normalizeAngles ((game.getAbsPos (pent) - getEyesPos ()).angles ().y + 180.0f);
|
||||
pev->v_angle.y = cr::normalizeAngles ((game.getEntityWorldOrigin (pent) - getEyesPos ()).angles ().y + 180.0f);
|
||||
|
||||
m_canChooseAimDirection = false;
|
||||
m_preventFlashing = game.time () + rg.float_ (1.0f, 2.0f);
|
||||
|
|
@ -385,8 +386,7 @@ void Bot::avoidGrenades () {
|
|||
}
|
||||
|
||||
void Bot::checkBreakable (edict_t *touch) {
|
||||
|
||||
if (!isShootableBreakable (touch)) {
|
||||
if (!game.isShootableBreakable (touch)) {
|
||||
return;
|
||||
}
|
||||
m_breakableEntity = lookupBreakable ();
|
||||
|
|
@ -395,10 +395,36 @@ void Bot::checkBreakable (edict_t *touch) {
|
|||
return;
|
||||
}
|
||||
m_campButtons = pev->button & IN_DUCK;
|
||||
|
||||
startTask (Task::ShootBreakable, TaskPri::ShootBreakable, kInvalidNodeIndex, 0.0f, false);
|
||||
}
|
||||
|
||||
void Bot::checkBreablesAround () {
|
||||
if (!yb_destroy_breakables_around.bool_ () || m_currentWeapon == Weapon::Knife || rg.chance (25) || !game.hasBreakables () || m_seeEnemyTime + 4.0f > game.time () || !game.isNullEntity (m_enemy) || !hasPrimaryWeapon ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we're have some breakbles in 450 units range
|
||||
for (const auto &breakable : game.getBreakables ()) {
|
||||
if (!game.isShootableBreakable (breakable)) {
|
||||
continue;
|
||||
}
|
||||
const auto &origin = game.getEntityWorldOrigin (breakable);
|
||||
|
||||
if ((origin - pev->origin).lengthSq () > cr::square (450.0f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isInFOV (origin - getEyesPos ()) < pev->fov && seesEntity (origin)) {
|
||||
m_breakableOrigin = origin;
|
||||
m_breakableEntity = breakable;
|
||||
m_campButtons = pev->button & IN_DUCK;
|
||||
|
||||
startTask (Task::ShootBreakable, TaskPri::ShootBreakable, kInvalidNodeIndex, 0.0f, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
edict_t *Bot::lookupBreakable () {
|
||||
// this function checks if bot is blocked by a shoot able breakable in his moving direction
|
||||
|
||||
|
|
@ -406,26 +432,26 @@ edict_t *Bot::lookupBreakable () {
|
|||
game.testLine (pev->origin, pev->origin + (m_destOrigin - pev->origin).normalize () * 72.0f, TraceIgnore::None, ent (), &tr);
|
||||
|
||||
if (tr.flFraction != 1.0f) {
|
||||
edict_t *ent = tr.pHit;
|
||||
auto ent = tr.pHit;
|
||||
|
||||
// check if this isn't a triggered (bomb) breakable and if it takes damage. if true, shoot the crap!
|
||||
if (isShootableBreakable (ent)) {
|
||||
m_breakableOrigin = game.getAbsPos (ent);
|
||||
if (game.isShootableBreakable (ent)) {
|
||||
m_breakableOrigin = game.getEntityWorldOrigin (ent);
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
game.testLine (getEyesPos (), getEyesPos () + (m_destOrigin - getEyesPos ()).normalize () * 72.0f, TraceIgnore::None, ent (), &tr);
|
||||
|
||||
if (tr.flFraction != 1.0f) {
|
||||
edict_t *ent = tr.pHit;
|
||||
auto ent = tr.pHit;
|
||||
|
||||
if (isShootableBreakable (ent)) {
|
||||
m_breakableOrigin = game.getAbsPos (ent);
|
||||
if (game.isShootableBreakable (ent)) {
|
||||
m_breakableOrigin = game.getEntityWorldOrigin (ent);
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
m_breakableEntity = nullptr;
|
||||
m_breakableOrigin= nullptr;
|
||||
m_breakableOrigin = nullptr;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -459,14 +485,14 @@ void Bot::updatePickups () {
|
|||
}
|
||||
|
||||
auto &intresting = bots.searchIntrestingEntities ();
|
||||
const float radius = cr::square (500.0f);
|
||||
const float radius = cr::square (320.0f);
|
||||
|
||||
if (!game.isNullEntity (m_pickupItem)) {
|
||||
bool itemExists = false;
|
||||
auto pickupItem = m_pickupItem;
|
||||
|
||||
for (auto &ent : intresting) {
|
||||
const Vector &origin = game.getAbsPos (ent);
|
||||
const Vector &origin = game.getEntityWorldOrigin (ent);
|
||||
|
||||
// too far from us ?
|
||||
if ((pev->origin - origin).lengthSq () > radius) {
|
||||
|
|
@ -505,7 +531,7 @@ void Bot::updatePickups () {
|
|||
if (ent == m_itemIgnore) {
|
||||
continue; // someone owns this weapon or it hasn't respawned yet
|
||||
}
|
||||
const Vector &origin = game.getAbsPos (ent);
|
||||
const Vector &origin = game.getEntityWorldOrigin (ent);
|
||||
|
||||
// too far from us ?
|
||||
if ((pev->origin - origin).lengthSq () > radius) {
|
||||
|
|
@ -714,7 +740,7 @@ void Bot::updatePickups () {
|
|||
|
||||
allowPickup = !isBombDefusing (origin) || m_hasProgressBar;
|
||||
pickupType = Pickup::PlantedC4;
|
||||
|
||||
|
||||
if (!m_defendedBomb && !allowPickup) {
|
||||
m_defendedBomb = true;
|
||||
|
||||
|
|
@ -1784,12 +1810,12 @@ void Bot::setConditions () {
|
|||
// check if our current enemy is still valid
|
||||
if (!game.isNullEntity (m_lastEnemy)) {
|
||||
if (!util.isAlive (m_lastEnemy) && m_shootAtDeadTime < game.time ()) {
|
||||
m_lastEnemyOrigin= nullptr;
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
m_lastEnemy = nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_lastEnemyOrigin= nullptr;
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
m_lastEnemy = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -1857,7 +1883,7 @@ void Bot::filterTasks () {
|
|||
filter[Task::PickupItem].desire = 50.0f; // always pickup button
|
||||
}
|
||||
else {
|
||||
float distance = (500.0f - (game.getAbsPos (m_pickupItem) - pev->origin).length ()) * 0.2f;
|
||||
float distance = (500.0f - (game.getEntityWorldOrigin (m_pickupItem) - pev->origin).length ()) * 0.2f;
|
||||
|
||||
if (distance > 50.0f) {
|
||||
distance = 50.0f;
|
||||
|
|
@ -2388,7 +2414,7 @@ void Bot::checkRadioQueue () {
|
|||
|
||||
clearSearchNodes ();
|
||||
|
||||
m_position = graph.getBombPos ();
|
||||
m_position = graph.getBombOrigin ();
|
||||
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, kInvalidNodeIndex, 0.0f, true);
|
||||
|
||||
pushRadioMessage (Radio::RogerThat);
|
||||
|
|
@ -2837,7 +2863,7 @@ void Bot::frame () {
|
|||
m_numEnemiesLeft = numEnemiesNear (pev->origin, kInfiniteDistance);
|
||||
|
||||
if (bots.isBombPlanted () && m_team == Team::CT && m_notKilled) {
|
||||
const Vector &bombPosition = graph.getBombPos ();
|
||||
const Vector &bombPosition = graph.getBombOrigin ();
|
||||
|
||||
if (!m_hasProgressBar && getCurrentTaskId () != Task::EscapeFromBomb && (pev->origin - bombPosition).lengthSq () < cr::square (1540.0f) && !isBombDefusing (bombPosition)) {
|
||||
m_itemIgnore = nullptr;
|
||||
|
|
@ -2846,8 +2872,10 @@ void Bot::frame () {
|
|||
clearTask (getCurrentTaskId ());
|
||||
}
|
||||
}
|
||||
|
||||
checkSpawnConditions ();
|
||||
checkForChat ();
|
||||
checkBreablesAround ();
|
||||
|
||||
if (game.is (GameFlags::HasBotVoice)) {
|
||||
showChaterIcon (false); // end voice feedback
|
||||
|
|
@ -2867,7 +2895,7 @@ void Bot::update () {
|
|||
|
||||
m_moveSpeed = 0.0f;
|
||||
m_strafeSpeed = 0.0f;
|
||||
m_moveAngles= nullptr;
|
||||
m_moveAngles = nullptr;
|
||||
|
||||
m_canChooseAimDirection = true;
|
||||
m_notKilled = util.isAlive (ent ());
|
||||
|
|
@ -3234,7 +3262,7 @@ void Bot::huntEnemy_ () {
|
|||
completeTask ();
|
||||
|
||||
m_prevGoalIndex = kInvalidNodeIndex;
|
||||
m_lastEnemyOrigin= nullptr;
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
}
|
||||
|
||||
// do we need to calculate a new path?
|
||||
|
|
@ -3455,7 +3483,7 @@ void Bot::camp_ () {
|
|||
m_checkTerrain = false;
|
||||
m_moveToGoal = false;
|
||||
|
||||
if (m_team == Team::CT && bots.isBombPlanted () && m_defendedBomb && !isBombDefusing (graph.getBombPos ()) && !isOutOfBombTimer ()) {
|
||||
if (m_team == Team::CT && bots.isBombPlanted () && m_defendedBomb && !isBombDefusing (graph.getBombOrigin ()) && !isOutOfBombTimer ()) {
|
||||
m_defendedBomb = false;
|
||||
completeTask ();
|
||||
}
|
||||
|
|
@ -3625,7 +3653,7 @@ void Bot::moveToPos_ () {
|
|||
completeTask (); // we're done
|
||||
|
||||
m_prevGoalIndex = kInvalidNodeIndex;
|
||||
m_position= nullptr;
|
||||
m_position = nullptr;
|
||||
}
|
||||
|
||||
// didn't choose goal waypoint yet?
|
||||
|
|
@ -3720,17 +3748,12 @@ void Bot::bombDefuse_ () {
|
|||
}
|
||||
|
||||
bool pickupExists = !game.isNullEntity (m_pickupItem);
|
||||
const Vector &bombPos = pickupExists ? m_pickupItem->v.origin : graph.getBombPos ();
|
||||
const Vector &bombPos = pickupExists ? game.getEntityWorldOrigin (m_pickupItem) : graph.getBombOrigin ();
|
||||
|
||||
if (pickupExists) {
|
||||
if (graph.getBombPos () != bombPos) {
|
||||
graph.setBombPos (bombPos);
|
||||
}
|
||||
}
|
||||
bool defuseError = false;
|
||||
|
||||
// exception: bomb has been defused
|
||||
if (bombPos.empty ()) {
|
||||
if (bombPos.empty () || game.isNullEntity (m_pickupItem)) {
|
||||
defuseError = true;
|
||||
|
||||
if (m_numFriendsLeft != 0 && rg.chance (50)) {
|
||||
|
|
@ -3771,8 +3794,8 @@ void Bot::bombDefuse_ () {
|
|||
m_checkTerrain = true;
|
||||
m_moveToGoal = true;
|
||||
|
||||
m_destOrigin= nullptr;
|
||||
m_entity= nullptr;
|
||||
m_destOrigin = nullptr;
|
||||
m_entity = nullptr;
|
||||
|
||||
m_pickupItem = nullptr;
|
||||
m_pickupType = Pickup::None;
|
||||
|
|
@ -4260,10 +4283,10 @@ void Bot::escapeFromBomb_ () {
|
|||
clearSearchNodes ();
|
||||
|
||||
int lastSelectedGoal = kInvalidNodeIndex, minPathDistance = kInfiniteDistanceLong;
|
||||
float safeRadius = rg.float_ (1248.0f, 2048.0f);
|
||||
float safeRadius = rg.float_ (1513.0f, 2048.0f);
|
||||
|
||||
for (int i = 0; i < graph.length (); ++i) {
|
||||
if ((graph[i].origin - graph.getBombPos ()).length () < safeRadius || isOccupiedNode (i)) {
|
||||
if ((graph[i].origin - graph.getBombOrigin ()).length () < safeRadius || isOccupiedNode (i)) {
|
||||
continue;
|
||||
}
|
||||
int pathDistance = graph.getPathDist (m_currentNodeIndex, i);
|
||||
|
|
@ -4296,8 +4319,8 @@ void Bot::escapeFromBomb_ () {
|
|||
void Bot::shootBreakable_ () {
|
||||
m_aimFlags |= AimFlags::Override;
|
||||
|
||||
// Breakable destroyed?
|
||||
if (game.isNullEntity (lookupBreakable ())) {
|
||||
// breakable destroyed?
|
||||
if (!game.isShootableBreakable (m_breakableEntity)) {
|
||||
completeTask ();
|
||||
return;
|
||||
}
|
||||
|
|
@ -4306,12 +4329,10 @@ void Bot::shootBreakable_ () {
|
|||
m_checkTerrain = false;
|
||||
m_moveToGoal = false;
|
||||
m_navTimeset = game.time ();
|
||||
|
||||
const Vector &src = m_breakableOrigin;
|
||||
m_camp = src;
|
||||
m_camp = m_breakableOrigin;
|
||||
|
||||
// is bot facing the breakable?
|
||||
if (util.getShootingCone (ent (), src) >= 0.90f) {
|
||||
if (util.getShootingCone (ent (), m_breakableOrigin) >= 0.90f) {
|
||||
m_moveSpeed = 0.0f;
|
||||
m_strafeSpeed = 0.0f;
|
||||
|
||||
|
|
@ -4324,6 +4345,8 @@ void Bot::shootBreakable_ () {
|
|||
else {
|
||||
m_checkTerrain = true;
|
||||
m_moveToGoal = true;
|
||||
|
||||
completeTask ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4334,7 +4357,7 @@ void Bot::pickupItem_ () {
|
|||
|
||||
return;
|
||||
}
|
||||
const Vector &dest = game.getAbsPos (m_pickupItem);
|
||||
const Vector &dest = game.getEntityWorldOrigin (m_pickupItem);
|
||||
|
||||
m_destOrigin = dest;
|
||||
m_entity = dest;
|
||||
|
|
@ -5295,7 +5318,7 @@ void Bot::resetDoubleJump () {
|
|||
|
||||
m_doubleJumpEntity = nullptr;
|
||||
m_duckForJump = 0.0f;
|
||||
m_doubleJumpOrigin= nullptr;
|
||||
m_doubleJumpOrigin = nullptr;
|
||||
m_travelStartIndex = kInvalidNodeIndex;
|
||||
m_jumpReady = false;
|
||||
}
|
||||
|
|
@ -5459,9 +5482,9 @@ Vector Bot::isBombAudible () {
|
|||
}
|
||||
|
||||
if (m_difficulty > 2) {
|
||||
return graph.getBombPos ();
|
||||
return graph.getBombOrigin ();
|
||||
}
|
||||
const Vector &bombOrigin = graph.getBombPos ();
|
||||
const Vector &bombOrigin = graph.getBombOrigin ();
|
||||
|
||||
float timeElapsed = ((game.time () - bots.getTimeBombPlanted ()) / mp_c4timer.float_ ()) * 100.0f;
|
||||
float desiredRadius = 768.0f;
|
||||
|
|
@ -5592,7 +5615,7 @@ bool Bot::isOutOfBombTimer () {
|
|||
if (timeLeft > 13.0f) {
|
||||
return false;
|
||||
}
|
||||
const Vector &bombOrigin = graph.getBombPos ();
|
||||
const Vector &bombOrigin = graph.getBombOrigin ();
|
||||
|
||||
// for terrorist, if timer is lower than 13 seconds, return true
|
||||
if (timeLeft < 13.0f && m_team == Team::Terrorist && (bombOrigin - pev->origin).lengthSq () < cr::square (964.0f)) {
|
||||
|
|
@ -5634,16 +5657,16 @@ void Bot::updateHearing () {
|
|||
for (int i = 0; i < game.maxClients (); ++i) {
|
||||
const Client &client = util.getClient (i);
|
||||
|
||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent () || client.team == m_team || client.timeSoundLasting < game.time ()) {
|
||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent () || client.team == m_team || client.noise.last < game.time ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game.checkVisibility (client.ent, set)) {
|
||||
continue;
|
||||
}
|
||||
float distance = (client.sound - pev->origin).length ();
|
||||
float distance = (client.noise.pos - pev->origin).length ();
|
||||
|
||||
if (distance > client.hearingDistance) {
|
||||
if (distance > client.noise.dist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -5727,17 +5750,6 @@ void Bot::updateHearing () {
|
|||
}
|
||||
}
|
||||
|
||||
bool Bot::isShootableBreakable (edict_t *ent) {
|
||||
// this function is checking that pointed by ent pointer obstacle, can be destroyed.
|
||||
|
||||
auto classname = STRING (ent->v.classname);
|
||||
|
||||
if (strcmp (classname, "func_breakable") == 0 || (strcmp (classname, "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE))) {
|
||||
return ent->v.takedamage != DAMAGE_NO && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY) && ent->v.health < 500.0f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::enteredBuyZone (int buyState) {
|
||||
// this function is gets called when bot enters a buyzone, to allow bot to buy some stuff
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_chat ("yb_chat", "1");
|
||||
ConVar yb_chat ("yb_chat", "1", "Enables or disables bots chat functionality.");
|
||||
|
||||
void BotUtils::stripTags (String &line) {
|
||||
if (line.empty ()) {
|
||||
|
|
@ -23,9 +23,9 @@ void BotUtils::stripTags (String &line) {
|
|||
const size_t end = line.find (tag.second, start);
|
||||
const size_t diff = end - start;
|
||||
|
||||
if (end != String::InvalidIndex && end > start && diff < 32 && diff > 2) {
|
||||
if (end != String::InvalidIndex && end > start && diff < 32 && diff > 1) {
|
||||
line.erase (start, diff + tag.second.length ());
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_shoots_thru_walls ("yb_shoots_thru_walls", "2");
|
||||
ConVar yb_ignore_enemies ("yb_ignore_enemies", "0");
|
||||
ConVar yb_check_enemy_rendering ("yb_check_enemy_rendering", "0");
|
||||
ConVar yb_shoots_thru_walls ("yb_shoots_thru_walls", "2", "Specifies whether bots able to fire at enemies behind the wall, if they hearing or suspecting them.", true, 0.0f, 3.0f);
|
||||
ConVar yb_ignore_enemies ("yb_ignore_enemies", "0", "Enables or disables searching world for enemies.");
|
||||
ConVar yb_check_enemy_rendering ("yb_check_enemy_rendering", "0", "Enables or disables checking enemy rendering flags. Useful for some mods.");
|
||||
|
||||
ConVar mp_friendlyfire ("mp_friendlyfire", nullptr, Var::NoRegister);
|
||||
|
||||
|
|
@ -218,7 +218,7 @@ bool Bot::lookupEnemies () {
|
|||
m_aimFlags |= AimFlags::LastEnemy;
|
||||
}
|
||||
m_enemyParts = Visibility::None;
|
||||
m_enemyOrigin= nullptr;
|
||||
m_enemyOrigin = nullptr;
|
||||
|
||||
if (!game.isNullEntity (m_enemy)) {
|
||||
player = m_enemy;
|
||||
|
|
@ -447,7 +447,7 @@ const Vector &Bot::getEnemyBodyOffset () {
|
|||
Vector aimPos = m_enemy->v.origin;
|
||||
|
||||
if (m_difficulty > 2) {
|
||||
aimPos += (m_enemy->v.velocity - pev->velocity) * (getFrameInterval () * 1.75f);
|
||||
aimPos += (m_enemy->v.velocity - pev->velocity) * (getFrameInterval () * 1.25f);
|
||||
}
|
||||
|
||||
// if we only suspect an enemy behind a wall take the worst skill
|
||||
|
|
@ -455,7 +455,7 @@ const Vector &Bot::getEnemyBodyOffset () {
|
|||
aimPos += getBodyOffsetError (distance);
|
||||
}
|
||||
else {
|
||||
bool useBody = !usesPistol () && distance > kSprayDistance && distance < 2048.0f;
|
||||
bool useBody = !usesPistol () && distance >= kSprayDistance && distance < 3072.0f;
|
||||
|
||||
// now take in account different parts of enemy body
|
||||
if (m_enemyParts & (Visibility::Head | Visibility::Body)) {
|
||||
|
|
@ -506,10 +506,7 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) {
|
|||
|
||||
float result = -2.0f;
|
||||
|
||||
if (distance < kSprayDistance) {
|
||||
return -16.0f;
|
||||
}
|
||||
else if (distance >= kDoubleSprayDistance) {
|
||||
if (distance >= kDoubleSprayDistance) {
|
||||
if (sniper) {
|
||||
result = 0.18f;
|
||||
}
|
||||
|
|
@ -523,7 +520,7 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) {
|
|||
result = 1.5f;
|
||||
}
|
||||
else if (rifle) {
|
||||
result = 1.0f;
|
||||
result = -1.0f;
|
||||
}
|
||||
else if (m249) {
|
||||
result = -5.5f;
|
||||
|
|
@ -844,7 +841,9 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
pev->button |= IN_ATTACK; // use primary attack
|
||||
}
|
||||
else {
|
||||
pev->button |= IN_ATTACK;
|
||||
if ((m_oldButtons & IN_ATTACK) == 0) {
|
||||
pev->button |= IN_ATTACK;
|
||||
}
|
||||
|
||||
const float minDelay[] = { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.6f };
|
||||
const float maxDelay[] = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.7f };
|
||||
|
|
@ -916,6 +915,7 @@ void Bot::fireWeapons () {
|
|||
const auto &prop = conf.getWeaponProp (id);
|
||||
|
||||
if (prop.ammo1 != -1 && prop.ammo1 < 32 && m_ammo[prop.ammo1] >= tab[selectIndex].minPrimaryAmmo) {
|
||||
|
||||
// available ammo found, reload weapon
|
||||
if (m_reloadState == Reload::None || m_reloadCheckTime > game.time ()) {
|
||||
m_isReloading = true;
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_display_menu_text ("yb_display_menu_text", "1");
|
||||
ConVar yb_password ("yb_password", "", Var::Password);
|
||||
ConVar yb_password_key ("yb_password_key", "_ybpw");
|
||||
ConVar yb_display_menu_text ("yb_display_menu_text", "1", "Enables or disables display menu text, when players asks for menu. Useful only for Android.");
|
||||
ConVar yb_password ("yb_password", "", "The value (password) for the setinfo key, if user set's correct password, he's gains access to bot commands and menus.", false, 0.0f, 0.0f, Var::Password);
|
||||
ConVar yb_password_key ("yb_password_key", "_ybpw", "The name of setinfo key used to store password to bot commands and menus", false);
|
||||
|
||||
int BotControl::cmdAddBot () {
|
||||
enum args { alias = 1, difficulty, personality, team, model, name, max };
|
||||
|
|
@ -161,9 +161,25 @@ int BotControl::cmdWeaponMode () {
|
|||
}
|
||||
|
||||
int BotControl::cmdVersion () {
|
||||
auto hash = String (PRODUCT_GIT_HASH).substr (0, 8);
|
||||
auto author = String (PRODUCT_GIT_COMMIT_AUTHOR);
|
||||
|
||||
// if no hash specified, set local one
|
||||
if (hash.startsWith ("unspe")) {
|
||||
hash = "local";
|
||||
}
|
||||
|
||||
// if no commit author, set local one
|
||||
if (author.startsWith ("unspe")) {
|
||||
author = PRODUCT_EMAIL;
|
||||
}
|
||||
|
||||
msg ("%s v%s (build %u)", PRODUCT_NAME, PRODUCT_VERSION, util.buildNumber ());
|
||||
msg (" compiled: %s %s by %s", __DATE__, __TIME__, PRODUCT_GIT_COMMIT_AUTHOR);
|
||||
msg (" commit: %scommit/%s", PRODUCT_COMMENTS, PRODUCT_GIT_HASH);
|
||||
msg (" compiled: %s %s by %s", __DATE__, __TIME__, author.chars ());
|
||||
|
||||
if (!hash.startsWith ("local")) {
|
||||
msg (" commit: %scommit/%s", PRODUCT_COMMENTS, hash.chars ());
|
||||
}
|
||||
msg (" url: %s", PRODUCT_URL);
|
||||
|
||||
return BotCommandResult::Handled;
|
||||
|
|
@ -207,6 +223,84 @@ int BotControl::cmdList () {
|
|||
return BotCommandResult::Handled;
|
||||
}
|
||||
|
||||
int BotControl::cmdCvars () {
|
||||
enum args { alias = 1, pattern, max };
|
||||
|
||||
// adding more args to args array, if not enough passed
|
||||
fixMissingArgs (max);
|
||||
|
||||
const auto &match = getStr (pattern);
|
||||
const bool isSave = match == "save";
|
||||
|
||||
File cfg;
|
||||
|
||||
// if save requested, dump cvars to yapb.cfg
|
||||
if (isSave) {
|
||||
cfg.open (strings.format ("%s/addons/yapb/conf/yapb.cfg", game.getModName ()), "wt");
|
||||
|
||||
cfg.puts ("// Configuration file for %s\n\n", PRODUCT_SHORT_NAME);
|
||||
}
|
||||
|
||||
for (const auto &cvar : game.getCvars ()) {
|
||||
if (cvar.info.empty ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isSave && match != "empty" && !strstr (cvar.reg.name, match.chars ())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// float value ?
|
||||
bool isFloat = !strings.isEmpty (cvar.self->str ()) && strstr (cvar.self->str (), ".");
|
||||
|
||||
if (isSave) {
|
||||
cfg.puts ("//\n");
|
||||
cfg.puts ("// %s\n", String::join (cvar.info.split ("\n"), "\n// ").chars ());
|
||||
cfg.puts ("// ---\n");
|
||||
|
||||
if (cvar.bounded) {
|
||||
if (isFloat) {
|
||||
cfg.puts ("// Default: \"%.1f\", Min: \"%.1f\", Max: \"%.1f\"\n", cvar.initial, cvar.min, cvar.max);
|
||||
}
|
||||
else {
|
||||
cfg.puts ("// Default: \"%i\", Min: \"%i\", Max: \"%i\"\n", static_cast <int> (cvar.initial), static_cast <int> (cvar.min), static_cast <int> (cvar.max));
|
||||
}
|
||||
}
|
||||
else {
|
||||
cfg.puts ("// Default: \"%s\"\n", cvar.self->str ());
|
||||
}
|
||||
cfg.puts ("// \n");
|
||||
|
||||
if (cvar.bounded) {
|
||||
if (isFloat) {
|
||||
cfg.puts ("%s \"%.1f\"\n", cvar.reg.name, cvar.self->float_ ());
|
||||
}
|
||||
else {
|
||||
cfg.puts ("%s \"%i\"\n", cvar.reg.name, cvar.self->int_ ());
|
||||
}
|
||||
}
|
||||
else {
|
||||
cfg.puts ("%s \"%s\"\n", cvar.reg.name, cvar.self->str ());
|
||||
}
|
||||
cfg.puts ("\n");
|
||||
}
|
||||
else {
|
||||
game.print ("cvar: %s", cvar.reg.name);
|
||||
game.print ("info: %s", cvar.info.chars ());
|
||||
|
||||
game.print (" ");
|
||||
}
|
||||
}
|
||||
|
||||
if (isSave) {
|
||||
ctrl.msg ("Bots cvars has been written to file.");
|
||||
|
||||
|
||||
cfg.close ();
|
||||
}
|
||||
return BotCommandResult::Handled;
|
||||
}
|
||||
|
||||
int BotControl::cmdNode () {
|
||||
enum args { root, alias, cmd, cmd2, max };
|
||||
|
||||
|
|
@ -1698,7 +1792,8 @@ void BotControl::kickBotByMenu (int page) {
|
|||
for (int i = menuKey; i < page * 8; ++i) {
|
||||
auto bot = bots[i];
|
||||
|
||||
if (bot != nullptr) {
|
||||
// check for fakeclient bit, since we're clear it upon kick, but actual bot struct destroyed after client disconnected
|
||||
if (bot != nullptr && (bot->pev->flags & FL_FAKECLIENT)) {
|
||||
menuKeys |= cr::bit (cr::abs (i - menuKey));
|
||||
menus.appendf ("%1.1d. %s%s\n", i - menuKey + 1, STRING (bot->pev->netname), bot->m_team == Team::CT ? " \\y(CT)\\w" : " \\r(T)\\w");
|
||||
}
|
||||
|
|
@ -1795,6 +1890,7 @@ BotControl::BotControl () {
|
|||
m_cmds.emplace ("graphmenu/wpmenu/wptmenu", "graphmenu [noarguments]", "Opens and displays bots graph edtior.", &BotControl::cmdNodeMenu);
|
||||
m_cmds.emplace ("list/listbots", "list [noarguments]", "Lists the bots currently playing on server.", &BotControl::cmdList);
|
||||
m_cmds.emplace ("graph/g/wp/wpt/waypoint", "graph [help]", "Handles graph operations.", &BotControl::cmdNode);
|
||||
m_cmds.emplace ("cvars", "cvars [save|cvar]", "Display all the cvars with their descriptions.", &BotControl::cmdCvars);
|
||||
|
||||
// declare the menus
|
||||
createMenus ();
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
|||
|
||||
m_spawnCount[Team::CT] = 0;
|
||||
m_spawnCount[Team::Terrorist] = 0;
|
||||
|
||||
// clear all breakables before initialization
|
||||
m_breakables.clear ();
|
||||
|
||||
// go thru the all entities on map, and do whatever we're want
|
||||
for (int i = 0; i < max; ++i) {
|
||||
|
|
@ -132,6 +135,9 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
|||
else if (strncmp (classname, "func_button", 11) == 0) {
|
||||
m_mapFlags |= MapFlags::HasButtons;
|
||||
}
|
||||
else if (isShootableBreakable (ent)) {
|
||||
m_breakables.push (ent);
|
||||
}
|
||||
}
|
||||
|
||||
// next maps doesn't have map-specific entities, so determine it by name
|
||||
|
|
@ -301,7 +307,7 @@ const char *Game::getMapName () {
|
|||
return strings.format ("%s", STRING (globals->mapname));
|
||||
}
|
||||
|
||||
Vector Game::getAbsPos (edict_t *ent) {
|
||||
Vector Game::getEntityWorldOrigin (edict_t *ent) {
|
||||
// this expanded function returns the vector origin of a bounded entity, assuming that any
|
||||
// entity that has a bounding box has its center at the center of the bounding box itself.
|
||||
|
||||
|
|
@ -463,35 +469,61 @@ bool Game::isSoftwareRenderer () {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Game::addNewCvar (const char *variable, const char *value, Var varType, bool regMissing, const char *regVal, ConVar *self) {
|
||||
void Game::addNewCvar (const char *name, const char *value, const char *info, bool bounded, float min, float max, Var varType, bool missingAction, const char *regval, ConVar *self) {
|
||||
// this function adds globally defined variable to registration stack
|
||||
|
||||
VarPair pair {};
|
||||
VarPair pair;
|
||||
|
||||
pair.reg.name = const_cast <char *> (variable);
|
||||
pair.reg.name = const_cast <char *> (name);
|
||||
pair.reg.string = const_cast <char *> (value);
|
||||
pair.missing = regMissing;
|
||||
pair.regval = regVal;
|
||||
pair.missing = missingAction;
|
||||
pair.regval = regval;
|
||||
pair.info = info;
|
||||
pair.bounded = bounded;
|
||||
|
||||
int engineFlags = FCVAR_EXTDLL;
|
||||
if (bounded) {
|
||||
pair.min = min;
|
||||
pair.max = max;
|
||||
pair.initial = static_cast <float> (atof (value));
|
||||
}
|
||||
|
||||
auto eflags = FCVAR_EXTDLL;
|
||||
|
||||
if (varType == Var::Normal) {
|
||||
engineFlags |= FCVAR_SERVER;
|
||||
eflags |= FCVAR_SERVER;
|
||||
}
|
||||
else if (varType == Var::ReadOnly) {
|
||||
engineFlags |= FCVAR_SERVER | FCVAR_SPONLY | FCVAR_PRINTABLEONLY;
|
||||
eflags |= FCVAR_SERVER | FCVAR_SPONLY | FCVAR_PRINTABLEONLY;
|
||||
}
|
||||
else if (varType == Var::Password) {
|
||||
engineFlags |= FCVAR_PROTECTED;
|
||||
eflags |= FCVAR_PROTECTED;
|
||||
}
|
||||
|
||||
pair.reg.flags = engineFlags;
|
||||
pair.reg.flags = eflags;
|
||||
pair.self = self;
|
||||
pair.type = varType;
|
||||
|
||||
m_cvars.push (cr::move (pair));
|
||||
}
|
||||
|
||||
void Game::checkCvarsBounds () {
|
||||
for (const auto &var : m_cvars) {
|
||||
if (!var.bounded || !var.self) {
|
||||
continue;
|
||||
}
|
||||
auto value = var.self->float_ ();
|
||||
auto str = var.self->str ();
|
||||
|
||||
// check the bounds and set default if out of bounds
|
||||
if (value > var.max || value < var.min || (!strings.isEmpty (str) && isalpha (*str))) {
|
||||
var.self->set (var.initial);
|
||||
|
||||
// notify about that
|
||||
ctrl.msg ("Bogus value for cvar '%s', min is '%.1f' and max is '%.1f', and we're got '%s', value reverted to default '%.1f'.", var.reg.name, var.min, var.max, str, var.initial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::registerCvars (bool gameVars) {
|
||||
// this function pushes all added global variables to engine registration
|
||||
|
||||
|
|
@ -500,9 +532,9 @@ void Game::registerCvars (bool gameVars) {
|
|||
cvar_t ® = var.reg;
|
||||
|
||||
if (var.type != Var::NoRegister) {
|
||||
self.eptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
self.ptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
|
||||
if (!self.eptr) {
|
||||
if (!self.ptr) {
|
||||
static cvar_t reg_;
|
||||
|
||||
// fix metamod' memlocs not found
|
||||
|
|
@ -513,22 +545,22 @@ void Game::registerCvars (bool gameVars) {
|
|||
else {
|
||||
engfuncs.pfnCVarRegister (&var.reg);
|
||||
}
|
||||
self.eptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
self.ptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
}
|
||||
}
|
||||
else if (gameVars) {
|
||||
self.eptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
self.ptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
|
||||
if (var.missing && !self.eptr) {
|
||||
if (var.missing && !self.ptr) {
|
||||
if (reg.string == nullptr && var.regval != nullptr) {
|
||||
reg.string = const_cast <char *> (var.regval);
|
||||
reg.flags |= FCVAR_SERVER;
|
||||
}
|
||||
engfuncs.pfnCVarRegister (&var.reg);
|
||||
self.eptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
self.ptr = engfuncs.pfnCVarGetPointer (reg.name);
|
||||
}
|
||||
|
||||
if (!self.eptr) {
|
||||
if (!self.ptr) {
|
||||
print ("Got nullptr on cvar %s!", reg.name);
|
||||
}
|
||||
}
|
||||
|
|
@ -644,7 +676,6 @@ bool Game::postload () {
|
|||
// register bot cvars
|
||||
game.registerCvars ();
|
||||
|
||||
|
||||
// register server command(s)
|
||||
registerEngineCommand ("yapb", [] () {
|
||||
ctrl.handleEngineCommands ();
|
||||
|
|
@ -718,7 +749,6 @@ bool Game::postload () {
|
|||
logger.fatal ("Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gamedll, getModName ());
|
||||
}
|
||||
displayCSVersion ();
|
||||
|
||||
}
|
||||
else {
|
||||
bool binaryLoaded = loadCSBinary ();
|
||||
|
|
@ -782,6 +812,9 @@ void Game::slowFrame () {
|
|||
// detect csdm
|
||||
detectDeathmatch ();
|
||||
|
||||
// check the cvar bounds
|
||||
checkCvarsBounds ();
|
||||
|
||||
// display welcome message
|
||||
util.checkWelcome ();
|
||||
m_slowFrame = time () + 1.0f;
|
||||
|
|
@ -816,6 +849,18 @@ void Game::searchEntities (const Vector &position, const float radius, EntitySea
|
|||
}
|
||||
}
|
||||
|
||||
bool Game::isShootableBreakable (edict_t *ent) {
|
||||
if (isNullEntity (ent)) {
|
||||
return false;
|
||||
}
|
||||
auto classname = STRING (ent->v.classname);
|
||||
|
||||
if (strcmp (classname, "func_breakable") == 0 || (strcmp (classname, "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE))) {
|
||||
return ent->v.takedamage != DAMAGE_NO && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY) && ent->v.health < 500.0f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LightMeasure::initializeLightstyles () {
|
||||
// this function initializes lighting information...
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_graph_subfolder ("yb_graph_subfolder", "");
|
||||
ConVar yb_graph_fixcamp ("yb_graph_fixcamp", "1");
|
||||
ConVar yb_graph_url ("yb_graph_url", "http://graph.yapb.ru");
|
||||
ConVar yb_graph_fixcamp ("yb_graph_fixcamp", "1", "Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format.");
|
||||
ConVar yb_graph_url ("yb_graph_url", "http://graph.yapb.ru", "Specifies the URL from bots will be able to download graph in case of missing local one.", false);
|
||||
|
||||
void BotGraph::initGraph () {
|
||||
// this function initialize the graph structures..
|
||||
|
|
@ -20,7 +19,7 @@ void BotGraph::initGraph () {
|
|||
m_editFlags = 0;
|
||||
|
||||
m_learnVelocity= nullptr;
|
||||
m_learnPosition= nullptr;
|
||||
m_learnPosition = nullptr;
|
||||
m_lastNode= nullptr;
|
||||
|
||||
m_pathDisplayTime = 0.0f;
|
||||
|
|
@ -1679,7 +1678,7 @@ void BotGraph::saveOldFormat () {
|
|||
|
||||
const char *BotGraph::getOldFormatGraphName (bool isMemoryFile) {
|
||||
static String buffer;
|
||||
buffer.assignf ("%s%s%s.pwf", getDataDirectory (isMemoryFile), strings.isEmpty (yb_graph_subfolder.str ()) ? "" : yb_graph_subfolder.str (), game.getMapName ());
|
||||
buffer.assignf ("%s%s.pwf", getDataDirectory (isMemoryFile), game.getMapName ());
|
||||
|
||||
if (File::exists (buffer)) {
|
||||
return buffer.chars ();
|
||||
|
|
@ -2472,7 +2471,7 @@ void BotGraph::addBasic () {
|
|||
Vector up, down, front, back;
|
||||
|
||||
const Vector &diff = ((ladderLeft - ladderRight) ^ Vector (0.0f, 0.0f, 0.0f)).normalize () * 15.0f;
|
||||
front = back = game.getAbsPos (ent);
|
||||
front = back = game.getEntityWorldOrigin (ent);
|
||||
|
||||
front = front + diff; // front
|
||||
back = back - diff; // back
|
||||
|
|
@ -2512,7 +2511,7 @@ void BotGraph::addBasic () {
|
|||
|
||||
auto autoCreateForEntity = [] (int type, const char *entity) {
|
||||
game.searchEntities ("classname", entity, [&] (edict_t *ent) {
|
||||
const Vector &pos = game.getAbsPos (ent);
|
||||
const Vector &pos = game.getEntityWorldOrigin (ent);
|
||||
|
||||
if (graph.getNearestNoBuckets (pos, 50.0f) == kInvalidNodeIndex) {
|
||||
graph.add (type, pos);
|
||||
|
|
@ -2577,24 +2576,24 @@ const char *BotGraph::getDataDirectory (bool isMemoryFile) {
|
|||
return buffer.chars ();
|
||||
}
|
||||
|
||||
void BotGraph::setBombPos (bool reset, const Vector &pos) {
|
||||
void BotGraph::setBombOrigin (bool reset, const Vector &pos) {
|
||||
// this function stores the bomb position as a vector
|
||||
|
||||
if (reset) {
|
||||
m_bombPos= nullptr;
|
||||
m_bombOrigin = nullptr;
|
||||
bots.setBombPlanted (false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pos.empty ()) {
|
||||
m_bombPos = pos;
|
||||
m_bombOrigin = pos;
|
||||
return;
|
||||
}
|
||||
|
||||
game.searchEntities ("classname", "grenade", [&] (edict_t *ent) {
|
||||
if (strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0) {
|
||||
m_bombPos = game.getAbsPos (ent);
|
||||
m_bombOrigin = game.getEntityWorldOrigin (ent);
|
||||
return EntitySearchResult::Break;
|
||||
}
|
||||
return EntitySearchResult::Continue;
|
||||
|
|
|
|||
|
|
@ -9,24 +9,25 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_autovacate ("yb_autovacate", "1");
|
||||
ConVar yb_autovacate ("yb_autovacate", "1", "Kick bots to automatically make room for human players.");
|
||||
|
||||
ConVar yb_quota ("yb_quota", "0", Var::Normal);
|
||||
ConVar yb_quota_mode ("yb_quota_mode", "normal");
|
||||
ConVar yb_quota_match ("yb_quota_match", "0");
|
||||
ConVar yb_think_fps ("yb_think_fps", "30.0");
|
||||
ConVar yb_quota ("yb_quota", "0", "Specifies the number bots to be added to the game.", true, 0.0f, static_cast <float> (kGameMaxPlayers));
|
||||
ConVar yb_quota_mode ("yb_quota_mode", "normal", "Specifies the type of quota.\nAllowed values: 'normal', 'fill', and 'match'.\nIf 'fill', the server will adjust bots to keep N players in the game, where N is yb_quota.\nIf 'match', the server will maintain a 1:N ratio of humans to bots, where N is yb_quota_match.", false);
|
||||
ConVar yb_quota_match ("yb_quota_match", "0", "Number of players to match if yb_quota_mode set to 'match'", true, 0.0f, static_cast <float> (kGameMaxPlayers));
|
||||
ConVar yb_think_fps ("yb_think_fps", "30.0", "Specifies hou many times per second bot code will run.", true, 30.0f, 90.0f);
|
||||
|
||||
ConVar yb_join_after_player ("yb_join_after_player", "0");
|
||||
ConVar yb_join_team ("yb_join_team", "any");
|
||||
ConVar yb_join_delay ("yb_join_delay", "5.0");
|
||||
ConVar yb_join_after_player ("yb_join_after_player", "0", "Sepcifies whether bots should join server, only when at least one human player in game.");
|
||||
ConVar yb_join_team ("yb_join_team", "any", "Forces all bots to join team specified here.", false);
|
||||
ConVar yb_join_delay ("yb_join_delay", "5.0", "Specifies after how many seconds bots should start to join the game after the changelevel.", true, 0.0f, 30.0f);
|
||||
|
||||
ConVar yb_name_prefix ("yb_name_prefix", "");
|
||||
ConVar yb_difficulty ("yb_difficulty", "4");
|
||||
ConVar yb_name_prefix ("yb_name_prefix", "", "All the bot names will be prefixed with string specified with this cvar.", false);
|
||||
ConVar yb_difficulty ("yb_difficulty", "4", "All bots difficulty level. Chaning at runtime will affect already created bots.", true, 0.0f, 4.0f);
|
||||
|
||||
ConVar yb_show_avatars ("yb_show_avatars", "1");
|
||||
ConVar yb_show_latency ("yb_show_latency", "2");
|
||||
ConVar yb_language ("yb_language", "en");
|
||||
ConVar yb_ignore_cvars_on_changelevel ("yb_ignore_cvars_on_changelevel", "yb_quota,yb_autovacate");
|
||||
ConVar yb_show_avatars ("yb_show_avatars", "1", "Enables or disabels displaying bot avatars in front of their names in scoreboard. Note, that is currently you can see only avatars of your steam friends.");
|
||||
ConVar yb_show_latency ("yb_show_latency", "2", "Enables latency display in scoreboard.\nAllowed values: '0', '1', '2'.\nIf '0', there is nothing displayed.\nIf '1', there is a 'BOT' is displayed.\nIf '2' fake ping is displayed.", true, 0.0f, 2.0f);
|
||||
|
||||
ConVar yb_language ("yb_language", "en", "Specifies the language for bot messages and menus.", false);
|
||||
ConVar yb_ignore_cvars_on_changelevel ("yb_ignore_cvars_on_changelevel", "yb_quota,yb_autovacate", "Specifies comma separated list of bot cvars, that will not be overriten by config on changelevel.", false);
|
||||
|
||||
ConVar mp_limitteams ("mp_limitteams", nullptr, Var::NoRegister);
|
||||
ConVar mp_autoteambalance ("mp_autoteambalance", nullptr, Var::NoRegister);
|
||||
|
|
@ -1024,8 +1025,9 @@ void Bot::newRound () {
|
|||
clearSearchNodes ();
|
||||
clearRoute ();
|
||||
|
||||
m_pathOrigin= nullptr;
|
||||
m_destOrigin= nullptr;
|
||||
m_pathOrigin = nullptr;
|
||||
m_destOrigin = nullptr;
|
||||
|
||||
m_path = nullptr;
|
||||
m_currentTravelFlags = 0;
|
||||
m_desiredVelocity= nullptr;
|
||||
|
|
@ -1095,7 +1097,7 @@ void Bot::newRound () {
|
|||
m_itemCheckTime = 0.0f;
|
||||
|
||||
m_breakableEntity = nullptr;
|
||||
m_breakableOrigin= nullptr;
|
||||
m_breakableOrigin = nullptr;
|
||||
m_timeDoorOpen = 0.0f;
|
||||
|
||||
resetCollision ();
|
||||
|
|
@ -1104,7 +1106,7 @@ void Bot::newRound () {
|
|||
m_enemy = nullptr;
|
||||
m_lastVictim = nullptr;
|
||||
m_lastEnemy = nullptr;
|
||||
m_lastEnemyOrigin= nullptr;
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
m_trackingEdict = nullptr;
|
||||
m_timeNextTracking = 0.0f;
|
||||
|
||||
|
|
@ -1129,8 +1131,8 @@ void Bot::newRound () {
|
|||
m_liftState = 0;
|
||||
|
||||
m_aimLastError= nullptr;
|
||||
m_position= nullptr;
|
||||
m_liftTravelPos= nullptr;
|
||||
m_position = nullptr;
|
||||
m_liftTravelPos = nullptr;
|
||||
|
||||
setIdealReactionTimers (true);
|
||||
|
||||
|
|
@ -1392,7 +1394,7 @@ void BotManager::notifyBombDefuse () {
|
|||
if (bot->m_team == Team::Terrorist && bot->m_notKilled && bot->getCurrentTaskId () != Task::MoveToPosition) {
|
||||
bot->clearSearchNodes ();
|
||||
|
||||
bot->m_position = graph.getBombPos ();
|
||||
bot->m_position = graph.getBombOrigin ();
|
||||
bot->startTask (Task::MoveToPosition, TaskPri::MoveToPosition, kInvalidNodeIndex, 0.0f, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1576,7 +1578,7 @@ void BotManager::initRound () {
|
|||
client.radio = 0;
|
||||
}
|
||||
|
||||
graph.setBombPos (true);
|
||||
graph.setBombOrigin (true);
|
||||
graph.clearVisited ();
|
||||
|
||||
m_bombSayStatus = BombPlantedSay::ChatSay | BombPlantedSay::Chatter;
|
||||
|
|
@ -1687,8 +1689,8 @@ void BotConfig::loadMainConfig () {
|
|||
firstLoad = false;
|
||||
|
||||
// android is abit hard to play, lower the difficulty by default
|
||||
if (plat.isAndroid && yb_difficulty.int_ () > 2) {
|
||||
yb_difficulty.set (2);
|
||||
if (plat.isAndroid && yb_difficulty.int_ () > 3) {
|
||||
yb_difficulty.set (3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1808,6 +1810,8 @@ void BotConfig::loadChatterConfig () {
|
|||
|
||||
// chatter initialization
|
||||
if (game.is (GameFlags::HasBotVoice) && yb_radio_mode.int_ () == 2 && util.openConfig ("chatter.cfg", "Couldn't open chatter system configuration", &file)) {
|
||||
m_chatter.clear ();
|
||||
|
||||
struct EventMap {
|
||||
String str;
|
||||
int code;
|
||||
|
|
@ -1827,10 +1831,10 @@ void BotConfig::loadChatterConfig () {
|
|||
{ "Radio_ReportTeam", Radio::ReportInTeam, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Affirmative", Radio::RogerThat, kMaxChatterRepeatInteval },
|
||||
{ "Radio_EnemySpotted", Radio::EnemySpotted, 4.0f },
|
||||
{ "Radio_NeedBackup", Radio::NeedBackup, kMaxChatterRepeatInteval },
|
||||
{ "Radio_NeedBackup", Radio::NeedBackup, 5.0f },
|
||||
{ "Radio_SectorClear", Radio::SectorClear, 10.0f },
|
||||
{ "Radio_InPosition", Radio::ImInPosition, 10.0f },
|
||||
{ "Radio_ReportingIn", Radio::ReportingIn, kMaxChatterRepeatInteval },
|
||||
{ "Radio_ReportingIn", Radio::ReportingIn, 3.0f },
|
||||
{ "Radio_ShesGonnaBlow", Radio::ShesGonnaBlow, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Negative", Radio::Negative, kMaxChatterRepeatInteval },
|
||||
{ "Radio_EnemyDown", Radio::EnemyDown, 10.0f },
|
||||
|
|
@ -1853,7 +1857,7 @@ void BotConfig::loadChatterConfig () {
|
|||
{ "Chatter_WhereIsTheBomb", Chatter::WhereIsTheC4, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_DefendingBombSite", Chatter::DefendingBombsite, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_BarelyDefused", Chatter::BarelyDefused, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_NiceshotCommander", Chatter::NiceShotCommander, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_NiceshotCommander", Chatter::NiceShotCommander, 10.0f },
|
||||
{ "Chatter_ReportingIn", Chatter::ReportingIn, 10.0f },
|
||||
{ "Chatter_SpotTheBomber", Chatter::SpotTheBomber, 4.3f },
|
||||
{ "Chatter_VIPSpotted", Chatter::VIPSpotted, 5.3f },
|
||||
|
|
|
|||
|
|
@ -37,10 +37,13 @@ void MessageDispatcher::netMsgTextMsg () {
|
|||
return;
|
||||
}
|
||||
|
||||
// reset bomb position
|
||||
if (game.mapIs (MapFlags::Demolition)) {
|
||||
graph.setBombPos (true);
|
||||
}
|
||||
|
||||
// reset bomb position for all the bots
|
||||
const auto resetBombPosition = [] () -> void {
|
||||
if (game.mapIs (MapFlags::Demolition)) {
|
||||
graph.setBombOrigin (true);
|
||||
}
|
||||
};
|
||||
|
||||
if (cached & TextMsgCache::Commencing) {
|
||||
util.setNeedForWelcome (true);
|
||||
|
|
@ -48,6 +51,8 @@ void MessageDispatcher::netMsgTextMsg () {
|
|||
else if (cached & TextMsgCache::CounterWin) {
|
||||
bots.setLastWinner (Team::CT); // update last winner for economics
|
||||
dispatchChatterMessage ();
|
||||
|
||||
resetBombPosition ();
|
||||
}
|
||||
else if (cached & TextMsgCache::RestartRound) {
|
||||
bots.updateTeamEconomics (Team::CT, true);
|
||||
|
|
@ -60,10 +65,14 @@ void MessageDispatcher::netMsgTextMsg () {
|
|||
bot->m_moneyAmount = mp_startmoney.int_ ();
|
||||
return false;
|
||||
});
|
||||
|
||||
resetBombPosition ();
|
||||
}
|
||||
else if (cached & TextMsgCache::TerroristWin) {
|
||||
bots.setLastWinner (Team::Terrorist); // update last winner for economics
|
||||
dispatchChatterMessage ();
|
||||
|
||||
resetBombPosition ();
|
||||
}
|
||||
else if ((cached & TextMsgCache::BombPlanted) && !bots.isBombPlanted ()) {
|
||||
bots.setBombPlanted (true);
|
||||
|
|
@ -78,7 +87,7 @@ void MessageDispatcher::netMsgTextMsg () {
|
|||
}
|
||||
}
|
||||
}
|
||||
graph.setBombPos ();
|
||||
graph.setBombOrigin ();
|
||||
}
|
||||
|
||||
// check for burst fire message
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_whose_your_daddy ("yb_whose_your_daddy", "0");
|
||||
ConVar yb_debug_heuristic_type ("yb_debug_heuristic_type", "4");
|
||||
ConVar yb_whose_your_daddy ("yb_whose_your_daddy", "0", "Enables or disables extra hard difficulty for bots.");
|
||||
ConVar yb_debug_heuristic_type ("yb_debug_heuristic_type", "4", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f);
|
||||
|
||||
int Bot::findBestGoal () {
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ int Bot::findBestGoal () {
|
|||
|
||||
game.searchEntities ("classname", "weaponbox", [&] (edict_t *ent) {
|
||||
if (strcmp (STRING (ent->v.model), "models/w_backpack.mdl") == 0) {
|
||||
result = graph.getNearest (game.getAbsPos (ent));
|
||||
result = graph.getNearest (game.getEntityWorldOrigin (ent));
|
||||
|
||||
if (graph.exists (result)) {
|
||||
return EntitySearchResult::Break;
|
||||
|
|
@ -100,7 +100,7 @@ int Bot::findBestGoal () {
|
|||
}
|
||||
}
|
||||
else if (game.mapIs (MapFlags::Demolition) && m_team == Team::CT) {
|
||||
if (bots.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && !graph.getBombPos ().empty ()) {
|
||||
if (bots.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && !graph.getBombOrigin ().empty ()) {
|
||||
|
||||
if (bots.hasBombSay (BombPlantedSay::ChatSay)) {
|
||||
pushChatMessage (Chat::Plant);
|
||||
|
|
@ -118,7 +118,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.0) {
|
||||
return m_chosenGoalIndex = graph.getNearest (graph.getBombPos ());
|
||||
return m_chosenGoalIndex = graph.getNearest (graph.getBombOrigin ());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -710,7 +710,7 @@ bool Bot::updateNavigation () {
|
|||
|
||||
if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && strncmp (STRING (tr.pHit->v.classname), "func_door", 9) == 0) {
|
||||
// if the door is near enough...
|
||||
if ((game.getAbsPos (tr.pHit) - pev->origin).lengthSq () < 2500.0f) {
|
||||
if ((game.getEntityWorldOrigin (tr.pHit) - pev->origin).lengthSq () < 2500.0f) {
|
||||
ignoreCollision (); // don't consider being stuck
|
||||
|
||||
if (rg.chance (50)) {
|
||||
|
|
@ -859,6 +859,8 @@ bool Bot::updateLiftHandling () {
|
|||
m_navTimeset = game.time ();
|
||||
m_aimFlags |= AimFlags::Nav;
|
||||
|
||||
pev->button &= ~(IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT);
|
||||
|
||||
ignoreCollision ();
|
||||
};
|
||||
|
||||
|
|
@ -1783,7 +1785,7 @@ int Bot::findBombNode () {
|
|||
|
||||
auto &goals = graph.m_goalPoints;
|
||||
|
||||
auto bomb = graph.getBombPos ();
|
||||
auto bomb = graph.getBombOrigin ();
|
||||
auto audible = isBombAudible ();
|
||||
|
||||
if (!audible.empty ()) {
|
||||
|
|
@ -2326,8 +2328,8 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
}
|
||||
|
||||
// trace from the left waist to the right forward waist pos
|
||||
src = pev->origin + Vector (0.0f, 0.0f, -17.0f) + right * 16.0f;
|
||||
forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) - right * -16.0f + normal * 24.0f;
|
||||
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;
|
||||
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
|
|
@ -2834,7 +2836,7 @@ void Bot::updateBodyAngles () {
|
|||
|
||||
void Bot::updateLookAngles () {
|
||||
|
||||
const float delta = cr::clamp (game.time () - m_lookUpdateTime, cr::kFloatEqualEpsilon, 0.03333f);
|
||||
const float delta = cr::clamp (game.time () - m_lookUpdateTime, cr::kFloatEqualEpsilon, 1.0f / 30.0f);
|
||||
m_lookUpdateTime = game.time ();
|
||||
|
||||
// adjust all body and view angles to face an absolute vector
|
||||
|
|
@ -2994,7 +2996,7 @@ int Bot::getNearestToPlantedBomb () {
|
|||
// search the bomb on the map
|
||||
game.searchEntities ("classname", "grenade", [&result] (edict_t *ent) {
|
||||
if (strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0) {
|
||||
result = graph.getNearest (game.getAbsPos (ent));
|
||||
result = graph.getNearest (game.getEntityWorldOrigin (ent));
|
||||
|
||||
if (graph.exists (result)) {
|
||||
return EntitySearchResult::Break;
|
||||
|
|
@ -3053,7 +3055,7 @@ edict_t *Bot::lookupButton (const char *targetName) {
|
|||
|
||||
// find the nearest button which can open our target
|
||||
game.searchEntities ("target", targetName, [&] (edict_t *ent) {
|
||||
const Vector &pos = game.getAbsPos (ent);
|
||||
const Vector &pos = game.getEntityWorldOrigin (ent);
|
||||
|
||||
// check if this place safe
|
||||
if (!isDeadlyMove (pos)) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1");
|
||||
ConVar yb_enable_query_hook ("yb_enable_query_hook", "1");
|
||||
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1", "Enables or disables showing welcome message to host entity on game start.");
|
||||
ConVar yb_enable_query_hook ("yb_enable_query_hook", "1", "Enables or disabled fake server queries response, that shows bots as real players in server browser.");
|
||||
|
||||
BotUtils::BotUtils () {
|
||||
m_needToSendWelcome = false;
|
||||
|
|
@ -329,7 +329,7 @@ void BotUtils::listenNoise (edict_t *ent, const String &sample, float volume) {
|
|||
if (game.isNullEntity (ent) || sample.empty ()) {
|
||||
return;
|
||||
}
|
||||
const Vector &origin = game.getAbsPos (ent);
|
||||
const Vector &origin = game.getEntityWorldOrigin (ent);
|
||||
|
||||
// something wrong with sound...
|
||||
if (origin.empty ()) {
|
||||
|
|
@ -366,9 +366,9 @@ void BotUtils::listenNoise (edict_t *ent, const String &sample, float volume) {
|
|||
|
||||
// update noise stats
|
||||
auto registerNoise = [&origin, &client, &volume] (float distance, float lasting) {
|
||||
client->hearingDistance = distance * volume;
|
||||
client->timeSoundLasting = game.time () + lasting;
|
||||
client->sound = origin;
|
||||
client->noise.dist = distance * volume;
|
||||
client->noise.last = game.time () + lasting;
|
||||
client->noise.pos = origin;
|
||||
};
|
||||
|
||||
// client wasn't found
|
||||
|
|
@ -398,7 +398,7 @@ void BotUtils::listenNoise (edict_t *ent, const String &sample, float volume) {
|
|||
|
||||
// ct used hostage?
|
||||
else if (noise & Noise::Hostage) {
|
||||
registerNoise (1024.0f, 5.00f);
|
||||
registerNoise (1024.0f, 5.00);
|
||||
}
|
||||
|
||||
// broke something?
|
||||
|
|
@ -420,31 +420,33 @@ void BotUtils::simulateNoise (int playerIndex) {
|
|||
return; // reliability check
|
||||
}
|
||||
Client &client = m_clients[playerIndex];
|
||||
ClientNoise noise {};
|
||||
|
||||
float hearDistance = 0.0f;
|
||||
float timeSound = 0.0f;
|
||||
auto buttons = client.ent->v.button | client.ent->v.oldbuttons;
|
||||
|
||||
if (buttons & IN_ATTACK) // pressed attack button?
|
||||
{
|
||||
hearDistance = 2048.0f;
|
||||
timeSound = game.time () + 0.3f;
|
||||
// pressed attack button?
|
||||
if (buttons & IN_ATTACK) {
|
||||
noise.dist = 2048.0f;
|
||||
noise.last = game.time () + 0.3f;
|
||||
}
|
||||
else if (buttons & IN_USE) // pressed used button?
|
||||
{
|
||||
hearDistance = 512.0f;
|
||||
timeSound = game.time () + 0.5f;
|
||||
|
||||
// pressed used button?
|
||||
else if (buttons & IN_USE) {
|
||||
noise.dist = 512.0f;
|
||||
noise.last = game.time () + 0.5f;
|
||||
}
|
||||
else if (buttons & IN_RELOAD) // pressed reload button?
|
||||
{
|
||||
hearDistance = 512.0f;
|
||||
timeSound = game.time () + 0.5f;
|
||||
|
||||
// pressed reload button?
|
||||
else if (buttons & IN_RELOAD) {
|
||||
noise.dist = 512.0f;
|
||||
noise.last = game.time () + 0.5f;
|
||||
}
|
||||
else if (client.ent->v.movetype == MOVETYPE_FLY) // uses ladder?
|
||||
{
|
||||
|
||||
// uses ladder?
|
||||
else if (client.ent->v.movetype == MOVETYPE_FLY) {
|
||||
if (cr::abs (client.ent->v.velocity.z) > 50.0f) {
|
||||
hearDistance = 1024.0f;
|
||||
timeSound = game.time () + 0.3f;
|
||||
noise.dist = 1024.0f;
|
||||
noise.last = game.time () + 0.3f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -452,29 +454,29 @@ void BotUtils::simulateNoise (int playerIndex) {
|
|||
|
||||
if (mp_footsteps.bool_ ()) {
|
||||
// moves fast enough?
|
||||
hearDistance = 1280.0f * (client.ent->v.velocity.length2d () / 260.0f);
|
||||
timeSound = game.time () + 0.3f;
|
||||
noise.dist = 1280.0f * (client.ent->v.velocity.length2d () / 260.0f);
|
||||
noise.last = game.time () + 0.3f;
|
||||
}
|
||||
}
|
||||
|
||||
if (hearDistance <= 0.0) {
|
||||
if (noise.dist <= 0.0) {
|
||||
return; // didn't issue sound?
|
||||
}
|
||||
|
||||
// some sound already associated
|
||||
if (client.timeSoundLasting > game.time ()) {
|
||||
if (client.hearingDistance <= hearDistance) {
|
||||
if (client.noise.last > game.time ()) {
|
||||
if (client.noise.dist <= noise.dist) {
|
||||
// override it with new
|
||||
client.hearingDistance = hearDistance;
|
||||
client.timeSoundLasting = timeSound;
|
||||
client.sound = client.ent->v.origin;
|
||||
client.noise.dist = noise.dist;
|
||||
client.noise.last = noise.last;
|
||||
client.noise.pos = client.ent->v.origin;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (!cr::fzero (noise.last)) {
|
||||
// just remember it
|
||||
client.hearingDistance = hearDistance;
|
||||
client.timeSoundLasting = timeSound;
|
||||
client.sound = client.ent->v.origin;
|
||||
client.noise.dist = noise.dist;
|
||||
client.noise.last = noise.last;
|
||||
client.noise.pos = client.ent->v.origin;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +617,7 @@ void BotUtils::installSendTo () {
|
|||
}
|
||||
|
||||
// enable only on modern games
|
||||
if (game.is (GameFlags::Modern) && (plat.isLinux || plat.isWindows) && !plat.isAndroid && !m_sendToHook.enabled ()) {
|
||||
if (game.is (GameFlags::Modern) && (plat.isLinux || plat.isWindows) && !plat.isArm && !m_sendToHook.enabled ()) {
|
||||
m_sendToHook.patch (reinterpret_cast <void *> (&sendto), reinterpret_cast <void *> (&BotUtils::sendTo));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue