Many small fixes to combat behaviour, navigation and perfomance.
This commit is contained in:
parent
858d247893
commit
f673f5cd0a
26 changed files with 1447 additions and 1330 deletions
|
|
@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \
|
|||
combat.cpp \
|
||||
control.cpp \
|
||||
engine.cpp \
|
||||
interface.cpp \
|
||||
linkage.cpp \
|
||||
navigate.cpp \
|
||||
support.cpp \
|
||||
graph.cpp \
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -19,11 +19,11 @@ void BotUtils::stripTags (String &line) {
|
|||
for (const auto &tag : m_tags) {
|
||||
const size_t start = line.find (tag.first, 0);
|
||||
|
||||
if (start != String::kInvalidIndex) {
|
||||
if (start != String::InvalidIndex) {
|
||||
const size_t end = line.find (tag.second, start);
|
||||
const size_t diff = end - start;
|
||||
|
||||
if (end != String::kInvalidIndex && end > start && diff < 32 && diff > 4) {
|
||||
if (end != String::InvalidIndex && end > start && diff < 32 && diff > 2) {
|
||||
line.erase (start, diff + tag.second.length ());
|
||||
break;
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ bool BotUtils::checkKeywords (const String &line, String &reply) {
|
|||
for (const auto &keyword : factory.keywords) {
|
||||
|
||||
// check is keyword has occurred in message
|
||||
if (line.find (keyword) != String::kInvalidIndex) {
|
||||
if (line.find (keyword) != String::InvalidIndex) {
|
||||
StringArray &usedReplies = factory.usedReplies;
|
||||
|
||||
if (usedReplies.length () >= factory.replies.length () / 4) {
|
||||
|
|
@ -140,7 +140,7 @@ void Bot::prepareChatMessage (const String &message) {
|
|||
size_t pos = message.find ('%');
|
||||
|
||||
// nothing found, bail out
|
||||
if (pos == String::kInvalidIndex || pos >= message.length ()) {
|
||||
if (pos == String::InvalidIndex || pos >= message.length ()) {
|
||||
finishPreparation ();
|
||||
return;
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ void Bot::prepareChatMessage (const String &message) {
|
|||
|
||||
// get roundtime
|
||||
auto getRoundTime = [] () -> String {
|
||||
auto roundTimeSecs = static_cast <int> (bots.getRoundEndTime () - game.timebase ());
|
||||
auto roundTimeSecs = static_cast <int> (bots.getRoundEndTime () - game.time ());
|
||||
|
||||
String roundTime;
|
||||
roundTime.assignf ("%02d:%02d", cr::clamp (roundTimeSecs / 60, 0, 59), cr::clamp (cr::abs (roundTimeSecs % 60), 0, 59));
|
||||
|
|
@ -235,7 +235,7 @@ void Bot::prepareChatMessage (const String &message) {
|
|||
};
|
||||
size_t replaceCounter = 0;
|
||||
|
||||
while (replaceCounter < 6 && (pos = m_chatBuffer.find ('%')) != String::kInvalidIndex) {
|
||||
while (replaceCounter < 6 && (pos = m_chatBuffer.find ('%')) != String::InvalidIndex) {
|
||||
// found one, let's do replace
|
||||
switch (m_chatBuffer[pos + 1]) {
|
||||
|
||||
|
|
@ -295,7 +295,7 @@ bool Bot::isReplyingToChat () {
|
|||
|
||||
if (m_sayTextBuffer.entityIndex != -1 && !m_sayTextBuffer.sayText.empty ()) {
|
||||
// check is time to chat is good
|
||||
if (m_sayTextBuffer.timeNextChat < game.timebase () + rg.float_ (m_sayTextBuffer.chatDelay / 2, m_sayTextBuffer.chatDelay)) {
|
||||
if (m_sayTextBuffer.timeNextChat < game.time () + rg.float_ (m_sayTextBuffer.chatDelay / 2, m_sayTextBuffer.chatDelay)) {
|
||||
String replyText;
|
||||
|
||||
if (rg.chance (m_sayTextBuffer.chatProbability + rg.int_ (20, 50)) && checkChatKeywords (replyText)) {
|
||||
|
|
@ -303,7 +303,7 @@ bool Bot::isReplyingToChat () {
|
|||
pushMsgQueue (BotMsg::Say);
|
||||
|
||||
m_sayTextBuffer.entityIndex = -1;
|
||||
m_sayTextBuffer.timeNextChat = game.timebase () + m_sayTextBuffer.chatDelay;
|
||||
m_sayTextBuffer.timeNextChat = game.time () + m_sayTextBuffer.chatDelay;
|
||||
m_sayTextBuffer.sayText.clear ();
|
||||
|
||||
return true;
|
||||
|
|
@ -323,7 +323,7 @@ void Bot::checkForChat () {
|
|||
}
|
||||
|
||||
// bot chatting turned on?
|
||||
if (m_lastChatTime + rg.float_ (6.0f, 10.0f) < game.timebase () && bots.getLastChatTimestamp () + rg.float_ (2.5f, 5.0f) < game.timebase () && !isReplyingToChat ()) {
|
||||
if (m_lastChatTime + rg.float_ (6.0f, 10.0f) < game.time () && bots.getLastChatTimestamp () + rg.float_ (2.5f, 5.0f) < game.time () && !isReplyingToChat ()) {
|
||||
if (conf.hasChatBank (Chat::Dead)) {
|
||||
const auto &phrase = conf.pickRandomFromChatBank (Chat::Dead);
|
||||
bool sayBufferExists = false;
|
||||
|
|
@ -340,8 +340,8 @@ void Bot::checkForChat () {
|
|||
prepareChatMessage (phrase);
|
||||
pushMsgQueue (BotMsg::Say);
|
||||
|
||||
m_lastChatTime = game.timebase ();
|
||||
bots.setLastChatTimestamp (game.timebase ());
|
||||
m_lastChatTime = game.time ();
|
||||
bots.setLastChatTimestamp (game.time ());
|
||||
|
||||
// add to ignore list
|
||||
m_sayTextBuffer.lastUsedSentences.push (phrase);
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ bool Bot::checkBodyParts (edict_t *target) {
|
|||
|
||||
if (isEnemyHidden (target)) {
|
||||
m_enemyParts = Visibility::None;
|
||||
m_enemyOrigin = nullvec;
|
||||
m_enemyOrigin = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ bool Bot::seesEnemy (edict_t *player, bool ignoreFOV) {
|
|||
}
|
||||
|
||||
if ((ignoreFOV || isInViewCone (player->v.origin)) && checkBodyParts (player)) {
|
||||
m_seeEnemyTime = game.timebase ();
|
||||
m_seeEnemyTime = game.time ();
|
||||
m_lastEnemy = player;
|
||||
m_lastEnemyOrigin = m_enemyOrigin;
|
||||
|
||||
|
|
@ -187,11 +187,21 @@ bool Bot::seesEnemy (edict_t *player, bool ignoreFOV) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Bot::trackEnemies () {
|
||||
if (lookupEnemies ()) {
|
||||
m_states |= Sense::SeeingEnemy;
|
||||
}
|
||||
else {
|
||||
m_states &= ~Sense::SeeingEnemy;
|
||||
m_enemy = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bot::lookupEnemies () {
|
||||
// this function tries to find the best suitable enemy for the bot
|
||||
|
||||
// do not search for enemies while we're blinded, or shooting disabled by user
|
||||
if (m_enemyIgnoreTimer > game.timebase () || m_blindTime > game.timebase () || yb_ignore_enemies.bool_ ()) {
|
||||
if (m_enemyIgnoreTimer > game.time () || m_blindTime > game.time () || yb_ignore_enemies.bool_ ()) {
|
||||
return false;
|
||||
}
|
||||
edict_t *player, *newEnemy = nullptr;
|
||||
|
|
@ -203,18 +213,18 @@ bool Bot::lookupEnemies () {
|
|||
if (!game.isNullEntity (m_enemy) && (m_states & Sense::SeeingEnemy)) {
|
||||
m_states &= ~Sense::SuspectEnemy;
|
||||
}
|
||||
else if (game.isNullEntity (m_enemy) && m_seeEnemyTime + 1.0f > game.timebase () && util.isAlive (m_lastEnemy)) {
|
||||
else if (game.isNullEntity (m_enemy) && m_seeEnemyTime + 1.0f > game.time () && util.isAlive (m_lastEnemy)) {
|
||||
m_states |= Sense::SuspectEnemy;
|
||||
m_aimFlags |= AimFlags::LastEnemy;
|
||||
}
|
||||
m_enemyParts = Visibility::None;
|
||||
m_enemyOrigin= nullvec;
|
||||
m_enemyOrigin= nullptr;
|
||||
|
||||
if (!game.isNullEntity (m_enemy)) {
|
||||
player = m_enemy;
|
||||
|
||||
// is player is alive
|
||||
if (m_enemyUpdateTime > game.timebase () && (m_enemy->v.origin - pev->origin).lengthSq () < nearestDistance && util.isAlive (player) && seesEnemy (player)) {
|
||||
if (m_enemyUpdateTime > game.time () && (m_enemy->v.origin - pev->origin).lengthSq () < nearestDistance && util.isAlive (player) && seesEnemy (player)) {
|
||||
newEnemy = player;
|
||||
}
|
||||
}
|
||||
|
|
@ -262,7 +272,7 @@ bool Bot::lookupEnemies () {
|
|||
}
|
||||
}
|
||||
}
|
||||
m_enemyUpdateTime = cr::clamp (game.timebase () + getFrameInterval () * 25.0f, 0.5f, 0.75f);
|
||||
m_enemyUpdateTime = cr::clamp (game.time () + getFrameInterval () * 25.0f, 0.5f, 0.75f);
|
||||
|
||||
if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) {
|
||||
newEnemy = shieldEnemy;
|
||||
|
|
@ -277,7 +287,7 @@ bool Bot::lookupEnemies () {
|
|||
|
||||
// if enemy is still visible and in field of view, keep it keep track of when we last saw an enemy
|
||||
if (newEnemy == m_enemy) {
|
||||
m_seeEnemyTime = game.timebase ();
|
||||
m_seeEnemyTime = game.time ();
|
||||
|
||||
// zero out reaction time
|
||||
m_actualReactionTime = 0.0f;
|
||||
|
|
@ -287,7 +297,7 @@ bool Bot::lookupEnemies () {
|
|||
return true;
|
||||
}
|
||||
else {
|
||||
if (m_seeEnemyTime + 3.0f < game.timebase () && (m_hasC4 || hasHostage () || !game.isNullEntity (m_targetEntity))) {
|
||||
if (m_seeEnemyTime + 3.0f < game.time () && (m_hasC4 || hasHostage () || !game.isNullEntity (m_targetEntity))) {
|
||||
pushRadioMessage (Radio::EnemySpotted);
|
||||
}
|
||||
m_targetEntity = nullptr; // stop following when we see an enemy...
|
||||
|
|
@ -302,7 +312,7 @@ bool Bot::lookupEnemies () {
|
|||
if (usesSniper ()) {
|
||||
m_enemySurpriseTime *= 0.5f;
|
||||
}
|
||||
m_enemySurpriseTime += game.timebase ();
|
||||
m_enemySurpriseTime += game.time ();
|
||||
|
||||
// zero out reaction time
|
||||
m_actualReactionTime = 0.0f;
|
||||
|
|
@ -312,7 +322,7 @@ bool Bot::lookupEnemies () {
|
|||
m_enemyReachableTimer = 0.0f;
|
||||
|
||||
// keep track of when we last saw an enemy
|
||||
m_seeEnemyTime = game.timebase ();
|
||||
m_seeEnemyTime = game.time ();
|
||||
|
||||
if (!(m_oldButtons & IN_ATTACK)) {
|
||||
return true;
|
||||
|
|
@ -324,10 +334,10 @@ bool Bot::lookupEnemies () {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (other->m_seeEnemyTime + 2.0f < game.timebase () && game.isNullEntity (other->m_lastEnemy) && util.isVisible (pev->origin, other->ent ()) && other->isInViewCone (pev->origin)) {
|
||||
if (other->m_seeEnemyTime + 2.0f < game.time () && game.isNullEntity (other->m_lastEnemy) && util.isVisible (pev->origin, other->ent ()) && other->isInViewCone (pev->origin)) {
|
||||
other->m_lastEnemy = newEnemy;
|
||||
other->m_lastEnemyOrigin = m_lastEnemyOrigin;
|
||||
other->m_seeEnemyTime = game.timebase ();
|
||||
other->m_seeEnemyTime = game.time ();
|
||||
other->m_states |= (Sense::SuspectEnemy | Sense::HearingEnemy);
|
||||
other->m_aimFlags |= AimFlags::LastEnemy;
|
||||
}
|
||||
|
|
@ -343,9 +353,9 @@ bool Bot::lookupEnemies () {
|
|||
m_enemy = nullptr;
|
||||
|
||||
// shoot at dying players if no new enemy to give some more human-like illusion
|
||||
if (m_seeEnemyTime + 0.3f > game.timebase ()) {
|
||||
if (m_seeEnemyTime + 0.3f > game.time ()) {
|
||||
if (!usesSniper ()) {
|
||||
m_shootAtDeadTime = game.timebase () + 0.4f;
|
||||
m_shootAtDeadTime = game.time () + 0.4f;
|
||||
m_actualReactionTime = 0.0f;
|
||||
m_states |= Sense::SuspectEnemy;
|
||||
|
||||
|
|
@ -353,7 +363,7 @@ bool Bot::lookupEnemies () {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
else if (m_shootAtDeadTime > game.timebase ()) {
|
||||
else if (m_shootAtDeadTime > game.time ()) {
|
||||
m_actualReactionTime = 0.0f;
|
||||
m_states |= Sense::SuspectEnemy;
|
||||
|
||||
|
|
@ -364,7 +374,7 @@ bool Bot::lookupEnemies () {
|
|||
|
||||
// if no enemy visible check if last one shoot able through wall
|
||||
if (yb_shoots_thru_walls.bool_ () && m_difficulty >= 2 && isPenetrableObstacle (newEnemy->v.origin)) {
|
||||
m_seeEnemyTime = game.timebase ();
|
||||
m_seeEnemyTime = game.time ();
|
||||
|
||||
m_states |= Sense::SuspectEnemy;
|
||||
m_aimFlags |= AimFlags::LastEnemy;
|
||||
|
|
@ -378,14 +388,14 @@ bool Bot::lookupEnemies () {
|
|||
}
|
||||
|
||||
// check if bots should reload...
|
||||
if ((m_aimFlags <= AimFlags::PredictPath && m_seeEnemyTime + 3.0f < game.timebase () && !(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && game.isNullEntity (m_lastEnemy) && game.isNullEntity (m_enemy) && getCurrentTaskId () != Task::ShootBreakable && getCurrentTaskId () != Task::PlantBomb && getCurrentTaskId () != Task::DefuseBomb) || bots.isRoundOver ()) {
|
||||
if ((m_aimFlags <= AimFlags::PredictPath && m_seeEnemyTime + 3.0f < game.time () && !(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && game.isNullEntity (m_lastEnemy) && game.isNullEntity (m_enemy) && getCurrentTaskId () != Task::ShootBreakable && getCurrentTaskId () != Task::PlantBomb && getCurrentTaskId () != Task::DefuseBomb) || bots.isRoundOver ()) {
|
||||
if (!m_reloadState) {
|
||||
m_reloadState = Reload::Primary;
|
||||
}
|
||||
}
|
||||
|
||||
// is the bot using a sniper rifle or a zoomable rifle?
|
||||
if ((usesSniper () || usesZoomableRifle ()) && m_zoomCheckTime + 1.0f < game.timebase ()) {
|
||||
if ((usesSniper () || usesZoomableRifle ()) && m_zoomCheckTime + 1.0f < game.time ()) {
|
||||
if (pev->fov < 90.0f) {
|
||||
pev->button |= IN_ATTACK2;
|
||||
}
|
||||
|
|
@ -398,15 +408,15 @@ bool Bot::lookupEnemies () {
|
|||
|
||||
Vector Bot::getBodyOffsetError (float distance) {
|
||||
if (game.isNullEntity (m_enemy)) {
|
||||
return nullvec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_aimErrorTime < game.timebase ()) {
|
||||
if (m_aimErrorTime < game.time ()) {
|
||||
const float error = distance / (cr::clamp (m_difficulty, 1, 4) * 1000.0f);
|
||||
Vector &maxs = m_enemy->v.maxs, &mins = m_enemy->v.mins;
|
||||
|
||||
m_aimLastError = Vector (rg.float_ (mins.x * error, maxs.x * error), rg.float_ (mins.y * error, maxs.y * error), rg.float_ (mins.z * error, maxs.z * error));
|
||||
m_aimErrorTime = game.timebase () + rg.float_ (0.5f, 1.0f);
|
||||
m_aimErrorTime = game.time () + rg.float_ (0.5f, 1.0f);
|
||||
}
|
||||
return m_aimLastError;
|
||||
}
|
||||
|
|
@ -434,15 +444,10 @@ const Vector &Bot::getEnemyBodyOffset () {
|
|||
else if (distance < 800.0f && usesSniper ()) {
|
||||
m_enemyParts &= ~Visibility::Head;
|
||||
}
|
||||
|
||||
// do not aim at head while enemy is soooo close enough to enemy when recoil aims at head automatically
|
||||
else if (distance < kSprayDistance) {
|
||||
m_enemyParts &= ~Visibility::Head;
|
||||
}
|
||||
Vector aimPos = m_enemy->v.origin;
|
||||
|
||||
if (m_difficulty > 2 && !(m_enemyParts & Visibility::Other)) {
|
||||
aimPos = (m_enemy->v.velocity - pev->velocity) * getFrameInterval () + aimPos;
|
||||
if (m_difficulty > 2) {
|
||||
aimPos += (m_enemy->v.velocity - pev->velocity) * (getFrameInterval () * 1.75f);
|
||||
}
|
||||
|
||||
// if we only suspect an enemy behind a wall take the worst skill
|
||||
|
|
@ -450,16 +455,22 @@ const Vector &Bot::getEnemyBodyOffset () {
|
|||
aimPos += getBodyOffsetError (distance);
|
||||
}
|
||||
else {
|
||||
bool useBody = !usesPistol () && distance > kSprayDistance && distance < 2048.0f;
|
||||
|
||||
// now take in account different parts of enemy body
|
||||
if (m_enemyParts & (Visibility::Head | Visibility::Body)) {
|
||||
int headshotFreq[5] = { 20, 40, 60, 80, 100 };
|
||||
|
||||
// now check is our skill match to aim at head, else aim at enemy body
|
||||
if (rg.chance (headshotFreq[m_difficulty]) || usesPistol ()) {
|
||||
if (rg.chance (headshotFreq[m_difficulty]) && !useBody) {
|
||||
aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance);
|
||||
}
|
||||
else {
|
||||
aimPos.z += getEnemyBodyOffsetCorrection (distance);
|
||||
|
||||
if (useBody) {
|
||||
aimPos.z += 4.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_enemyParts & Visibility::Body) {
|
||||
|
|
@ -496,7 +507,7 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) {
|
|||
float result = -2.0f;
|
||||
|
||||
if (distance < kSprayDistance) {
|
||||
return -9.0f;
|
||||
return -16.0f;
|
||||
}
|
||||
else if (distance >= kDoubleSprayDistance) {
|
||||
if (sniper) {
|
||||
|
|
@ -655,7 +666,7 @@ bool Bot::needToPauseFiring (float distance) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_firePause > game.timebase ()) {
|
||||
if (m_firePause > game.time ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -678,16 +689,16 @@ bool Bot::needToPauseFiring (float distance) {
|
|||
const float xPunch = cr::degreesToRadians (pev->punchangle.x);
|
||||
const float yPunch = cr::degreesToRadians (pev->punchangle.y);
|
||||
|
||||
float interval = getFrameInterval ();
|
||||
float tolerance = (100.0f - m_difficulty * 25.0f) / 99.0f;
|
||||
const float interval = getFrameInterval ();
|
||||
const float tolerance = (100.0f - m_difficulty * 25.0f) / 99.0f;
|
||||
|
||||
// check if we need to compensate recoil
|
||||
if (cr::tanf (cr::sqrtf (cr::abs (xPunch * xPunch) + cr::abs (yPunch * yPunch))) * distance > offset + 30.0f + tolerance) {
|
||||
if (m_firePause < game.timebase ()) {
|
||||
m_firePause = rg.float_ (0.5f, 0.5f + 0.3f * tolerance);
|
||||
if (m_firePause < game.time ()) {
|
||||
m_firePause = rg.float_ (0.65f, 0.65f + 0.3f * tolerance);
|
||||
}
|
||||
m_firePause -= interval;
|
||||
m_firePause += game.timebase ();
|
||||
m_firePause += game.time ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -700,7 +711,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
// we want to fire weapon, don't reload now
|
||||
if (!m_isReloading) {
|
||||
m_reloadState = Reload::None;
|
||||
m_reloadCheckTime = game.timebase () + 3.0f;
|
||||
m_reloadCheckTime = game.time () + 3.0f;
|
||||
}
|
||||
|
||||
// select this weapon if it isn't already selected
|
||||
|
|
@ -730,7 +741,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
// if we're have a glock or famas vary burst fire mode
|
||||
checkBurstMode (distance);
|
||||
|
||||
if (hasShield () && m_shieldCheckTime < game.timebase () && getCurrentTaskId () != Task::Camp) // better shield gun usage
|
||||
if (hasShield () && m_shieldCheckTime < game.time () && getCurrentTaskId () != Task::Camp) // better shield gun usage
|
||||
{
|
||||
if (distance >= 750.0f && !isShieldDrawn ()) {
|
||||
pev->button |= IN_ATTACK2; // draw the shield
|
||||
|
|
@ -738,11 +749,11 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
else if (isShieldDrawn () || (!game.isNullEntity (m_enemy) && ((m_enemy->v.button & IN_RELOAD) || !seesEntity (m_enemy->v.origin)))) {
|
||||
pev->button |= IN_ATTACK2; // draw out the shield
|
||||
}
|
||||
m_shieldCheckTime = game.timebase () + 1.0f;
|
||||
m_shieldCheckTime = game.time () + 1.0f;
|
||||
}
|
||||
|
||||
// is the bot holding a sniper rifle?
|
||||
if (usesSniper () && m_zoomCheckTime < game.timebase ()) {
|
||||
if (usesSniper () && m_zoomCheckTime < game.time ()) {
|
||||
// should the bot switch to the long-range zoom?
|
||||
if (distance > 1500.0f && pev->fov >= 40.0f) {
|
||||
pev->button |= IN_ATTACK2;
|
||||
|
|
@ -757,11 +768,11 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
else if (distance <= 150.0f && pev->fov < 90.0f) {
|
||||
pev->button |= IN_ATTACK2;
|
||||
}
|
||||
m_zoomCheckTime = game.timebase () + 0.25f;
|
||||
m_zoomCheckTime = game.time () + 0.25f;
|
||||
}
|
||||
|
||||
// else is the bot holding a zoomable rifle?
|
||||
else if (m_difficulty < 3 && usesZoomableRifle () && m_zoomCheckTime < game.timebase ()) {
|
||||
else if (m_difficulty < 3 && usesZoomableRifle () && m_zoomCheckTime < game.time ()) {
|
||||
// should the bot switch to zoomed mode?
|
||||
if (distance > 800.0f && pev->fov >= 90.0f) {
|
||||
pev->button |= IN_ATTACK2;
|
||||
|
|
@ -771,23 +782,23 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
else if (distance <= 800.0f && pev->fov < 90.0f) {
|
||||
pev->button |= IN_ATTACK2;
|
||||
}
|
||||
m_zoomCheckTime = game.timebase () + 0.5f;
|
||||
m_zoomCheckTime = game.time () + 0.5f;
|
||||
}
|
||||
|
||||
// we're should stand still before firing sniper weapons, else sniping is useless..
|
||||
if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) && !m_isReloading && pev->velocity.lengthSq () > 0.0f) {
|
||||
m_moveSpeed = 0.0f;
|
||||
m_strafeSpeed = 0.0f;
|
||||
m_navTimeset = game.timebase ();
|
||||
m_navTimeset = game.time ();
|
||||
|
||||
if (cr::abs (pev->velocity.x) > 5.0f || cr::abs (pev->velocity.y) > 5.0f || cr::abs (pev->velocity.z) > 5.0f) {
|
||||
m_sniperStopTime = game.timebase () + 2.5f;
|
||||
m_sniperStopTime = game.time () + 2.5f;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// need to care for burst fire?
|
||||
if (distance < kSprayDistance || m_blindTime > game.timebase ()) {
|
||||
if (distance < kSprayDistance || m_blindTime > game.time ()) {
|
||||
if (id == Weapon::Knife) {
|
||||
if (distance < 64.0f) {
|
||||
if (rg.chance (30) || hasShield ()) {
|
||||
|
|
@ -813,7 +824,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
}
|
||||
}
|
||||
}
|
||||
m_shootTime = game.timebase ();
|
||||
m_shootTime = game.time ();
|
||||
}
|
||||
else {
|
||||
if (needToPauseFiring (distance)) {
|
||||
|
|
@ -822,13 +833,13 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
|
||||
// don't attack with knife over long distance
|
||||
if (id == Weapon::Knife) {
|
||||
m_shootTime = game.timebase ();
|
||||
m_shootTime = game.time ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab[choosen].primaryFireHold) {
|
||||
m_shootTime = game.timebase ();
|
||||
m_zoomCheckTime = game.timebase ();
|
||||
m_shootTime = game.time ();
|
||||
m_zoomCheckTime = game.time ();
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack
|
||||
}
|
||||
|
|
@ -840,21 +851,22 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
|||
|
||||
const int offset = cr::abs <int> (m_difficulty * 25 / 20 - 5);
|
||||
|
||||
m_shootTime = game.timebase () + 0.1f + rg.float_ (minDelay[offset], maxDelay[offset]);
|
||||
m_zoomCheckTime = game.timebase ();
|
||||
m_shootTime = game.time () + 0.1f + rg.float_ (minDelay[offset], maxDelay[offset]);
|
||||
m_zoomCheckTime = game.time ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::fireWeapons () {
|
||||
// this function will return true if weapon was fired, false otherwise
|
||||
|
||||
float distance = (m_lookAt - getEyesPos ()).length (); // how far away is the enemy?
|
||||
|
||||
// or if friend in line of fire, stop this too but do not update shoot time
|
||||
if (!game.isNullEntity (m_enemy)) {
|
||||
if (isFriendInLineOfFire (distance)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
m_lastFightStyleCheck = game.timebase ();
|
||||
m_lastFightStyleCheck = game.time ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -905,10 +917,10 @@ void Bot::fireWeapons () {
|
|||
|
||||
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.timebase ()) {
|
||||
if (m_reloadState == Reload::None || m_reloadCheckTime > game.time ()) {
|
||||
m_isReloading = true;
|
||||
m_reloadState = Reload::Primary;
|
||||
m_reloadCheckTime = game.timebase ();
|
||||
m_reloadCheckTime = game.time ();
|
||||
|
||||
if (rg.chance (cr::abs (m_difficulty * 25 - 100))) {
|
||||
pushRadioMessage (Radio::NeedBackup);
|
||||
|
|
@ -957,10 +969,14 @@ bool Bot::isWeaponBadAtDistance (int weaponIndex, float distance) {
|
|||
}
|
||||
|
||||
void Bot::focusEnemy () {
|
||||
if (game.isNullEntity (m_enemy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// aim for the head and/or body
|
||||
m_lookAt = getEnemyBodyOffset ();
|
||||
|
||||
if (m_enemySurpriseTime > game.timebase () || game.isNullEntity (m_enemy)) {
|
||||
if (m_enemySurpriseTime > game.time ()) {
|
||||
return;
|
||||
}
|
||||
float distance = (m_lookAt - getEyesPos ()).length2d (); // how far away is the enemy scum?
|
||||
|
|
@ -1010,7 +1026,7 @@ void Bot::attackMovement () {
|
|||
}
|
||||
float distance = (m_lookAt - getEyesPos ()).length2d (); // how far away is the enemy scum?
|
||||
|
||||
if (m_lastUsedNodesTime + getFrameInterval () + 0.5f < game.timebase ()) {
|
||||
if (m_lastUsedNodesTime + getFrameInterval () + 0.5f < game.time ()) {
|
||||
int approach;
|
||||
|
||||
if (m_currentWeapon == Weapon::Knife) {
|
||||
|
|
@ -1048,10 +1064,10 @@ void Bot::attackMovement () {
|
|||
|
||||
if (usesSniper () || !(m_enemyParts & (Visibility::Body | Visibility::Head))) {
|
||||
m_fightStyle = Fight::Stay;
|
||||
m_lastFightStyleCheck = game.timebase ();
|
||||
m_lastFightStyleCheck = game.time ();
|
||||
}
|
||||
else if (usesRifle () || usesSubmachine ()) {
|
||||
if (m_lastFightStyleCheck + 3.0f < game.timebase ()) {
|
||||
if (m_lastFightStyleCheck + 3.0f < game.time ()) {
|
||||
int rand = rg.int_ (1, 100);
|
||||
|
||||
if (distance < 450.0f) {
|
||||
|
|
@ -1073,23 +1089,15 @@ void Bot::attackMovement () {
|
|||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
}
|
||||
m_lastFightStyleCheck = game.timebase ();
|
||||
m_lastFightStyleCheck = game.time ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_lastFightStyleCheck + 3.0f < game.timebase ()) {
|
||||
if (rg.chance (50)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
else {
|
||||
m_fightStyle = Fight::Stay;
|
||||
}
|
||||
m_lastFightStyleCheck = game.timebase ();
|
||||
}
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
|
||||
if (m_fightStyle == Fight::Strafe || ((pev->button & IN_RELOAD) || m_isReloading) || (usesPistol () && distance < 400.0f) || m_currentWeapon == Weapon::Knife) {
|
||||
if (m_strafeSetTime < game.timebase ()) {
|
||||
if (m_strafeSetTime < game.time ()) {
|
||||
|
||||
// to start strafing, we have to first figure out if the target is on the left side or right side
|
||||
const auto &dirToPoint = (pev->origin - m_enemy->v.origin).normalize2d ();
|
||||
|
|
@ -1105,7 +1113,7 @@ void Bot::attackMovement () {
|
|||
if (rg.chance (30)) {
|
||||
m_combatStrafeDir = (m_combatStrafeDir == Dodge::Left ? Dodge::Right : Dodge::Left);
|
||||
}
|
||||
m_strafeSetTime = game.timebase () + rg.float_ (0.5f, 3.0f);
|
||||
m_strafeSetTime = game.time () + rg.float_ (0.5f, 3.0f);
|
||||
}
|
||||
|
||||
if (m_combatStrafeDir == Dodge::Right) {
|
||||
|
|
@ -1114,7 +1122,7 @@ void Bot::attackMovement () {
|
|||
}
|
||||
else {
|
||||
m_combatStrafeDir = Dodge::Left;
|
||||
m_strafeSetTime = game.timebase () + rg.float_ (0.8f, 1.3f);
|
||||
m_strafeSetTime = game.time () + rg.float_ (0.8f, 1.1f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -1123,11 +1131,11 @@ void Bot::attackMovement () {
|
|||
}
|
||||
else {
|
||||
m_combatStrafeDir = Dodge::Right;
|
||||
m_strafeSetTime = game.timebase () + rg.float_ (0.8f, 1.3f);
|
||||
m_strafeSetTime = game.time () + rg.float_ (0.8f, 1.1f);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_difficulty >= 3 && (m_jumpTime + 5.0f < game.timebase () && isOnFloor () && rg.int_ (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.length2d () > 120.0f) && !usesSniper ()) {
|
||||
if (m_difficulty >= 3 && (m_jumpTime + 5.0f < game.time () && isOnFloor () && rg.int_ (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.length2d () > 120.0f) && !usesSniper ()) {
|
||||
pev->button |= IN_JUMP;
|
||||
}
|
||||
|
||||
|
|
@ -1144,32 +1152,24 @@ void Bot::attackMovement () {
|
|||
int enemyNearestIndex = graph.getNearest (m_enemy->v.origin);
|
||||
|
||||
if (graph.isDuckVisible (m_currentNodeIndex, enemyNearestIndex) && graph.isDuckVisible (enemyNearestIndex, m_currentNodeIndex)) {
|
||||
m_duckTime = game.timebase () + 0.5f;
|
||||
m_duckTime = game.time () + 0.64f;
|
||||
}
|
||||
}
|
||||
m_moveSpeed = 0.0f;
|
||||
m_strafeSpeed = 0.0f;
|
||||
m_navTimeset = game.timebase ();
|
||||
m_navTimeset = game.time ();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_duckTime > game.timebase ()) {
|
||||
m_moveSpeed = 0.0f;
|
||||
m_strafeSpeed = 0.0f;
|
||||
}
|
||||
|
||||
if (m_moveSpeed > 0.0f && m_currentWeapon != Weapon::Knife) {
|
||||
m_moveSpeed = getShiftSpeed ();
|
||||
}
|
||||
|
||||
if (m_isReloading) {
|
||||
m_moveSpeed = -pev->maxspeed;
|
||||
m_duckTime = game.timebase () - 1.0f;
|
||||
if (m_fightStyle == Fight::Stay || (m_duckTime > game.time () || m_sniperStopTime > game.time ())) {
|
||||
if (m_moveSpeed > 0.0f) {
|
||||
m_moveSpeed = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isInWater () && !isOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed >= 0.0f)) {
|
||||
Vector right, forward;
|
||||
pev->v_angle.buildVectors (&forward, &right, nullptr);
|
||||
pev->v_angle.angleVectors (&forward, &right, nullptr);
|
||||
|
||||
if (isDeadlyMove (pev->origin + (forward * m_moveSpeed * 0.2f) + (right * m_strafeSpeed * 0.2f) + (pev->velocity * getFrameInterval ()))) {
|
||||
m_strafeSpeed = -m_strafeSpeed;
|
||||
|
|
@ -1499,7 +1499,7 @@ void Bot::decideFollowUser () {
|
|||
|
||||
void Bot::updateTeamCommands () {
|
||||
// prevent spamming
|
||||
if (m_timeTeamOrder > game.timebase () + 2.0f || game.is (GameFlags::FreeForAll) || !yb_radio_mode.int_ ()) {
|
||||
if (m_timeTeamOrder > game.time () + 2.0f || game.is (GameFlags::FreeForAll) || !yb_radio_mode.int_ ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1534,7 +1534,7 @@ void Bot::updateTeamCommands () {
|
|||
else if (memberExists && yb_radio_mode.int_ () == 2) {
|
||||
pushChatterMessage (Chatter::ScaredEmotion);
|
||||
}
|
||||
m_timeTeamOrder = game.timebase () + rg.float_ (15.0f, 30.0f);
|
||||
m_timeTeamOrder = game.time () + rg.float_ (15.0f, 30.0f);
|
||||
}
|
||||
|
||||
bool Bot::isGroupOfEnemies (const Vector &location, int numEnemies, float radius) {
|
||||
|
|
@ -1562,13 +1562,19 @@ bool Bot::isGroupOfEnemies (const Vector &location, int numEnemies, float radius
|
|||
|
||||
void Bot::checkReload () {
|
||||
// check the reload state
|
||||
if (getCurrentTaskId () == Task::PlantBomb || getCurrentTaskId () == Task::DefuseBomb || getCurrentTaskId () == Task::PickupItem || getCurrentTaskId () == Task::ThrowFlashbang || getCurrentTaskId () == Task::ThrowSmoke || m_isUsingGrenade) {
|
||||
auto task = getCurrentTaskId ();
|
||||
|
||||
// we're should not reload, while doing next tasks
|
||||
bool uninterruptibleTask = (task == Task::PlantBomb || task == Task::DefuseBomb || task == Task::PickupItem || task == Task::ThrowExplosive || task == Task::ThrowFlashbang || task == Task::ThrowSmoke);
|
||||
|
||||
// do not check for reload
|
||||
if (uninterruptibleTask || m_isUsingGrenade) {
|
||||
m_reloadState = Reload::None;
|
||||
return;
|
||||
}
|
||||
|
||||
m_isReloading = false; // update reloading status
|
||||
m_reloadCheckTime = game.timebase () + 3.0f;
|
||||
m_reloadCheckTime = game.time () + 3.0f;
|
||||
|
||||
if (m_reloadState != Reload::None) {
|
||||
int weaponIndex = 0;
|
||||
|
|
@ -1611,7 +1617,7 @@ void Bot::checkReload () {
|
|||
}
|
||||
else {
|
||||
// if we have enemy don't reload next weapon
|
||||
if ((m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) || m_seeEnemyTime + 5.0f > game.timebase ()) {
|
||||
if ((m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) || m_seeEnemyTime + 5.0f > game.time ()) {
|
||||
m_reloadState = Reload::None;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,15 +26,15 @@ int BotControl::cmdAddBot () {
|
|||
}
|
||||
|
||||
// if team is specified, modify args to set team
|
||||
if (m_args[alias].find ("_ct", 0) != String::kInvalidIndex) {
|
||||
if (m_args[alias].find ("_ct", 0) != String::InvalidIndex) {
|
||||
m_args.set (team, "2");
|
||||
}
|
||||
else if (m_args[alias].find ("_t", 0) != String::kInvalidIndex) {
|
||||
else if (m_args[alias].find ("_t", 0) != String::InvalidIndex) {
|
||||
m_args.set (team, "1");
|
||||
}
|
||||
|
||||
// if highskilled bot is requsted set personality to rusher and maxout difficulty
|
||||
if (m_args[alias].find ("hs", 0) != String::kInvalidIndex) {
|
||||
if (m_args[alias].find ("hs", 0) != String::InvalidIndex) {
|
||||
m_args.set (difficulty, "4");
|
||||
m_args.set (personality, "1");
|
||||
}
|
||||
|
|
@ -50,10 +50,10 @@ int BotControl::cmdKickBot () {
|
|||
fixMissingArgs (max);
|
||||
|
||||
// if team is specified, kick from specified tram
|
||||
if (m_args[alias].find ("_ct", 0) != String::kInvalidIndex || getInt (team) == 2 || getStr (team) == "ct") {
|
||||
if (m_args[alias].find ("_ct", 0) != String::InvalidIndex || getInt (team) == 2 || getStr (team) == "ct") {
|
||||
bots.kickFromTeam (Team::CT);
|
||||
}
|
||||
else if (m_args[alias].find ("_t", 0) != String::kInvalidIndex || getInt (team) == 1 || getStr (team) == "t") {
|
||||
else if (m_args[alias].find ("_t", 0) != String::InvalidIndex || getInt (team) == 1 || getStr (team) == "t") {
|
||||
bots.kickFromTeam (Team::Terrorist);
|
||||
}
|
||||
else {
|
||||
|
|
@ -84,10 +84,10 @@ int BotControl::cmdKillBots () {
|
|||
fixMissingArgs (max);
|
||||
|
||||
// if team is specified, kick from specified tram
|
||||
if (m_args[alias].find ("_ct", 0) != String::kInvalidIndex || getInt (team) == 2 || getStr (team) == "ct") {
|
||||
if (m_args[alias].find ("_ct", 0) != String::InvalidIndex || getInt (team) == 2 || getStr (team) == "ct") {
|
||||
bots.killAllBots (Team::CT);
|
||||
}
|
||||
else if (m_args[alias].find ("_t", 0) != String::kInvalidIndex || getInt (team) == 1 || getStr (team) == "t") {
|
||||
else if (m_args[alias].find ("_t", 0) != String::InvalidIndex || getInt (team) == 1 || getStr (team) == "t") {
|
||||
bots.killAllBots (Team::Terrorist);
|
||||
}
|
||||
else {
|
||||
|
|
@ -638,13 +638,13 @@ int BotControl::cmdNodePathCreate () {
|
|||
graph.setEditFlag (GraphEdit::On);
|
||||
|
||||
// choose the direction for path creation
|
||||
if (m_args[cmd].find ("_both", 0) != String::kInvalidIndex) {
|
||||
if (m_args[cmd].find ("_both", 0) != String::InvalidIndex) {
|
||||
graph.pathCreate (PathConnection::Bidirectional);
|
||||
}
|
||||
else if (m_args[cmd].find ("_in", 0) != String::kInvalidIndex) {
|
||||
else if (m_args[cmd].find ("_in", 0) != String::InvalidIndex) {
|
||||
graph.pathCreate (PathConnection::Incoming);
|
||||
}
|
||||
else if (m_args[cmd].find ("_out", 0) != String::kInvalidIndex) {
|
||||
else if (m_args[cmd].find ("_out", 0) != String::InvalidIndex) {
|
||||
graph.pathCreate (PathConnection::Outgoing);
|
||||
}
|
||||
else {
|
||||
|
|
@ -1052,17 +1052,20 @@ int BotControl::menuClassSelect (int item) {
|
|||
|
||||
int BotControl::menuCommands (int item) {
|
||||
showMenu (Menu::None); // reset menu display
|
||||
Bot *bot = nullptr;
|
||||
Bot *nearest = nullptr;
|
||||
|
||||
switch (item) {
|
||||
case 1:
|
||||
case 2:
|
||||
if (util.findNearestPlayer (reinterpret_cast <void **> (&bot), m_ent, 600.0f, true, true, true) && bot->m_hasC4 && !bot->hasHostage ()) {
|
||||
if (util.findNearestPlayer (reinterpret_cast <void **> (&m_djump), m_ent, 600.0f, true, true, true, true, false) && !m_djump->m_hasC4 && !m_djump->hasHostage ()) {
|
||||
if (item == 1) {
|
||||
bot->startDoubleJump (m_ent);
|
||||
m_djump->startDoubleJump (m_ent);
|
||||
}
|
||||
else {
|
||||
bot->resetDoubleJump ();
|
||||
if (m_djump) {
|
||||
m_djump->resetDoubleJump ();
|
||||
m_djump = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
showMenu (Menu::Commands);
|
||||
|
|
@ -1070,8 +1073,8 @@ int BotControl::menuCommands (int item) {
|
|||
|
||||
case 3:
|
||||
case 4:
|
||||
if (util.findNearestPlayer (reinterpret_cast <void **> (&bot), m_ent, 600.0f, true, true, true, true, item == 4 ? false : true)) {
|
||||
bot->dropWeaponForUser (m_ent, item == 4 ? false : true);
|
||||
if (util.findNearestPlayer (reinterpret_cast <void **> (&nearest), m_ent, 600.0f, true, true, true, true, item == 4 ? false : true)) {
|
||||
nearest->dropWeaponForUser (m_ent, item == 4 ? false : true);
|
||||
}
|
||||
showMenu (Menu::Commands);
|
||||
break;
|
||||
|
|
@ -1641,7 +1644,7 @@ void BotControl::showMenu (int id) {
|
|||
Client &client = util.getClient (game.indexOfPlayer (m_ent));
|
||||
|
||||
if (id == Menu::None) {
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullvec, m_ent)
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullptr, m_ent)
|
||||
.writeShort (0)
|
||||
.writeChar (0)
|
||||
.writeByte (0)
|
||||
|
|
@ -1657,7 +1660,7 @@ void BotControl::showMenu (int id) {
|
|||
MessageWriter msg;
|
||||
|
||||
while (strlen (text) >= 64) {
|
||||
msg.start (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullvec, m_ent)
|
||||
msg.start (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullptr, m_ent)
|
||||
.writeShort (display.slots)
|
||||
.writeChar (-1)
|
||||
.writeByte (1);
|
||||
|
|
@ -1669,7 +1672,7 @@ void BotControl::showMenu (int id) {
|
|||
text += 64;
|
||||
}
|
||||
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullvec, m_ent)
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, msgs.id (NetMsg::ShowMenu), nullptr, m_ent)
|
||||
.writeShort (display.slots)
|
||||
.writeChar (-1)
|
||||
.writeByte (0)
|
||||
|
|
@ -1773,6 +1776,8 @@ void BotControl::maintainAdminRights () {
|
|||
|
||||
BotControl::BotControl () {
|
||||
m_ent = nullptr;
|
||||
m_djump = nullptr;
|
||||
|
||||
m_isFromConsole = false;
|
||||
m_isMenuFillCommand = false;
|
||||
m_rapidOutput = false;
|
||||
|
|
@ -1789,22 +1794,22 @@ BotControl::BotControl () {
|
|||
m_cmds.emplace ("version/ver/about", "version [no arguments]", "Displays version information about bot build.", &BotControl::cmdVersion);
|
||||
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/wp/wpt/waypoint", "graph [help]", "Handles graph operations.", &BotControl::cmdNode);
|
||||
m_cmds.emplace ("graph/g/wp/wpt/waypoint", "graph [help]", "Handles graph operations.", &BotControl::cmdNode);
|
||||
|
||||
// declare the menus
|
||||
createMenus ();
|
||||
}
|
||||
|
||||
void BotControl::handleEngineCommands () {
|
||||
ctrl.collectArgs ();
|
||||
ctrl.setIssuer (game.getLocalEntity ());
|
||||
collectArgs ();
|
||||
setIssuer (game.getLocalEntity ());
|
||||
|
||||
ctrl.setFromConsole (true);
|
||||
ctrl.executeCommands ();
|
||||
setFromConsole (true);
|
||||
executeCommands ();
|
||||
}
|
||||
|
||||
bool BotControl::handleClientCommands (edict_t *ent) {
|
||||
ctrl.collectArgs ();
|
||||
collectArgs ();
|
||||
setIssuer (ent);
|
||||
|
||||
setFromConsole (true);
|
||||
|
|
@ -1812,7 +1817,7 @@ bool BotControl::handleClientCommands (edict_t *ent) {
|
|||
}
|
||||
|
||||
bool BotControl::handleMenuCommands (edict_t *ent) {
|
||||
ctrl.collectArgs ();
|
||||
collectArgs ();
|
||||
setIssuer (ent);
|
||||
|
||||
setFromConsole (false);
|
||||
|
|
@ -1827,16 +1832,15 @@ void BotControl::enableDrawModels (bool enable) {
|
|||
entities.push ("info_vip_start");
|
||||
|
||||
for (auto &entity : entities) {
|
||||
edict_t *ent = nullptr;
|
||||
|
||||
while (!game.isNullEntity (ent = engfuncs.pfnFindEntityByString (ent, "classname", entity.chars ()))) {
|
||||
game.searchEntities ("classname", entity, [&enable] (edict_t *ent) {
|
||||
if (enable) {
|
||||
ent->v.effects &= ~EF_NODRAW;
|
||||
}
|
||||
else {
|
||||
ent->v.effects |= EF_NODRAW;
|
||||
}
|
||||
}
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,9 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
|||
else if (strncmp (classname, "func_door", 9) == 0) {
|
||||
m_mapFlags |= MapFlags::HasDoors;
|
||||
}
|
||||
else if (strncmp (classname, "func_button", 11) == 0) {
|
||||
m_mapFlags |= MapFlags::HasButtons;
|
||||
}
|
||||
}
|
||||
|
||||
// next maps doesn't have map-specific entities, so determine it by name
|
||||
|
|
@ -152,7 +155,7 @@ void Game::drawLine (edict_t *ent, const Vector &start, const Vector &end, int w
|
|||
return; // reliability check
|
||||
}
|
||||
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullvec, ent)
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, ent)
|
||||
.writeByte (TE_BEAMPOINTS)
|
||||
.writeCoord (end.x)
|
||||
.writeCoord (end.y)
|
||||
|
|
@ -285,7 +288,7 @@ const char *Game::getModName () {
|
|||
name = engineModName;
|
||||
size_t slash = name.findLastOf ("\\/");
|
||||
|
||||
if (slash != String::kInvalidIndex) {
|
||||
if (slash != String::InvalidIndex) {
|
||||
name = name.substr (slash + 1);
|
||||
}
|
||||
name = name.trim (" \\/");
|
||||
|
|
@ -303,7 +306,7 @@ Vector Game::getAbsPos (edict_t *ent) {
|
|||
// entity that has a bounding box has its center at the center of the bounding box itself.
|
||||
|
||||
if (isNullEntity (ent)) {
|
||||
return nullvec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ent->v.origin.empty ()) {
|
||||
|
|
@ -312,7 +315,7 @@ Vector Game::getAbsPos (edict_t *ent) {
|
|||
return ent->v.origin;
|
||||
}
|
||||
|
||||
void Game::registerCmd (const char *command, void func ()) {
|
||||
void Game::registerEngineCommand (const char *command, void func ()) {
|
||||
// this function tells the engine that a new server command is being declared, in addition
|
||||
// to the standard ones, whose name is command_name. The engine is thus supposed to be aware
|
||||
// that for every "command_name" server command it receives, it should call the function
|
||||
|
|
@ -378,7 +381,7 @@ uint8 *Game::getVisibilitySet (Bot *bot, bool pvs) {
|
|||
void Game::sendClientMessage (bool console, edict_t *ent, const char *message) {
|
||||
// helper to sending the client message
|
||||
|
||||
MessageWriter (MSG_ONE, msgs.id (NetMsg::TextMsg), nullvec, ent)
|
||||
MessageWriter (MSG_ONE, msgs.id (NetMsg::TextMsg), nullptr, ent)
|
||||
.writeByte (console ? HUD_PRINTCONSOLE : HUD_PRINTCENTER)
|
||||
.writeString (message);
|
||||
}
|
||||
|
|
@ -409,7 +412,7 @@ void Game::prepareBotArgs (edict_t *ent, String str) {
|
|||
const size_t space = args.find (' ', 0);
|
||||
|
||||
// if found space
|
||||
if (space != String::kInvalidIndex) {
|
||||
if (space != String::InvalidIndex) {
|
||||
const auto quote = space + 1; // check for quote next to space
|
||||
|
||||
// check if we're got a quoted string
|
||||
|
|
@ -430,7 +433,7 @@ void Game::prepareBotArgs (edict_t *ent, String str) {
|
|||
m_botArgs.clear (); // clear space for next cmd
|
||||
};
|
||||
|
||||
if (str.find (';', 0) != String::kInvalidIndex) {
|
||||
if (str.find (';', 0) != String::InvalidIndex) {
|
||||
for (auto &part : str.split (";")) {
|
||||
parsePartArgs (part);
|
||||
}
|
||||
|
|
@ -629,20 +632,40 @@ bool Game::loadCSBinary () {
|
|||
}
|
||||
|
||||
bool Game::postload () {
|
||||
// ensure we're have all needed directories
|
||||
const char *mod = getModName ();
|
||||
|
||||
// create the needed paths
|
||||
File::createPath (strings.format ("%s/addons/yapb/conf/lang", mod));
|
||||
File::createPath (strings.format ("%s/addons/yapb/data/learned", mod));
|
||||
File::createPath (strings.format ("%s/addons/yapb/data/graph", mod));
|
||||
File::createPath (strings.format ("%s/addons/yapb/data/logs", mod));
|
||||
// ensure we're have all needed directories
|
||||
for (const auto &dir : StringArray { "conf/lang", "data/learned", "data/graph", "data/logs" }) {
|
||||
File::createPath (strings.format ("%s/addons/yapb/%s", getModName (), dir.chars ()));
|
||||
}
|
||||
|
||||
// set out user agent for http stuff
|
||||
http.setUserAgent (strings.format ("%s/%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION));
|
||||
|
||||
// register bot cvars
|
||||
game.registerCvars ();
|
||||
|
||||
|
||||
// register server command(s)
|
||||
registerEngineCommand ("yapb", [] () {
|
||||
ctrl.handleEngineCommands ();
|
||||
});
|
||||
|
||||
registerEngineCommand ("yb", [] () {
|
||||
ctrl.handleEngineCommands ();
|
||||
});
|
||||
|
||||
// register fake metamod command handler if we not! under mm
|
||||
if (!(game.is (GameFlags::Metamod))) {
|
||||
game.registerEngineCommand ("meta", [] () {
|
||||
game.print ("You're launched standalone version of %s. Metamod is not installed or not enabled!", PRODUCT_SHORT_NAME);
|
||||
});
|
||||
}
|
||||
|
||||
// initialize weapons
|
||||
conf.initWeapons ();
|
||||
|
||||
// print game detection info
|
||||
auto printGame = [&] () {
|
||||
auto displayCSVersion = [&] () {
|
||||
String gameVersionStr;
|
||||
StringArray gameVersionFlags;
|
||||
|
||||
|
|
@ -694,7 +717,7 @@ bool Game::postload () {
|
|||
if (!m_gameLib.load (gamedll)) {
|
||||
logger.fatal ("Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gamedll, getModName ());
|
||||
}
|
||||
printGame ();
|
||||
displayCSVersion ();
|
||||
|
||||
}
|
||||
else {
|
||||
|
|
@ -703,7 +726,7 @@ bool Game::postload () {
|
|||
if (!binaryLoaded && !is (GameFlags::Metamod)) {
|
||||
logger.fatal ("Mod that you has started, not supported by this bot (gamedir: %s)", getModName ());
|
||||
}
|
||||
printGame ();
|
||||
displayCSVersion ();
|
||||
|
||||
if (is (GameFlags::Metamod)) {
|
||||
m_gameLib.unload ();
|
||||
|
|
@ -742,7 +765,7 @@ void Game::detectDeathmatch () {
|
|||
}
|
||||
|
||||
void Game::slowFrame () {
|
||||
if (m_slowFrame > timebase ()) {
|
||||
if (m_slowFrame > time ()) {
|
||||
return;
|
||||
}
|
||||
ctrl.maintainAdminRights ();
|
||||
|
|
@ -761,7 +784,36 @@ void Game::slowFrame () {
|
|||
|
||||
// display welcome message
|
||||
util.checkWelcome ();
|
||||
m_slowFrame = timebase () + 1.0f;
|
||||
m_slowFrame = time () + 1.0f;
|
||||
}
|
||||
|
||||
void Game::searchEntities (const String &field, const String &value, EntitySearch functor) {
|
||||
edict_t *ent = nullptr;
|
||||
|
||||
while (!game.isNullEntity (ent = engfuncs.pfnFindEntityByString (ent, field.chars (), value.chars ()))) {
|
||||
if ((ent->v.flags & EF_NODRAW) || (ent->v.flags & FL_CLIENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (functor (ent) == EntitySearchResult::Break) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::searchEntities (const Vector &position, const float radius, EntitySearch functor) {
|
||||
edict_t *ent = nullptr;
|
||||
const Vector &pos = position.empty () ? m_startEntity->v.origin : position;
|
||||
|
||||
while (!game.isNullEntity (ent = engfuncs.pfnFindEntityInSphere (ent, pos, radius))) {
|
||||
if ((ent->v.flags & EF_NODRAW) || (ent->v.flags & FL_CLIENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (functor (ent) == EntitySearchResult::Break) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LightMeasure::initializeLightstyles () {
|
||||
|
|
@ -786,7 +838,7 @@ void LightMeasure::animateLight () {
|
|||
}
|
||||
|
||||
// 'm' is normal light, 'a' is no light, 'z' is double bright
|
||||
const int index = static_cast <int> (game.timebase () * 10.0f);
|
||||
const int index = static_cast <int> (game.time () * 10.0f);
|
||||
|
||||
for (int j = 0; j < MAX_LIGHTSTYLES; ++j) {
|
||||
if (!m_lightstyle[j].length) {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ void BotGraph::initGraph () {
|
|||
m_loadAttempts = 0;
|
||||
m_editFlags = 0;
|
||||
|
||||
m_learnVelocity= nullvec;
|
||||
m_learnPosition= nullvec;
|
||||
m_lastNode= nullvec;
|
||||
m_learnVelocity= nullptr;
|
||||
m_learnPosition= nullptr;
|
||||
m_lastNode= nullptr;
|
||||
|
||||
m_pathDisplayTime = 0.0f;
|
||||
m_arrowDisplayTime = 0.0f;
|
||||
|
|
@ -540,7 +540,7 @@ void BotGraph::add (int type, const Vector &pos) {
|
|||
Path *path = nullptr;
|
||||
|
||||
bool addNewNode = true;
|
||||
Vector newOrigin = pos == nullvec ? m_editor->v.origin : pos;
|
||||
Vector newOrigin = pos.empty () ? m_editor->v.origin : pos;
|
||||
|
||||
if (bots.hasBotsOnline ()) {
|
||||
bots.kickEveryone (true);
|
||||
|
|
@ -624,8 +624,8 @@ void BotGraph::add (int type, const Vector &pos) {
|
|||
path->origin = newOrigin;
|
||||
addToBucket (newOrigin, index);
|
||||
|
||||
path->start = nullvec;
|
||||
path->end = nullvec;
|
||||
path->start = nullptr;
|
||||
path->end = nullptr;
|
||||
|
||||
path->display = 0.0f;
|
||||
path->light = 0.0f;
|
||||
|
|
@ -634,7 +634,7 @@ void BotGraph::add (int type, const Vector &pos) {
|
|||
link.index = kInvalidNodeIndex;
|
||||
link.distance = 0;
|
||||
link.flags = 0;
|
||||
link.velocity = nullvec;
|
||||
link.velocity = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -804,7 +804,7 @@ void BotGraph::erase (int target) {
|
|||
link.index = kInvalidNodeIndex;
|
||||
link.flags = 0;
|
||||
link.distance = 0;
|
||||
link.velocity = nullvec;
|
||||
link.velocity = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1037,9 +1037,8 @@ void BotGraph::calculatePathRadius (int index) {
|
|||
|
||||
for (float scanDistance = 32.0f; scanDistance < 128.0f; scanDistance += 16.0f) {
|
||||
start = path.origin;
|
||||
auto null = nullvec;
|
||||
|
||||
direction = null.forward () * scanDistance;
|
||||
direction = Vector (0.0f, 0.0f, 0.0f).forward () * scanDistance;
|
||||
direction = direction.angles ();
|
||||
|
||||
path.radius = scanDistance;
|
||||
|
|
@ -1925,7 +1924,7 @@ void BotGraph::frame () {
|
|||
if (m_editor->v.button & IN_JUMP) {
|
||||
add (9);
|
||||
|
||||
m_timeJumpStarted = game.timebase ();
|
||||
m_timeJumpStarted = game.time ();
|
||||
m_endJumpPoint = true;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1933,7 +1932,7 @@ void BotGraph::frame () {
|
|||
m_learnPosition = m_editor->v.origin;
|
||||
}
|
||||
}
|
||||
else if (((m_editor->v.flags & FL_ONGROUND) || m_editor->v.movetype == MOVETYPE_FLY) && m_timeJumpStarted + 0.1f < game.timebase () && m_endJumpPoint) {
|
||||
else if (((m_editor->v.flags & FL_ONGROUND) || m_editor->v.movetype == MOVETYPE_FLY) && m_timeJumpStarted + 0.1f < game.time () && m_endJumpPoint) {
|
||||
add (10);
|
||||
|
||||
m_jumpLearnNode = false;
|
||||
|
|
@ -1946,7 +1945,7 @@ void BotGraph::frame () {
|
|||
// find the distance from the last used node
|
||||
float distance = (m_lastNode - m_editor->v.origin).lengthSq ();
|
||||
|
||||
if (distance > 16384.0f) {
|
||||
if (distance > cr::square (128.0f)) {
|
||||
// check that no other reachable nodes are nearby...
|
||||
for (const auto &path : m_paths) {
|
||||
if (isNodeReacheable (m_editor->v.origin, path.origin)) {
|
||||
|
|
@ -1981,7 +1980,7 @@ void BotGraph::frame () {
|
|||
nearestDistance = distance;
|
||||
}
|
||||
|
||||
if (path.display + 0.8f < game.timebase ()) {
|
||||
if (path.display + 0.8f < game.time ()) {
|
||||
float nodeHeight = 0.0f;
|
||||
|
||||
// check the node height
|
||||
|
|
@ -2045,7 +2044,7 @@ void BotGraph::frame () {
|
|||
game.drawLine (m_editor, path.origin - Vector (0, 0, nodeHalfHeight), path.origin - Vector (0, 0, nodeHalfHeight - nodeHeight * 0.75f), nodeWidth, 0, nodeColor, 250, 0, 10); // draw basic path
|
||||
game.drawLine (m_editor, path.origin - Vector (0, 0, nodeHalfHeight - nodeHeight * 0.75f), path.origin + Vector (0, 0, nodeHalfHeight), nodeWidth, 0, nodeFlagColor, 250, 0, 10); // draw additional path
|
||||
}
|
||||
path.display = game.timebase ();
|
||||
path.display = game.time ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2057,7 +2056,7 @@ void BotGraph::frame () {
|
|||
// draw arrow to a some importaint nodes
|
||||
if (exists (m_findWPIndex) || exists (m_cacheNodeIndex) || exists (m_facingAtIndex)) {
|
||||
// check for drawing code
|
||||
if (m_arrowDisplayTime + 0.5f < game.timebase ()) {
|
||||
if (m_arrowDisplayTime + 0.5f < game.time ()) {
|
||||
|
||||
// finding node - pink arrow
|
||||
if (m_findWPIndex != kInvalidNodeIndex) {
|
||||
|
|
@ -2073,7 +2072,7 @@ void BotGraph::frame () {
|
|||
if (m_facingAtIndex != kInvalidNodeIndex) {
|
||||
game.drawLine (m_editor, m_editor->v.origin, m_paths[m_facingAtIndex].origin, 10, 0, Color (255, 255, 255), 200, 0, 5, DrawLine::Arrow);
|
||||
}
|
||||
m_arrowDisplayTime = game.timebase ();
|
||||
m_arrowDisplayTime = game.time ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2081,8 +2080,8 @@ void BotGraph::frame () {
|
|||
auto &path = m_paths[nearestIndex];
|
||||
|
||||
// draw a paths, camplines and danger directions for nearest node
|
||||
if (nearestDistance <= 56.0f && m_pathDisplayTime <= game.timebase ()) {
|
||||
m_pathDisplayTime = game.timebase () + 1.0f;
|
||||
if (nearestDistance <= 56.0f && m_pathDisplayTime <= game.time ()) {
|
||||
m_pathDisplayTime = game.time () + 1.0f;
|
||||
|
||||
// draw the camplines
|
||||
if (path.flags & NodeFlag::Camp) {
|
||||
|
|
@ -2214,7 +2213,7 @@ void BotGraph::frame () {
|
|||
}
|
||||
|
||||
// draw entire message
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullvec, m_editor)
|
||||
MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, m_editor)
|
||||
.writeByte (TE_TEXTMESSAGE)
|
||||
.writeByte (4) // channel
|
||||
.writeShort (MessageWriter::fs16 (0.0f, 13.0f)) // x
|
||||
|
|
@ -2293,7 +2292,7 @@ bool BotGraph::checkNodes (bool teleportPlayer) {
|
|||
}
|
||||
|
||||
if (path.flags & NodeFlag::Camp) {
|
||||
if (path.end == nullvec) {
|
||||
if (path.end.empty ()) {
|
||||
ctrl.msg ("Node %d Camp-Endposition not set!", path.number);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2463,10 +2462,8 @@ bool BotGraph::isVisited (int index) {
|
|||
void BotGraph::addBasic () {
|
||||
// this function creates basic node types on map
|
||||
|
||||
edict_t *ent = nullptr;
|
||||
|
||||
// first of all, if map contains ladder points, create it
|
||||
while (!game.isNullEntity (ent = engfuncs.pfnFindEntityByString (ent, "classname", "func_ladder"))) {
|
||||
game.searchEntities ("classname", "func_ladder", [&] (edict_t *ent) {
|
||||
Vector ladderLeft = ent->v.absmin;
|
||||
Vector ladderRight = ent->v.absmax;
|
||||
ladderLeft.z = ladderRight.z;
|
||||
|
|
@ -2474,7 +2471,7 @@ void BotGraph::addBasic () {
|
|||
TraceResult tr;
|
||||
Vector up, down, front, back;
|
||||
|
||||
Vector diff = ((ladderLeft - ladderRight) ^ Vector (0.0f, 0.0f, 0.0f)).normalize () * 15.0f;
|
||||
const Vector &diff = ((ladderLeft - ladderRight) ^ Vector (0.0f, 0.0f, 0.0f)).normalize () * 15.0f;
|
||||
front = back = game.getAbsPos (ent);
|
||||
|
||||
front = front + diff; // front
|
||||
|
|
@ -2509,18 +2506,19 @@ void BotGraph::addBasic () {
|
|||
add (3, point);
|
||||
}
|
||||
m_isOnLadder = false;
|
||||
}
|
||||
|
||||
auto autoCreateForEntity = [](int type, const char *entity) {
|
||||
edict_t *ent = nullptr;
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
|
||||
while (!game.isNullEntity (ent = engfuncs.pfnFindEntityByString (ent, "classname", entity))) {
|
||||
auto autoCreateForEntity = [] (int type, const char *entity) {
|
||||
game.searchEntities ("classname", entity, [&] (edict_t *ent) {
|
||||
const Vector &pos = game.getAbsPos (ent);
|
||||
|
||||
if (graph.getNearestNoBuckets (pos, 50.0f) == kInvalidNodeIndex) {
|
||||
graph.add (type, pos);
|
||||
}
|
||||
}
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
};
|
||||
|
||||
autoCreateForEntity (0, "info_player_deathmatch"); // then terrortist spawnpoints
|
||||
|
|
@ -2583,7 +2581,7 @@ void BotGraph::setBombPos (bool reset, const Vector &pos) {
|
|||
// this function stores the bomb position as a vector
|
||||
|
||||
if (reset) {
|
||||
m_bombPos= nullvec;
|
||||
m_bombPos= nullptr;
|
||||
bots.setBombPlanted (false);
|
||||
|
||||
return;
|
||||
|
|
@ -2593,14 +2591,14 @@ void BotGraph::setBombPos (bool reset, const Vector &pos) {
|
|||
m_bombPos = pos;
|
||||
return;
|
||||
}
|
||||
edict_t *ent = nullptr;
|
||||
|
||||
while (!game.isNullEntity (ent = engfuncs.pfnFindEntityByString (ent, "classname", "grenade"))) {
|
||||
|
||||
game.searchEntities ("classname", "grenade", [&] (edict_t *ent) {
|
||||
if (strcmp (STRING (ent->v.model) + 9, "c4.mdl") == 0) {
|
||||
m_bombPos = game.getAbsPos (ent);
|
||||
break;
|
||||
return EntitySearchResult::Break;
|
||||
}
|
||||
}
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void BotGraph::startLearnJump () {
|
||||
|
|
@ -2760,7 +2758,7 @@ void BotGraph::unassignPath (int from, int to) {
|
|||
link.index = kInvalidNodeIndex;
|
||||
link.distance = 0;
|
||||
link.flags = 0;
|
||||
link.velocity = nullvec;
|
||||
link.velocity = nullptr;
|
||||
|
||||
setEditFlag (GraphEdit::On);
|
||||
m_hasChanged = true;
|
||||
|
|
|
|||
|
|
@ -109,32 +109,16 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
|
|||
// server is enabled. Here is a good place to do our own game session initialization, and
|
||||
// to register by the engine side the server commands we need to administrate our bots.
|
||||
|
||||
// register bot cvars
|
||||
game.registerCvars ();
|
||||
|
||||
// register logger
|
||||
logger.initialize (strings.format ("%slogs/yapb.log", graph.getDataDirectory (false)), [] (const char *msg) {
|
||||
game.print (msg);
|
||||
});
|
||||
|
||||
conf.initWeapons ();
|
||||
|
||||
// register server command(s)
|
||||
game.registerCmd ("yapb", BotControl::handleEngineCommands);
|
||||
game.registerCmd ("yb", BotControl::handleEngineCommands);
|
||||
|
||||
// set correct version string
|
||||
yb_version.set (strings.format ("%d.%d.%d", PRODUCT_VERSION_DWORD_INTERNAL, util.buildNumber ()));
|
||||
|
||||
// execute main config
|
||||
conf.loadMainConfig ();
|
||||
|
||||
// register fake metamod command handler if we not! under mm
|
||||
if (!(game.is (GameFlags::Metamod))) {
|
||||
game.registerCmd ("meta", [] () {
|
||||
game.print ("You're launched standalone version of yapb. Metamod is not installed or not enabled!");
|
||||
});
|
||||
}
|
||||
conf.adjustWeaponPrices ();
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
|
|
@ -177,14 +161,8 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
|
|||
if (!game.isNullEntity (pentTouched) && pentOther != game.getStartEntity ()) {
|
||||
auto bot = bots[pentTouched];
|
||||
|
||||
if (bot != nullptr && pentOther != bot->ent ()) {
|
||||
|
||||
if (util.isPlayer (pentOther)) {
|
||||
bot->avoidIncomingPlayers (pentOther);
|
||||
}
|
||||
else {
|
||||
bot->processBreakables (pentOther);
|
||||
}
|
||||
if (bot && pentOther != bot->ent () && !util.isPlayer (pentOther)) {
|
||||
bot->checkBreakable (pentOther);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -397,9 +375,6 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
|
|||
// for example if a new player joins the server, we should disconnect a bot, and if the
|
||||
// player population decreases, we should fill the server with other bots.
|
||||
|
||||
// run periodic update of bot states
|
||||
bots.frame ();
|
||||
|
||||
// update lightstyle animations
|
||||
illum.animateLight ();
|
||||
|
||||
|
|
@ -430,7 +405,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
|
|||
dllapi.pfnStartFrame ();
|
||||
|
||||
// run the bot ai
|
||||
bots.slowFrame ();
|
||||
bots.frame ();
|
||||
};
|
||||
|
||||
functionTable->pfnCmdStart = [] (const edict_t *player, usercmd_t *cmd, unsigned int random_seed) {
|
||||
|
|
@ -504,7 +479,8 @@ CR_EXPORT int GetEntityAPI2_Post (gamefuncs_t *table, int *) {
|
|||
// for the bots by the MOD side, remember). Post version called only by metamod.
|
||||
|
||||
// run the bot ai
|
||||
bots.slowFrame ();
|
||||
bots.frame ();
|
||||
|
||||
RETURN_META (MRES_IGNORED);
|
||||
};
|
||||
|
||||
|
|
@ -578,17 +554,19 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
|||
engfuncs.pfnLightStyle (style, val);
|
||||
};
|
||||
|
||||
functionTable->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) {
|
||||
// round starts in counter-strike 1.5
|
||||
if ((game.is (GameFlags::Legacy)) && strcmp (value, "info_map_parameters") == 0) {
|
||||
bots.initRound ();
|
||||
}
|
||||
if (game.is (GameFlags::Legacy)) {
|
||||
functionTable->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) {
|
||||
// round starts in counter-strike 1.5
|
||||
if (strcmp (value, "info_map_parameters") == 0) {
|
||||
bots.initRound ();
|
||||
}
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
RETURN_META_VALUE (MRES_IGNORED, static_cast <edict_t *> (nullptr));
|
||||
}
|
||||
return engfuncs.pfnFindEntityByString (edictStartSearchAfter, field, value);
|
||||
};
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
RETURN_META_VALUE (MRES_IGNORED, static_cast <edict_t *> (nullptr));
|
||||
}
|
||||
return engfuncs.pfnFindEntityByString (edictStartSearchAfter, field, value);
|
||||
};
|
||||
}
|
||||
|
||||
functionTable->pfnEmitSound = [] (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch) {
|
||||
// this function tells the engine that the entity pointed to by "entity", is emitting a sound
|
||||
|
|
@ -601,7 +579,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
|
|||
// SoundAttachToThreat() to bring the sound to the ears of the bots. Since bots have no client DLL
|
||||
// to handle this for them, such a job has to be done manually.
|
||||
|
||||
util.attachSoundsToClients (entity, sample, volume);
|
||||
util.listenNoise (entity, sample, volume);
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
RETURN_META (MRES_IGNORED);
|
||||
|
|
@ -863,7 +841,7 @@ CR_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMet
|
|||
return TRUE; // tell metamod this plugin looks safe
|
||||
}
|
||||
|
||||
CR_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) {
|
||||
CR_EXPORT int Meta_Attach (PLUG_LOADTIME now, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) {
|
||||
// this function is called when metamod attempts to load the plugin. Since it's the place
|
||||
// where we can tell if the plugin will be allowed to run or not, we wait until here to make
|
||||
// our initialization stuff, like registering CVARs and dedicated server commands.
|
||||
|
|
@ -880,6 +858,11 @@ CR_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_g
|
|||
nullptr, // pfnGetEngineFunctions_Post ()
|
||||
};
|
||||
|
||||
if (now > Plugin_info.loadable) {
|
||||
logger.error ("%s: plugin NOT attaching (can't load plugin right now)", Plugin_info.name);
|
||||
return FALSE; // returning FALSE prevents metamod from attaching this plugin
|
||||
}
|
||||
|
||||
// keep track of the pointers to engine function tables metamod gives us
|
||||
gpMetaGlobals = pMGlobals;
|
||||
memcpy (functionTable, &metamodFunctionTable, sizeof (metamod_funcs_t));
|
||||
|
|
@ -888,10 +871,14 @@ CR_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_g
|
|||
return TRUE; // returning true enables metamod to attach this plugin
|
||||
}
|
||||
|
||||
CR_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) {
|
||||
CR_EXPORT int Meta_Detach (PLUG_LOADTIME now, PL_UNLOAD_REASON reason) {
|
||||
// this function is called when metamod unloads the plugin. A basic check is made in order
|
||||
// to prevent unloading the plugin if its processing should not be interrupted.
|
||||
|
||||
if (now > Plugin_info.unloadable && reason != PNL_CMD_FORCED) {
|
||||
logger.error ("%s: plugin NOT detaching (can't unload plugin right now)", Plugin_info.name);
|
||||
return FALSE; // returning FALSE prevents metamod from unloading this plugin
|
||||
}
|
||||
bots.kickEveryone (true); // kick all bots off this server
|
||||
|
||||
// save collected experience on shutdown
|
||||
|
|
@ -250,23 +250,15 @@ Bot *BotManager::findAliveBot () {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void BotManager::slowFrame () {
|
||||
// this function calls showframe function for all available at call moment bots
|
||||
|
||||
for (const auto &bot : m_bots) {
|
||||
bot->slowFrame ();
|
||||
}
|
||||
}
|
||||
|
||||
void BotManager::frame () {
|
||||
// this function calls periodic frame function for all available at call moment bots
|
||||
// this function calls showframe function for all available at call moment bots
|
||||
|
||||
for (const auto &bot : m_bots) {
|
||||
bot->frame ();
|
||||
}
|
||||
|
||||
// select leader each team somewhere in round start
|
||||
if (m_timeRoundStart + 5.0f > game.timebase () && m_timeRoundStart + 10.0f < game.timebase ()) {
|
||||
if (m_timeRoundStart + 5.0f > game.time () && m_timeRoundStart + 10.0f < game.time ()) {
|
||||
for (int team = 0; team < kGameTeamNum; ++team) {
|
||||
selectLeaders (team, false);
|
||||
}
|
||||
|
|
@ -319,7 +311,7 @@ void BotManager::maintainQuota () {
|
|||
}
|
||||
|
||||
// bot's creation update
|
||||
if (!m_creationTab.empty () && m_maintainTime < game.timebase ()) {
|
||||
if (!m_creationTab.empty () && m_maintainTime < game.time ()) {
|
||||
const CreateQueue &last = m_creationTab.pop ();
|
||||
const BotCreateResult callResult = create (last.name, last.difficulty, last.personality, last.team, last.member);
|
||||
|
||||
|
|
@ -342,11 +334,11 @@ void BotManager::maintainQuota () {
|
|||
m_creationTab.clear ();
|
||||
yb_quota.set (getBotCount ());
|
||||
}
|
||||
m_maintainTime = game.timebase () + 0.10f;
|
||||
m_maintainTime = game.time () + 0.10f;
|
||||
}
|
||||
|
||||
// now keep bot number up to date
|
||||
if (m_quotaMaintainTime > game.timebase ()) {
|
||||
if (m_quotaMaintainTime > game.time ()) {
|
||||
return;
|
||||
}
|
||||
yb_quota.set (cr::clamp <int> (yb_quota.int_ (), 0, game.maxClients ()));
|
||||
|
|
@ -410,7 +402,7 @@ void BotManager::maintainQuota () {
|
|||
kickRandom (false, Team::Unassigned);
|
||||
}
|
||||
}
|
||||
m_quotaMaintainTime = game.timebase () + 0.40f;
|
||||
m_quotaMaintainTime = game.time () + 0.40f;
|
||||
}
|
||||
|
||||
void BotManager::reset () {
|
||||
|
|
@ -467,8 +459,8 @@ void BotManager::decrementQuota (int by) {
|
|||
}
|
||||
|
||||
void BotManager::initQuota () {
|
||||
m_maintainTime = game.timebase () + yb_join_delay.float_ ();
|
||||
m_quotaMaintainTime = game.timebase () + yb_join_delay.float_ ();
|
||||
m_maintainTime = game.time () + yb_join_delay.float_ ();
|
||||
m_quotaMaintainTime = game.time () + yb_join_delay.float_ ();
|
||||
|
||||
m_creationTab.clear ();
|
||||
}
|
||||
|
|
@ -855,8 +847,8 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) {
|
|||
m_difficulty = cr::clamp (difficulty, 0, 4);
|
||||
m_basePing = rg.int_ (7, 14);
|
||||
|
||||
m_lastCommandTime = game.timebase () - 0.1f;
|
||||
m_frameInterval = game.timebase ();
|
||||
m_lastCommandTime = game.time () - 0.1f;
|
||||
m_frameInterval = game.time ();
|
||||
m_slowFrameTimestamp = 0.0f;
|
||||
|
||||
// stuff from jk_botti
|
||||
|
|
@ -892,7 +884,7 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) {
|
|||
// copy them over to the temp level variables
|
||||
m_agressionLevel = m_baseAgressionLevel;
|
||||
m_fearLevel = m_baseFearLevel;
|
||||
m_nextEmotionUpdate = game.timebase () + 0.5f;
|
||||
m_nextEmotionUpdate = game.time () + 0.5f;
|
||||
|
||||
// just to be sure
|
||||
m_actMessageIndex = 0;
|
||||
|
|
@ -906,7 +898,7 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) {
|
|||
}
|
||||
|
||||
float Bot::getFrameInterval () {
|
||||
return cr::fzero (m_thinkInterval) ? m_frameInterval : m_thinkInterval;
|
||||
return m_frameInterval;
|
||||
}
|
||||
|
||||
int BotManager::getHumansCount (bool ignoreSpectators) {
|
||||
|
|
@ -977,10 +969,10 @@ void BotManager::handleDeath (edict_t *killer, edict_t *victim) {
|
|||
for (const auto ¬ify : bots) {
|
||||
if (notify->m_notKilled && killerTeam == notify->m_team && killerTeam != victimTeam && killer != notify->ent () && notify->seesEntity (victim->v.origin)) {
|
||||
if (!(killer->v.flags & FL_FAKECLIENT)) {
|
||||
notify->processChatterMessage ("#Bot_NiceShotCommander");
|
||||
notify->handleChatter ("#Bot_NiceShotCommander");
|
||||
}
|
||||
else {
|
||||
notify->processChatterMessage ("#Bot_NiceShotPall");
|
||||
notify->handleChatter ("#Bot_NiceShotPall");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -991,9 +983,9 @@ void BotManager::handleDeath (edict_t *killer, edict_t *victim) {
|
|||
|
||||
// notice nearby to victim teammates, that attacker is near
|
||||
for (const auto ¬ify : bots) {
|
||||
if (notify->m_seeEnemyTime + 2.0f < game.timebase () && notify->m_notKilled && notify->m_team == victimTeam && game.isNullEntity (notify->m_enemy) && killerTeam != victimTeam && util.isVisible (killer->v.origin, notify->ent ())) {
|
||||
if (notify->m_seeEnemyTime + 2.0f < game.time () && notify->m_notKilled && notify->m_team == victimTeam && game.isNullEntity (notify->m_enemy) && killerTeam != victimTeam && util.isVisible (killer->v.origin, notify->ent ())) {
|
||||
notify->m_actualReactionTime = 0.0f;
|
||||
notify->m_seeEnemyTime = game.timebase ();
|
||||
notify->m_seeEnemyTime = game.time ();
|
||||
notify->m_enemy = killer;
|
||||
notify->m_lastEnemy = killer;
|
||||
notify->m_lastEnemyOrigin = killer->v.origin;
|
||||
|
|
@ -1032,11 +1024,11 @@ void Bot::newRound () {
|
|||
clearSearchNodes ();
|
||||
clearRoute ();
|
||||
|
||||
m_pathOrigin= nullvec;
|
||||
m_destOrigin= nullvec;
|
||||
m_pathOrigin= nullptr;
|
||||
m_destOrigin= nullptr;
|
||||
m_path = nullptr;
|
||||
m_currentTravelFlags = 0;
|
||||
m_desiredVelocity= nullvec;
|
||||
m_desiredVelocity= nullptr;
|
||||
m_currentNodeIndex = kInvalidNodeIndex;
|
||||
m_prevGoalIndex = kInvalidNodeIndex;
|
||||
m_chosenGoalIndex = kInvalidNodeIndex;
|
||||
|
|
@ -1053,13 +1045,10 @@ void Bot::newRound () {
|
|||
m_oldButtons = pev->button;
|
||||
m_rechoiceGoalCount = 0;
|
||||
|
||||
m_avoid = nullptr;
|
||||
m_avoidTime = 0.0f;
|
||||
|
||||
for (i = 0; i < 5; ++i) {
|
||||
m_previousNodes[i] = kInvalidNodeIndex;
|
||||
}
|
||||
m_navTimeset = game.timebase ();
|
||||
m_navTimeset = game.time ();
|
||||
m_team = game.getTeam (ent ());
|
||||
m_isVIP = false;
|
||||
|
||||
|
|
@ -1093,9 +1082,9 @@ void Bot::newRound () {
|
|||
m_minSpeed = 260.0f;
|
||||
m_prevSpeed = 0.0f;
|
||||
m_prevOrigin = Vector (kInfiniteDistance, kInfiniteDistance, kInfiniteDistance);
|
||||
m_prevTime = game.timebase ();
|
||||
m_lookUpdateTime = game.timebase ();
|
||||
m_aimErrorTime = game.timebase ();
|
||||
m_prevTime = game.time ();
|
||||
m_lookUpdateTime = game.time ();
|
||||
m_aimErrorTime = game.time ();
|
||||
|
||||
m_viewDistance = 4096.0f;
|
||||
m_maxViewDistance = 4096.0f;
|
||||
|
|
@ -1106,7 +1095,7 @@ void Bot::newRound () {
|
|||
m_itemCheckTime = 0.0f;
|
||||
|
||||
m_breakableEntity = nullptr;
|
||||
m_breakableOrigin= nullvec;
|
||||
m_breakableOrigin= nullptr;
|
||||
m_timeDoorOpen = 0.0f;
|
||||
|
||||
resetCollision ();
|
||||
|
|
@ -1115,7 +1104,7 @@ void Bot::newRound () {
|
|||
m_enemy = nullptr;
|
||||
m_lastVictim = nullptr;
|
||||
m_lastEnemy = nullptr;
|
||||
m_lastEnemyOrigin= nullvec;
|
||||
m_lastEnemyOrigin= nullptr;
|
||||
m_trackingEdict = nullptr;
|
||||
m_timeNextTracking = 0.0f;
|
||||
|
||||
|
|
@ -1139,9 +1128,9 @@ void Bot::newRound () {
|
|||
m_aimFlags = 0;
|
||||
m_liftState = 0;
|
||||
|
||||
m_aimLastError= nullvec;
|
||||
m_position= nullvec;
|
||||
m_liftTravelPos= nullvec;
|
||||
m_aimLastError= nullptr;
|
||||
m_position= nullptr;
|
||||
m_liftTravelPos= nullptr;
|
||||
|
||||
setIdealReactionTimers (true);
|
||||
|
||||
|
|
@ -1157,8 +1146,8 @@ void Bot::newRound () {
|
|||
m_reloadState = Reload::None;
|
||||
|
||||
m_reloadCheckTime = 0.0f;
|
||||
m_shootTime = game.timebase ();
|
||||
m_playerTargetTime = game.timebase ();
|
||||
m_shootTime = game.time ();
|
||||
m_playerTargetTime = game.time ();
|
||||
m_firePause = 0.0f;
|
||||
m_timeLastFired = 0.0f;
|
||||
|
||||
|
|
@ -1174,7 +1163,7 @@ void Bot::newRound () {
|
|||
m_jumpFinished = false;
|
||||
m_isStuck = false;
|
||||
|
||||
m_sayTextBuffer.timeNextChat = game.timebase ();
|
||||
m_sayTextBuffer.timeNextChat = game.time ();
|
||||
m_sayTextBuffer.entityIndex = -1;
|
||||
m_sayTextBuffer.sayText.clear ();
|
||||
|
||||
|
|
@ -1189,10 +1178,10 @@ void Bot::newRound () {
|
|||
m_currentWeapon = 0;
|
||||
}
|
||||
m_flashLevel = 100.0f;
|
||||
m_checkDarkTime = game.timebase ();
|
||||
m_checkDarkTime = game.time ();
|
||||
|
||||
m_knifeAttackTime = game.timebase () + rg.float_ (1.3f, 2.6f);
|
||||
m_nextBuyTime = game.timebase () + rg.float_ (0.6f, 2.0f);
|
||||
m_knifeAttackTime = game.time () + rg.float_ (1.3f, 2.6f);
|
||||
m_nextBuyTime = game.time () + rg.float_ (0.6f, 2.0f);
|
||||
|
||||
m_buyPending = false;
|
||||
m_inBombZone = false;
|
||||
|
|
@ -1217,9 +1206,9 @@ void Bot::newRound () {
|
|||
m_defendHostage = false;
|
||||
m_headedTime = 0.0f;
|
||||
|
||||
m_timeLogoSpray = game.timebase () + rg.float_ (5.0f, 30.0f);
|
||||
m_spawnTime = game.timebase ();
|
||||
m_lastChatTime = game.timebase ();
|
||||
m_timeLogoSpray = game.time () + rg.float_ (5.0f, 30.0f);
|
||||
m_spawnTime = game.time ();
|
||||
m_lastChatTime = game.time ();
|
||||
|
||||
m_timeCamping = 0.0f;
|
||||
m_campDirection = 0;
|
||||
|
|
@ -1227,7 +1216,7 @@ void Bot::newRound () {
|
|||
m_campButtons = 0;
|
||||
|
||||
m_soundUpdateTime = 0.0f;
|
||||
m_heardSoundTime = game.timebase ();
|
||||
m_heardSoundTime = game.time ();
|
||||
|
||||
// clear its message queue
|
||||
for (i = 0; i < 32; ++i) {
|
||||
|
|
@ -1243,7 +1232,8 @@ void Bot::newRound () {
|
|||
if (rg.chance (50)) {
|
||||
pushChatterMessage (Chatter::NewRound);
|
||||
}
|
||||
m_thinkInterval = game.is (GameFlags::Legacy | GameFlags::Xash3D) ? 0.0f : (1.0f / cr::clamp (yb_think_fps.float_ (), 30.0f, 90.0f)) * rg.float_ (0.95f, 1.05f);
|
||||
m_updateInterval = game.is (GameFlags::Legacy | GameFlags::Xash3D) ? 0.0f : (1.0f / cr::clamp (yb_think_fps.float_ (), 30.0f, 60.0f));
|
||||
m_viewUpdateInterval = 1.0f / 30.0f;
|
||||
}
|
||||
|
||||
void Bot::kill () {
|
||||
|
|
@ -1344,15 +1334,6 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
|||
}
|
||||
|
||||
if (plat.caseStrMatch (cmd, "say") || plat.caseStrMatch (cmd, "say_team")) {
|
||||
if (strcmp (arg, "dropme") == 0 || strcmp (arg, "dropc4") == 0) {
|
||||
Bot *bot = nullptr;
|
||||
|
||||
if (util.findNearestPlayer (reinterpret_cast <void **> (&bot), ent, 300.0f, true, true, true)) {
|
||||
bot->dropWeaponForUser (ent, strings.isEmpty (strstr (arg, "c4")) ? false : true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool alive = util.isAlive (ent);
|
||||
int team = -1;
|
||||
|
||||
|
|
@ -1373,7 +1354,7 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
|||
continue;
|
||||
}
|
||||
target->m_sayTextBuffer.sayText = engfuncs.pfnCmd_Args ();
|
||||
target->m_sayTextBuffer.timeNextChat = game.timebase () + target->m_sayTextBuffer.chatDelay;
|
||||
target->m_sayTextBuffer.timeNextChat = game.time () + target->m_sayTextBuffer.chatDelay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1396,7 +1377,7 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
|||
}
|
||||
}
|
||||
}
|
||||
bots.setLastRadioTimestamp (target.team, game.timebase ());
|
||||
bots.setLastRadioTimestamp (target.team, game.time ());
|
||||
}
|
||||
target.radio = 0;
|
||||
}
|
||||
|
|
@ -1419,59 +1400,61 @@ void BotManager::notifyBombDefuse () {
|
|||
}
|
||||
|
||||
void BotManager::updateActiveGrenade () {
|
||||
if (m_grenadeUpdateTime > game.timebase ()) {
|
||||
if (m_grenadeUpdateTime > game.time ()) {
|
||||
return;
|
||||
}
|
||||
edict_t *grenade = nullptr;
|
||||
|
||||
// clear previously stored grenades
|
||||
m_activeGrenades.clear ();
|
||||
m_activeGrenades.clear (); // clear previously stored grenades
|
||||
|
||||
// search the map for any type of grenade
|
||||
while (!game.isNullEntity (grenade = engfuncs.pfnFindEntityByString (grenade, "classname", "grenade"))) {
|
||||
game.searchEntities ("classname", "grenade", [&] (edict_t *e) {
|
||||
// do not count c4 as a grenade
|
||||
if (strcmp (STRING (grenade->v.model) + 9, "c4.mdl") == 0) {
|
||||
continue;
|
||||
if (strcmp (STRING (e->v.model) + 9, "c4.mdl") == 0) {
|
||||
return EntitySearchResult::Continue;
|
||||
}
|
||||
m_activeGrenades.push (grenade);
|
||||
}
|
||||
m_grenadeUpdateTime = game.timebase () + 0.213f;
|
||||
m_activeGrenades.push (e);
|
||||
|
||||
// continue iteration
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
m_grenadeUpdateTime = game.time () + 0.25f;
|
||||
}
|
||||
|
||||
void BotManager::updateIntrestingEntities () {
|
||||
if (m_entityUpdateTime > game.timebase ()) {
|
||||
if (m_entityUpdateTime > game.time ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clear previously stored entities
|
||||
m_intrestingEntities.clear ();
|
||||
|
||||
// search the map for entities
|
||||
for (int i = kGameMaxPlayers - 1; i < globals->maxEntities; ++i) {
|
||||
auto ent = game.entityOfIndex (i);
|
||||
// search the map for any type of grenade
|
||||
game.searchEntities (nullptr, kInfiniteDistance, [&] (edict_t *e) {
|
||||
auto classname = STRING (e->v.classname);
|
||||
|
||||
// only valid drawn entities
|
||||
if (game.isNullEntity (ent) || ent->free || ent->v.classname == 0 || (ent->v.effects & EF_NODRAW)) {
|
||||
continue;
|
||||
}
|
||||
auto classname = STRING (ent->v.classname);
|
||||
|
||||
// search for grenades, weaponboxes, weapons, items and armoury entities
|
||||
if (strncmp ("weapon", classname, 6) == 0 || strncmp ("grenade", classname, 7) == 0 || strncmp ("item", classname, 4) == 0 || strncmp ("armoury", classname, 7) == 0) {
|
||||
m_intrestingEntities.push (ent);
|
||||
m_intrestingEntities.push (e);
|
||||
}
|
||||
|
||||
// pickup some csdm stuff if we're running csdm
|
||||
if (game.mapIs (MapFlags::HostageRescue) && strncmp ("hostage", classname, 7) == 0) {
|
||||
m_intrestingEntities.push (ent);
|
||||
m_intrestingEntities.push (e);
|
||||
}
|
||||
|
||||
|
||||
// add buttons
|
||||
if (game.mapIs (MapFlags::HasButtons) && strncmp ("func_button", classname, 11) == 0) {
|
||||
m_intrestingEntities.push (e);
|
||||
}
|
||||
|
||||
// pickup some csdm stuff if we're running csdm
|
||||
if (game.is (GameFlags::CSDM) && strncmp ("csdm", classname, 4) == 0) {
|
||||
m_intrestingEntities.push (ent);
|
||||
m_intrestingEntities.push (e);
|
||||
}
|
||||
}
|
||||
m_entityUpdateTime = game.timebase () + 0.5f;
|
||||
|
||||
// continue iteration
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
m_entityUpdateTime = game.time () + 0.5f;
|
||||
}
|
||||
|
||||
void BotManager::selectLeaders (int team, bool reset) {
|
||||
|
|
@ -1606,14 +1589,14 @@ void BotManager::initRound () {
|
|||
graph.updateGlobalPractice (); // update experience data on round start
|
||||
|
||||
// calculate the round mid/end in world time
|
||||
m_timeRoundStart = game.timebase () + mp_freezetime.float_ ();
|
||||
m_timeRoundStart = game.time () + mp_freezetime.float_ ();
|
||||
m_timeRoundMid = m_timeRoundStart + mp_roundtime.float_ () * 60.0f * 0.5f;
|
||||
m_timeRoundEnd = m_timeRoundStart + mp_roundtime.float_ () * 60.0f;
|
||||
}
|
||||
|
||||
void BotManager::setBombPlanted (bool isPlanted) {
|
||||
if (isPlanted) {
|
||||
m_timeBombPlanted = game.timebase ();
|
||||
m_timeBombPlanted = game.time ();
|
||||
}
|
||||
m_bombPlanted = isPlanted;
|
||||
}
|
||||
|
|
@ -1680,6 +1663,12 @@ void BotConfig::loadMainConfig () {
|
|||
auto value = const_cast <char *> (keyval[1].trim ().trim ("\"").trim ().chars ());
|
||||
|
||||
if (needsToIgnoreVar (ignore, key) && !plat.caseStrMatch (value, cvar->string)) {
|
||||
|
||||
// preserve quota number if it's zero
|
||||
if (plat.caseStrMatch (cvar->name, "yb_quota") && yb_quota.int_ () <= 0) {
|
||||
engfuncs.pfnCvar_DirectSet (cvar, value);
|
||||
continue;
|
||||
}
|
||||
game.print ("Bot CVAR '%s' differs from the stored in the config (%s/%s). Ignoring.", cvar->name, cvar->string, value);
|
||||
|
||||
// ensure cvar will have old value
|
||||
|
|
@ -2183,6 +2172,8 @@ void BotConfig::clearUsedName (Bot *bot) {
|
|||
}
|
||||
|
||||
void BotConfig::initWeapons () {
|
||||
m_weapons.clear ();
|
||||
|
||||
// fill array with available weapons
|
||||
m_weapons.emplace (Weapon::Knife, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, true );
|
||||
m_weapons.emplace (Weapon::USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, 12, false );
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ void MessageDispatcher::netMsgTextMsg () {
|
|||
auto notify = bots.findAliveBot ();
|
||||
|
||||
if (notify && notify->m_notKilled) {
|
||||
notify->processChatterMessage (m_args[msg].chars_);
|
||||
notify->handleChatter (m_args[msg].chars_);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +52,14 @@ void MessageDispatcher::netMsgTextMsg () {
|
|||
else if (cached & TextMsgCache::RestartRound) {
|
||||
bots.updateTeamEconomics (Team::CT, true);
|
||||
bots.updateTeamEconomics (Team::Terrorist, true);
|
||||
|
||||
extern ConVar mp_startmoney;
|
||||
|
||||
// set balance for all players
|
||||
bots.forEach ([] (Bot *bot) {
|
||||
bot->m_moneyAmount = mp_startmoney.int_ ();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else if (cached & TextMsgCache::TerroristWin) {
|
||||
bots.setLastWinner (Team::Terrorist); // update last winner for economics
|
||||
|
|
@ -157,7 +165,7 @@ void MessageDispatcher::netMsgCurWeapon () {
|
|||
|
||||
// ammo amount decreased ? must have fired a bullet...
|
||||
if (m_args[id].long_ == m_bot->m_currentWeapon && m_bot->m_ammoInClip[m_args[id].long_] > m_args[clip].long_) {
|
||||
m_bot->m_timeLastFired = game.timebase (); // remember the last bullet time
|
||||
m_bot->m_timeLastFired = game.time (); // remember the last bullet time
|
||||
}
|
||||
m_bot->m_ammoInClip[m_args[id].long_] = m_args[clip].long_;
|
||||
}
|
||||
|
|
@ -201,7 +209,7 @@ void MessageDispatcher::netMsgDamage () {
|
|||
|
||||
// handle damage if any
|
||||
if (m_args[armor].long_ > 0 || m_args[health].long_) {
|
||||
m_bot->processDamage (m_bot->pev->dmg_inflictor, m_args[health].long_, m_args[armor].long_, m_args[bits].long_);
|
||||
m_bot->takeDamage (m_bot->pev->dmg_inflictor, m_args[health].long_, m_args[armor].long_, m_args[bits].long_);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,7 +245,7 @@ void MessageDispatcher::netMsgStatusIcon () {
|
|||
m_bot->m_inBuyZone = (m_args[enabled].long_ != 0);
|
||||
|
||||
// try to equip in buyzone
|
||||
m_bot->processBuyzoneEntering (BuyState::PrimaryWeapon);
|
||||
m_bot->enteredBuyZone (BuyState::PrimaryWeapon);
|
||||
}
|
||||
else if (cached & StatusIconCache::VipSafety) {
|
||||
m_bot->m_inVIPZone = (m_args[enabled].long_ != 0);
|
||||
|
|
@ -278,7 +286,7 @@ void MessageDispatcher::netMsgScreenFade () {
|
|||
|
||||
// screen completely faded ?
|
||||
if (m_args[r].long_ >= 255 && m_args[g].long_ >= 255 && m_args[b].long_ >= 255 && m_args[alpha].long_ > 170) {
|
||||
m_bot->processBlind (m_args[alpha].long_);
|
||||
m_bot->takeBlind (m_args[alpha].long_);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -56,6 +56,17 @@ BotUtils::BotUtils () {
|
|||
m_tags.emplace ("(", ")");
|
||||
m_tags.emplace (")", "(");
|
||||
|
||||
// register noise cache
|
||||
m_noiseCache["player/bhit"] = Noise::NeedHandle | Noise::HitFall;
|
||||
m_noiseCache["player/head"] = Noise::NeedHandle | Noise::HitFall;
|
||||
m_noiseCache["items/gunpi"] = Noise::NeedHandle | Noise::Pickup;
|
||||
m_noiseCache["items/9mmcl"] = Noise::NeedHandle | Noise::Ammo;
|
||||
m_noiseCache["weapons/zoo"] = Noise::NeedHandle | Noise::Zoom;
|
||||
m_noiseCache["hostage/hos"] = Noise::NeedHandle | Noise::Hostage;
|
||||
m_noiseCache["debris/bust"] = Noise::NeedHandle | Noise::Broke;
|
||||
m_noiseCache["debris/bust"] = Noise::NeedHandle | Noise::Broke;
|
||||
m_noiseCache["doors/doorm"] = Noise::NeedHandle | Noise::Door;
|
||||
|
||||
m_clients.resize (kGameMaxPlayers + 1);
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +245,7 @@ void BotUtils::checkWelcome () {
|
|||
auto receiveEntity = game.getLocalEntity ();
|
||||
|
||||
if (isAlive (receiveEntity) && m_welcomeReceiveTime < 1.0 && needToSendMsg) {
|
||||
m_welcomeReceiveTime = game.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing
|
||||
m_welcomeReceiveTime = game.time () + 4.0f; // receive welcome message in four seconds after game has commencing
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -243,11 +254,11 @@ void BotUtils::checkWelcome () {
|
|||
game.serverCommand ("speak \"%s\"", m_sentences.random ().chars ());
|
||||
}
|
||||
|
||||
MessageWriter (MSG_ONE, msgs.id (NetMsg::TextMsg), nullvec, receiveEntity)
|
||||
MessageWriter (MSG_ONE, msgs.id (NetMsg::TextMsg), nullptr, receiveEntity)
|
||||
.writeByte (HUD_PRINTTALK)
|
||||
.writeString (strings.format ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL));
|
||||
|
||||
MessageWriter (MSG_ONE, SVC_TEMPENTITY, nullvec, receiveEntity)
|
||||
MessageWriter (MSG_ONE, SVC_TEMPENTITY, nullptr, receiveEntity)
|
||||
.writeByte (TE_TEXTMESSAGE)
|
||||
.writeByte (1)
|
||||
.writeShort (MessageWriter::fs16 (-1.0f, 13.0f))
|
||||
|
|
@ -312,91 +323,96 @@ bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDist
|
|||
return true;
|
||||
}
|
||||
|
||||
void BotUtils::attachSoundsToClients (edict_t *ent, const char *sample, float volume) {
|
||||
// this function called by the sound hooking code (in emit_sound) enters the played sound into
|
||||
// the array associated with the entity
|
||||
void BotUtils::listenNoise (edict_t *ent, const String &sample, float volume) {
|
||||
// this function called by the sound hooking code (in emit_sound) enters the played sound into the array associated with the entity
|
||||
|
||||
if (game.isNullEntity (ent) || strings.isEmpty (sample)) {
|
||||
if (game.isNullEntity (ent) || sample.empty ()) {
|
||||
return;
|
||||
}
|
||||
const Vector &origin = game.getAbsPos (ent);
|
||||
|
||||
// something wrong with sound...
|
||||
if (origin.empty ()) {
|
||||
return;
|
||||
}
|
||||
int index = game.indexOfPlayer (ent);
|
||||
auto noise = m_noiseCache[sample.substr (0, 11)];
|
||||
|
||||
if (index < 0 || index >= game.maxClients ()) {
|
||||
float nearestDistance = kInfiniteDistance;
|
||||
// we're not handling theese
|
||||
if (!(noise & Noise::NeedHandle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find nearest player to sound origin
|
||||
auto findNearbyClient = [&origin] () {
|
||||
float nearest = kInfiniteDistance;
|
||||
Client *result = nullptr;
|
||||
|
||||
// loop through all players
|
||||
for (int i = 0; i < game.maxClients (); ++i) {
|
||||
const Client &client = m_clients[i];
|
||||
|
||||
for (auto &client : util.getClients ()) {
|
||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) {
|
||||
continue;
|
||||
}
|
||||
float distance = (client.origin - origin).length ();
|
||||
auto distance = (client.origin - origin).lengthSq ();
|
||||
|
||||
// now find nearest player
|
||||
if (distance < nearestDistance) {
|
||||
index = i;
|
||||
nearestDistance = distance;
|
||||
if (distance < nearest) {
|
||||
result = &client;
|
||||
nearest = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
auto client = findNearbyClient ();
|
||||
|
||||
// in case of worst case
|
||||
if (index < 0 || index >= game.maxClients ()) {
|
||||
// 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 wasn't found
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
Client &client = m_clients[index];
|
||||
|
||||
if (strncmp ("player/bhit_flesh", sample, 17) == 0 || strncmp ("player/headshot", sample, 15) == 0) {
|
||||
// hit/fall sound?
|
||||
client.hearingDistance = 768.0f * volume;
|
||||
client.timeSoundLasting = game.timebase () + 0.5f;
|
||||
client.sound = origin;
|
||||
// hit/fall sound?
|
||||
if (noise & Noise::HitFall) {
|
||||
registerNoise (768.0f, 0.52f);
|
||||
}
|
||||
else if (strncmp ("items/gunpickup", sample, 15) == 0) {
|
||||
// weapon pickup?
|
||||
client.hearingDistance = 768.0f * volume;
|
||||
client.timeSoundLasting = game.timebase () + 0.5f;
|
||||
client.sound = origin;
|
||||
|
||||
// weapon pickup?
|
||||
else if (noise & Noise::Pickup) {
|
||||
registerNoise (768.0f, 0.45f);
|
||||
}
|
||||
else if (strncmp ("weapons/zoom", sample, 12) == 0) {
|
||||
// sniper zooming?
|
||||
client.hearingDistance = 512.0f * volume;
|
||||
client.timeSoundLasting = game.timebase () + 0.1f;
|
||||
client.sound = origin;
|
||||
|
||||
// sniper zooming?
|
||||
else if (noise & Noise::Zoom) {
|
||||
registerNoise (512.0f, 0.10f);
|
||||
}
|
||||
else if (strncmp ("items/9mmclip", sample, 13) == 0) {
|
||||
// ammo pickup?
|
||||
client.hearingDistance = 512.0f * volume;
|
||||
client.timeSoundLasting = game.timebase () + 0.1f;
|
||||
client.sound = origin;
|
||||
|
||||
// ammo pickup?
|
||||
else if (noise & Noise::Ammo) {
|
||||
registerNoise (512.0f, 0.25f);
|
||||
}
|
||||
else if (strncmp ("hostage/hos", sample, 11) == 0) {
|
||||
// CT used hostage?
|
||||
client.hearingDistance = 1024.0f * volume;
|
||||
client.timeSoundLasting = game.timebase () + 5.0f;
|
||||
client.sound = origin;
|
||||
|
||||
// ct used hostage?
|
||||
else if (noise & Noise::Hostage) {
|
||||
registerNoise (1024.0f, 5.00f);
|
||||
}
|
||||
else if (strncmp ("debris/bustmetal", sample, 16) == 0 || strncmp ("debris/bustglass", sample, 16) == 0) {
|
||||
// broke something?
|
||||
client.hearingDistance = 1024.0f * volume;
|
||||
client.timeSoundLasting = game.timebase () + 2.0f;
|
||||
client.sound = origin;
|
||||
|
||||
// broke something?
|
||||
else if (noise & Noise::Broke) {
|
||||
registerNoise (1024.0f, 2.00f);
|
||||
}
|
||||
else if (strncmp ("doors/doormove", sample, 14) == 0) {
|
||||
// someone opened a door
|
||||
client.hearingDistance = 1024.0f * volume;
|
||||
client.timeSoundLasting = game.timebase () + 3.0f;
|
||||
client.sound = origin;
|
||||
|
||||
// someone opened a door
|
||||
else if (noise & Noise::Door) {
|
||||
registerNoise (1024.0f, 3.00f);
|
||||
}
|
||||
}
|
||||
|
||||
void BotUtils::simulateSoundUpdates (int playerIndex) {
|
||||
void BotUtils::simulateNoise (int playerIndex) {
|
||||
// this function tries to simulate playing of sounds to let the bots hear sounds which aren't
|
||||
// captured through server sound hooking
|
||||
|
||||
|
|
@ -407,27 +423,28 @@ void BotUtils::simulateSoundUpdates (int playerIndex) {
|
|||
|
||||
float hearDistance = 0.0f;
|
||||
float timeSound = 0.0f;
|
||||
auto buttons = client.ent->v.button | client.ent->v.oldbuttons;
|
||||
|
||||
if (client.ent->v.oldbuttons & IN_ATTACK) // pressed attack button?
|
||||
if (buttons & IN_ATTACK) // pressed attack button?
|
||||
{
|
||||
hearDistance = 2048.0f;
|
||||
timeSound = game.timebase () + 0.3f;
|
||||
timeSound = game.time () + 0.3f;
|
||||
}
|
||||
else if (client.ent->v.oldbuttons & IN_USE) // pressed used button?
|
||||
else if (buttons & IN_USE) // pressed used button?
|
||||
{
|
||||
hearDistance = 512.0f;
|
||||
timeSound = game.timebase () + 0.5f;
|
||||
timeSound = game.time () + 0.5f;
|
||||
}
|
||||
else if (client.ent->v.oldbuttons & IN_RELOAD) // pressed reload button?
|
||||
else if (buttons & IN_RELOAD) // pressed reload button?
|
||||
{
|
||||
hearDistance = 512.0f;
|
||||
timeSound = game.timebase () + 0.5f;
|
||||
timeSound = game.time () + 0.5f;
|
||||
}
|
||||
else if (client.ent->v.movetype == MOVETYPE_FLY) // uses ladder?
|
||||
{
|
||||
if (cr::abs (client.ent->v.velocity.z) > 50.0f) {
|
||||
hearDistance = 1024.0f;
|
||||
timeSound = game.timebase () + 0.3f;
|
||||
timeSound = game.time () + 0.3f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -436,7 +453,7 @@ void BotUtils::simulateSoundUpdates (int playerIndex) {
|
|||
if (mp_footsteps.bool_ ()) {
|
||||
// moves fast enough?
|
||||
hearDistance = 1280.0f * (client.ent->v.velocity.length2d () / 260.0f);
|
||||
timeSound = game.timebase () + 0.3f;
|
||||
timeSound = game.time () + 0.3f;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -445,7 +462,7 @@ void BotUtils::simulateSoundUpdates (int playerIndex) {
|
|||
}
|
||||
|
||||
// some sound already associated
|
||||
if (client.timeSoundLasting > game.timebase ()) {
|
||||
if (client.timeSoundLasting > game.time ()) {
|
||||
if (client.hearingDistance <= hearDistance) {
|
||||
// override it with new
|
||||
client.hearingDistance = hearDistance;
|
||||
|
|
@ -481,7 +498,7 @@ void BotUtils::updateClients () {
|
|||
|
||||
if (client.flags & ClientFlags::Alive) {
|
||||
client.origin = player->v.origin;
|
||||
simulateSoundUpdates (i);
|
||||
simulateNoise (i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -576,7 +593,7 @@ void BotUtils::sendPings (edict_t *to) {
|
|||
client.ping = getPingBitmask (client.ent, rg.int_ (5, 10), rg.int_ (15, 40));
|
||||
}
|
||||
|
||||
msg.start (MSG_ONE_UNRELIABLE, kGamePingSVC, nullvec, to)
|
||||
msg.start (MSG_ONE_UNRELIABLE, kGamePingSVC, nullptr, to)
|
||||
.writeLong (client.ping)
|
||||
.end ();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue