Major refactoring (#86)

Major code refactoring.
This commit is contained in:
jeefo 2019-07-01 21:10:00 +03:00
commit 68f2f010fd
24 changed files with 8524 additions and 8166 deletions

View file

@ -1,4 +1,3 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
@ -9,13 +8,57 @@
#include <yapb.h>
ConVar yb_display_menu_text ("yb_display_menu_text", "1");
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1");
ConVar mp_roundtime ("mp_roundtime", nullptr, VT_NOREGISTER);
ConVar mp_freezetime ("mp_freezetime", nullptr, VT_NOREGISTER, true, "0");
BotUtils::BotUtils (void) {
m_needToSendWelcome = false;
m_welcomeReceiveTime = 0.0f;
const char *format (const char *format, ...) {
// add default messages
m_sentences.push ("hello user,communication is acquired");
m_sentences.push ("your presence is acknowledged");
m_sentences.push ("high man, your in command now");
m_sentences.push ("blast your hostile for good");
m_sentences.push ("high man, kill some idiot here");
m_sentences.push ("is there a doctor in the area");
m_sentences.push ("warning, experimental materials detected");
m_sentences.push ("high amigo, shoot some but");
m_sentences.push ("attention, hours of work software, detected");
m_sentences.push ("time for some bad ass explosion");
m_sentences.push ("bad ass son of a breach device activated");
m_sentences.push ("high, do not question this great service");
m_sentences.push ("engine is operative, hello and goodbye");
m_sentences.push ("high amigo, your administration has been great last day");
m_sentences.push ("attention, expect experimental armed hostile presence");
m_sentences.push ("warning, medical attention required");
m_tags.push ({ "[[", "]]" });
m_tags.push ({ "-=", "=-" });
m_tags.push ({ "-[", "]-" });
m_tags.push ({ "-]", "[-" });
m_tags.push ({ "-}", "{-" });
m_tags.push ({ "-{", "}-" });
m_tags.push ({ "<[", "]>" });
m_tags.push ({ "<]", "[>" });
m_tags.push ({ "[-", "-]" });
m_tags.push ({ "]-", "-[" });
m_tags.push ({ "{-", "-}" });
m_tags.push ({ "}-", "-{" });
m_tags.push ({ "[", "]" });
m_tags.push ({ "{", "}" });
m_tags.push ({ "<", "[" });
m_tags.push ({ ">", "<" });
m_tags.push ({ "-", "-" });
m_tags.push ({ "|", "|" });
m_tags.push ({ "=", "=" });
m_tags.push ({ "+", "+" });
m_tags.push ({ "(", ")" });
m_tags.push ({ ")", "(" });
m_clients.resize (MAX_ENGINE_PLAYERS + 1);
}
const char *BotUtils::format (const char *format, ...) {
static char strBuffer[2][MAX_PRINT_BUFFER];
static int rotator = 0;
@ -32,30 +75,30 @@ const char *format (const char *format, ...) {
return ptr;
}
bool isAlive (edict_t *ent) {
if (engine.isNullEntity (ent)) {
bool BotUtils::isAlive (edict_t *ent) {
if (game.isNullEntity (ent)) {
return false;
}
return ent->v.deadflag == DEAD_NO && ent->v.health > 0 && ent->v.movetype != MOVETYPE_NOCLIP;
}
float getShootingConeDeviation (edict_t *ent, const Vector &position) {
makeVectors (ent->v.v_angle);
float BotUtils::getShootingCone (edict_t *ent, const Vector &position) {
game.makeVectors (ent->v.v_angle);
// he's facing it, he meant it
return g_pGlobals->v_forward | (position - (ent->v.origin + ent->v.view_ofs)).normalize ();
return game.vec.forward | (position - (ent->v.origin + ent->v.view_ofs)).normalize ();
}
bool isInViewCone (const Vector &origin, edict_t *ent) {
return getShootingConeDeviation (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
bool BotUtils::isInViewCone (const Vector &origin, edict_t *ent) {
return getShootingCone (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
}
bool isVisible (const Vector &origin, edict_t *ent) {
if (engine.isNullEntity (ent)) {
bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
if (game.isNullEntity (ent)) {
return false;
}
TraceResult tr;
engine.testLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr);
game.testLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr);
if (tr.flFraction != 1.0f) {
return false;
@ -63,81 +106,7 @@ bool isVisible (const Vector &origin, edict_t *ent) {
return true;
}
void showMenu (edict_t *ent, MenuId menu) {
static bool s_menusParsed = false;
// make menus looks like we need only once
if (!s_menusParsed) {
extern void setupBotMenus (void);
setupBotMenus ();
for (int i = 0; i < BOT_MENU_TOTAL_MENUS; i++) {
auto parsed = &g_menus[i];
const String &translated = engine.translate (parsed->text.chars ());
// translate all the things
parsed->text = translated;
// make menu looks best
if (!(g_gameFlags & GAME_LEGACY)) {
for (int j = 0; j < 10; j++) {
parsed->text.replace (format ("%d.", j), format ("\\r%d.\\w", j));
}
}
}
s_menusParsed = true;
}
if (!isPlayer (ent)) {
return;
}
Client &client = g_clients[engine.indexOfEntity (ent) - 1];
if (menu == BOT_MENU_INVALID) {
MessageWriter (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent)
.writeShort (0)
.writeChar (0)
.writeByte (0)
.writeString ("");
client.menu = menu;
return;
}
int menuIndex = 0;
for (; menuIndex < BOT_MENU_TOTAL_MENUS; menuIndex++) {
if (g_menus[menuIndex].id == menu) {
break;
}
}
const auto &menuText = g_menus[menuIndex];
const char *text = ((g_gameFlags & (GAME_XASH_ENGINE | GAME_MOBILITY)) && !yb_display_menu_text.boolean ()) ? " " : menuText.text.chars ();
MessageWriter msg;
while (strlen (text) >= 64) {
msg.start (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent)
.writeShort (menuText.slots)
.writeChar (-1)
.writeByte (1);
for (int i = 0; i < 64; i++) {
msg.writeChar (text[i]);
}
msg.end ();
text += 64;
}
MessageWriter (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent)
.writeShort (menuText.slots)
.writeChar (-1)
.writeByte (0)
.writeString (text);
client.menu = menu;
g_engfuncs.pfnClientCommand (ent, "speak \"player/geiger1\"\n"); // Stops others from hearing menu sounds..
}
void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
void BotUtils::traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
// this function draw spraypaint depending on the tracing results.
static StringArray logotypes;
@ -146,18 +115,18 @@ void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
logotypes = String ("{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r").split (";");
}
int entityIndex = -1, message = TE_DECAL;
int decalIndex = g_engfuncs.pfnDecalIndex (logotypes[logotypeIndex].chars ());
int decalIndex = engfuncs.pfnDecalIndex (logotypes[logotypeIndex].chars ());
if (decalIndex < 0) {
decalIndex = g_engfuncs.pfnDecalIndex ("{lambda06");
decalIndex = engfuncs.pfnDecalIndex ("{lambda06");
}
if (trace->flFraction == 1.0f) {
return;
}
if (!engine.isNullEntity (trace->pHit)) {
if (!game.isNullEntity (trace->pHit)) {
if (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) {
entityIndex = engine.indexOfEntity (trace->pHit);
entityIndex = game.indexOfEntity (trace->pHit);
}
else {
return;
@ -185,11 +154,11 @@ void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
if (logotypes[logotypeIndex].contains ("{")) {
MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY)
.writeByte (TE_PLAYERDECAL)
.writeByte (engine.indexOfEntity (pev->pContainingEntity))
.writeByte (game.indexOfEntity (pev->pContainingEntity))
.writeCoord (trace->vecEndPos.x)
.writeCoord (trace->vecEndPos.y)
.writeCoord (trace->vecEndPos.z)
.writeShort (static_cast <short> (engine.indexOfEntity (trace->pHit)))
.writeShort (static_cast <short> (game.indexOfEntity (trace->pHit)))
.writeByte (decalIndex);
}
else {
@ -209,185 +178,8 @@ void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
}
}
void cleanupGarbage (void) {
// this function free's all allocated memory
waypoints.init (); // frees waypoint data
delete[] g_experienceData;
g_experienceData = nullptr;
}
void updateGlobalExperience (void) {
// this function called after each end of the round to update knowledge about most dangerous waypoints for each team.
// no waypoints, no experience used or waypoints edited or being edited?
if (waypoints.length () < 1 || waypoints.hasChanged ()) {
return; // no action
}
uint16 maxDamage; // maximum damage
uint16 actDamage; // actual damage
int bestIndex; // best index to store
bool recalcKills = false;
// get the most dangerous waypoint for this position for terrorist team
for (int i = 0; i < waypoints.length (); i++) {
maxDamage = 0;
bestIndex = INVALID_WAYPOINT_INDEX;
for (int j = 0; j < waypoints.length (); j++) {
if (i == j) {
continue;
}
actDamage = (g_experienceData + (i * waypoints.length ()) + j)->team0Damage;
if (actDamage > maxDamage) {
maxDamage = actDamage;
bestIndex = j;
}
}
if (maxDamage > MAX_DAMAGE_VALUE) {
recalcKills = true;
}
(g_experienceData + (i * waypoints.length ()) + i)->team0DangerIndex = static_cast <short> (bestIndex);
}
// get the most dangerous waypoint for this position for counter-terrorist team
for (int i = 0; i < waypoints.length (); i++) {
maxDamage = 0;
bestIndex = INVALID_WAYPOINT_INDEX;
for (int j = 0; j < waypoints.length (); j++) {
if (i == j) {
continue;
}
actDamage = (g_experienceData + (i * waypoints.length ()) + j)->team1Damage;
if (actDamage > maxDamage) {
maxDamage = actDamage;
bestIndex = j;
}
}
if (maxDamage > MAX_DAMAGE_VALUE) {
recalcKills = true;
}
(g_experienceData + (i * waypoints.length ()) + i)->team1DangerIndex = static_cast <short> (bestIndex);
}
// adjust values if overflow is about to happen
if (recalcKills) {
for (int i = 0; i < waypoints.length (); i++) {
for (int j = 0; j < waypoints.length (); j++) {
if (i == j) {
continue;
}
int clip = (g_experienceData + (i * waypoints.length ()) + j)->team0Damage;
clip -= static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
if (clip < 0) {
clip = 0;
}
(g_experienceData + (i * waypoints.length ()) + j)->team0Damage = static_cast <uint16> (clip);
clip = (g_experienceData + (i * waypoints.length ()) + j)->team1Damage;
clip -= static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
if (clip < 0) {
clip = 0;
}
(g_experienceData + (i * waypoints.length ()) + j)->team1Damage = static_cast <uint16> (clip);
}
}
}
g_highestKills++;
int clip = g_highestDamageT - static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
if (clip < 1) {
clip = 1;
}
g_highestDamageT = clip;
clip = (int)g_highestDamageCT - static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
if (clip < 1) {
clip = 1;
}
g_highestDamageCT = clip;
if (g_highestKills == MAX_KILL_HISTORY) {
for (int i = 0; i < waypoints.length (); i++) {
(g_experienceData + (i * waypoints.length ()) + i)->team0Damage /= static_cast <uint16> (engine.maxClients () * 0.5);
(g_experienceData + (i * waypoints.length ()) + i)->team1Damage /= static_cast <uint16> (engine.maxClients () * 0.5);
}
g_highestKills = 1;
}
}
void initRound (void) {
// this is called at the start of each round
g_roundEnded = false;
g_canSayBombPlanted = true;
// check team economics
for (int team = 0; team < MAX_TEAM_COUNT; team++) {
bots.updateTeamEconomics (team);
bots.selectLeaders (team, true);
}
bots.reset ();
for (int i = 0; i < engine.maxClients (); i++) {
auto bot = bots.getBot (i);
if (bot != nullptr) {
bot->newRound ();
}
g_radioSelect[i] = 0;
}
waypoints.setBombPos (true);
waypoints.clearVisited ();
g_bombSayString = false;
g_timeBombPlanted = 0.0f;
g_timeNextBombUpdate = 0.0f;
for (int i = 0; i < MAX_TEAM_COUNT; i++) {
g_lastRadioTime[i] = 0.0f;
}
g_botsCanPause = false;
for (int i = 0; i < TASK_MAX; i++) {
g_taskFilters[i].time = 0.0f;
}
updateGlobalExperience (); // update experience data on round start
// calculate the round mid/end in world time
g_timeRoundStart = engine.timebase () + mp_freezetime.flt ();
g_timeRoundMid = g_timeRoundStart + mp_roundtime.flt () * 60.0f * 0.5f;
g_timeRoundEnd = g_timeRoundStart + mp_roundtime.flt () * 60.0f;
}
int getWeaponPenetrationPower (int id) {
// returns if weapon can pierce through a wall
int i = 0;
while (g_weaponSelect[i].id) {
if (g_weaponSelect[i].id == id) {
return g_weaponSelect[i].penetratePower;
}
i++;
}
return 0;
}
bool isPlayer (edict_t *ent) {
if (engine.isNullEntity (ent)) {
bool BotUtils::isPlayer (edict_t *ent) {
if (game.isNullEntity (ent)) {
return false;
}
@ -401,25 +193,25 @@ bool isPlayer (edict_t *ent) {
return false;
}
bool isPlayerVIP (edict_t *ent) {
if (!(g_mapFlags & MAP_AS)) {
bool BotUtils::isPlayerVIP (edict_t *ent) {
if (!game.mapIs (MAP_AS)) {
return false;
}
if (!isPlayer (ent)) {
return false;
}
return *(g_engfuncs.pfnInfoKeyValue (g_engfuncs.pfnGetInfoKeyBuffer (ent), "model")) == 'v';
return *(engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (ent), "model")) == 'v';
}
bool isFakeClient (edict_t *ent) {
if (bots.getBot (ent) != nullptr || (!engine.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
bool BotUtils::isFakeClient (edict_t *ent) {
if (bots.getBot (ent) != nullptr || (!game.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
return true;
}
return false;
}
bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
bool BotUtils::openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
if (outFile->isValid ()) {
outFile->close ();
}
@ -446,11 +238,13 @@ bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *ou
// unload and reopen file using MemoryFile
outFile->open (langConfig);
}
else
else {
outFile->open (format ("%s/lang/en_%s", configDir, fileName));
}
}
else
else {
outFile->open (format ("%s/%s", configDir, fileName));
}
if (!outFile->isValid ()) {
logEntry (true, LL_ERROR, errorIfNotExists);
@ -459,57 +253,31 @@ bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *ou
return true;
}
void checkWelcome (void) {
void BotUtils::checkWelcome (void) {
// the purpose of this function, is to send quick welcome message, to the listenserver entity.
if (engine.isDedicated ())
return;
static bool messageSent = !yb_display_welcome_text.boolean ();
static float receiveTime = 0.0f;
if (messageSent) {
if (game.isDedicated () || !yb_display_welcome_text.boolean () || !m_needToSendWelcome) {
return;
}
m_welcomeReceiveTime = 0.0f;
if (g_gameFlags & GAME_LEGACY) {
g_gameWelcomeSent = true;
if (game.is (GAME_LEGACY)) {
m_needToSendWelcome = true;
return;
}
bool needToSendMsg = (waypoints.length () > 0 ? m_needToSendWelcome : true);
if (isAlive (game.getLocalEntity ()) && m_welcomeReceiveTime < 1.0 && needToSendMsg) {
m_welcomeReceiveTime = game.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing
}
static StringArray sentences;
if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE)) && sentences.empty ()) {
// add default messages
sentences.push ("hello user,communication is acquired");
sentences.push ("your presence is acknowledged");
sentences.push ("high man, your in command now");
sentences.push ("blast your hostile for good");
sentences.push ("high man, kill some idiot here");
sentences.push ("is there a doctor in the area");
sentences.push ("warning, experimental materials detected");
sentences.push ("high amigo, shoot some but");
sentences.push ("attention, hours of work software, detected");
sentences.push ("time for some bad ass explosion");
sentences.push ("bad ass son of a breach device activated");
sentences.push ("high, do not question this great service");
sentences.push ("engine is operative, hello and goodbye");
sentences.push ("high amigo, your administration has been great last day");
sentences.push ("attention, expect experimental armed hostile presence");
sentences.push ("warning, medical attention required");
}
bool needToSendMsg = (waypoints.length () > 0 ? g_gameWelcomeSent : true);
if (isAlive (g_hostEntity) && receiveTime < 1.0 && needToSendMsg) {
receiveTime = engine.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing
}
if (receiveTime > 0.0f && receiveTime < engine.timebase () && needToSendMsg) {
if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE))) {
engine.execCmd ("speak \"%s\"", sentences.random ().chars ());
if (m_welcomeReceiveTime > 0.0f && needToSendMsg) {
if (!game.is (GAME_MOBILITY | GAME_XASH_ENGINE)) {
game.execCmd ("speak \"%s\"", m_sentences.random ().chars ());
}
engine.chatPrint ("----- %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);
game.chatPrint ("----- %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, Vector::null (), g_hostEntity)
MessageWriter (MSG_ONE, SVC_TEMPENTITY, Vector::null (), game.getLocalEntity ())
.writeByte (TE_TEXTMESSAGE)
.writeByte (1)
.writeShort (MessageWriter::fs16 (-1, 1 << 13))
@ -529,12 +297,12 @@ void checkWelcome (void) {
.writeShort (MessageWriter::fu16 (0.1f, 1 << 8))
.writeString (format ("\nServer is running %s v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, waypoints.getAuthor ()));
receiveTime = 0.0;
messageSent = true;
m_welcomeReceiveTime = 0.0f;
m_needToSendWelcome = false;
}
}
void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
void BotUtils::logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
// this function logs a message to the message log file root directory.
va_list ap;
@ -563,7 +331,7 @@ void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
}
if (outputToConsole) {
engine.print ("%s%s", levelString, buffer);
game.print ("%s%s", levelString, buffer);
}
// now check if logging disabled
@ -599,7 +367,7 @@ void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
if (logLevel == LL_FATAL) {
bots.kickEveryone (true);
cleanupGarbage ();
waypoints.init ();
#if defined(PLATFORM_WIN32)
DestroyWindow (GetForegroundWindow ());
@ -616,7 +384,7 @@ void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
}
}
bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool isAlive, bool needDrawn, bool needBotWithC4) {
bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool needAlive, bool needDrawn, bool needBotWithC4) {
// this function finds nearest to to, player with set of parameters, like his
// team, live status, search distance etc. if needBot is true, then pvHolder, will
// be filled with bot pointer, else with edict pointer(!).
@ -624,16 +392,14 @@ bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool
edict_t *survive = nullptr; // pointer to temporally & survive entity
float nearestPlayer = 4096.0f; // nearest player
int toTeam = engine.getTeam (to);
for (int i = 0; i < engine.maxClients (); i++) {
const Client &client = g_clients[i];
int toTeam = game.getTeam (to);
for (const auto &client : m_clients) {
if (!(client.flags & CF_USED) || client.ent == to) {
continue;
}
if ((sameTeam && client.team != toTeam) || (isAlive && !(client.flags & CF_ALIVE)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & WEAPON_C4))) {
if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & CF_ALIVE)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & WEAPON_C4))) {
continue; // filter players with parameters
}
float distance = (client.ent->v.origin - to->v.origin).length ();
@ -644,8 +410,9 @@ bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool
}
}
if (engine.isNullEntity (survive))
if (game.isNullEntity (survive)) {
return false; // nothing found
}
// fill the holder
if (needBot) {
@ -657,26 +424,26 @@ bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool
return true;
}
void attachSoundsToClients (edict_t *ent, const char *sample, float volume) {
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
if (engine.isNullEntity (ent) || isEmptyStr (sample)) {
if (game.isNullEntity (ent) || isEmptyStr (sample)) {
return;
}
const Vector &origin = engine.getAbsPos (ent);
const Vector &origin = game.getAbsPos (ent);
if (origin.empty ()) {
return;
}
int index = engine.indexOfEntity (ent) - 1;
int index = game.indexOfEntity (ent) - 1;
if (index < 0 || index >= engine.maxClients ()) {
if (index < 0 || index >= game.maxClients ()) {
float nearestDistance = 99999.0f;
// loop through all players
for (int i = 0; i < engine.maxClients (); i++) {
const Client &client = g_clients[i];
for (int i = 0; i < game.maxClients (); i++) {
const Client &client = m_clients[i];
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE)) {
continue;
@ -692,63 +459,63 @@ void attachSoundsToClients (edict_t *ent, const char *sample, float volume) {
}
// in case of worst case
if (index < 0 || index >= engine.maxClients ()) {
if (index < 0 || index >= game.maxClients ()) {
return;
}
Client &client = g_clients[index];
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 = engine.timebase () + 0.5f;
client.soundPos = origin;
client.timeSoundLasting = game.timebase () + 0.5f;
client.sound = origin;
}
else if (strncmp ("items/gunpickup", sample, 15) == 0) {
// weapon pickup?
client.hearingDistance = 768.0f * volume;
client.timeSoundLasting = engine.timebase () + 0.5f;
client.soundPos = origin;
client.timeSoundLasting = game.timebase () + 0.5f;
client.sound = origin;
}
else if (strncmp ("weapons/zoom", sample, 12) == 0) {
// sniper zooming?
client.hearingDistance = 512.0f * volume;
client.timeSoundLasting = engine.timebase () + 0.1f;
client.soundPos = origin;
client.timeSoundLasting = game.timebase () + 0.1f;
client.sound = origin;
}
else if (strncmp ("items/9mmclip", sample, 13) == 0) {
// ammo pickup?
client.hearingDistance = 512.0f * volume;
client.timeSoundLasting = engine.timebase () + 0.1f;
client.soundPos = origin;
client.timeSoundLasting = game.timebase () + 0.1f;
client.sound = origin;
}
else if (strncmp ("hostage/hos", sample, 11) == 0) {
// CT used hostage?
client.hearingDistance = 1024.0f * volume;
client.timeSoundLasting = engine.timebase () + 5.0f;
client.soundPos = origin;
client.timeSoundLasting = game.timebase () + 5.0f;
client.sound = origin;
}
else if (strncmp ("debris/bustmetal", sample, 16) == 0 || strncmp ("debris/bustglass", sample, 16) == 0) {
// broke something?
client.hearingDistance = 1024.0f * volume;
client.timeSoundLasting = engine.timebase () + 2.0f;
client.soundPos = origin;
client.timeSoundLasting = game.timebase () + 2.0f;
client.sound = origin;
}
else if (strncmp ("doors/doormove", sample, 14) == 0) {
// someone opened a door
client.hearingDistance = 1024.0f * volume;
client.timeSoundLasting = engine.timebase () + 3.0f;
client.soundPos = origin;
client.timeSoundLasting = game.timebase () + 3.0f;
client.sound = origin;
}
}
void simulateSoundUpdates (int playerIndex) {
void BotUtils::simulateSoundUpdates (int playerIndex) {
// this function tries to simulate playing of sounds to let the bots hear sounds which aren't
// captured through server sound hooking
if (playerIndex < 0 || playerIndex >= engine.maxClients ()) {
if (playerIndex < 0 || playerIndex >= game.maxClients ()) {
return; // reliability check
}
Client &client = g_clients[playerIndex];
Client &client = m_clients[playerIndex];
float hearDistance = 0.0f;
float timeSound = 0.0f;
@ -756,23 +523,23 @@ void simulateSoundUpdates (int playerIndex) {
if (client.ent->v.oldbuttons & IN_ATTACK) // pressed attack button?
{
hearDistance = 2048.0f;
timeSound = engine.timebase () + 0.3f;
timeSound = game.timebase () + 0.3f;
}
else if (client.ent->v.oldbuttons & IN_USE) // pressed used button?
{
hearDistance = 512.0f;
timeSound = engine.timebase () + 0.5f;
timeSound = game.timebase () + 0.5f;
}
else if (client.ent->v.oldbuttons & IN_RELOAD) // pressed reload button?
{
hearDistance = 512.0f;
timeSound = engine.timebase () + 0.5f;
timeSound = game.timebase () + 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 = engine.timebase () + 0.3f;
timeSound = game.timebase () + 0.3f;
}
}
else {
@ -781,7 +548,7 @@ void simulateSoundUpdates (int playerIndex) {
if (mp_footsteps.boolean ()) {
// moves fast enough?
hearDistance = 1280.0f * (client.ent->v.velocity.length2D () / 260.0f);
timeSound = engine.timebase () + 0.3f;
timeSound = game.timebase () + 0.3f;
}
}
@ -790,23 +557,52 @@ void simulateSoundUpdates (int playerIndex) {
}
// some sound already associated
if (client.timeSoundLasting > engine.timebase ()) {
if (client.timeSoundLasting > game.timebase ()) {
if (client.hearingDistance <= hearDistance) {
// override it with new
client.hearingDistance = hearDistance;
client.timeSoundLasting = timeSound;
client.soundPos = client.ent->v.origin;
client.sound = client.ent->v.origin;
}
}
else {
// just remember it
client.hearingDistance = hearDistance;
client.timeSoundLasting = timeSound;
client.soundPos = client.ent->v.origin;
client.sound = client.ent->v.origin;
}
}
int buildNumber (void) {
void BotUtils::updateClients (void) {
// record some stats of all players on the server
for (int i = 0; i < game.maxClients (); i++) {
edict_t *player = game.entityOfIndex (i + 1);
Client &client = m_clients[i];
if (!game.isNullEntity (player) && (player->v.flags & FL_CLIENT)) {
client.ent = player;
client.flags |= CF_USED;
if (util.isAlive (player)) {
client.flags |= CF_ALIVE;
}
else {
client.flags &= ~CF_ALIVE;
}
if (client.flags & CF_ALIVE) {
client.origin = player->v.origin;
simulateSoundUpdates (i);
}
}
else {
client.flags &= ~(CF_USED | CF_ALIVE);
client.ent = nullptr;
}
}
}
int BotUtils::buildNumber (void) {
// this function generates build number from the compiler date macros
static int buildNumber = 0;
@ -848,7 +644,7 @@ int buildNumber (void) {
return buildNumber;
}
int getWeaponData (bool needString, const char *weaponAlias, int weaponIndex) {
int BotUtils::getWeaponAlias (bool needString, const char *weaponAlias, int weaponIndex) {
// this function returning weapon id from the weapon alias and vice versa.
// structure definition for weapon tab
@ -895,18 +691,18 @@ int getWeaponData (bool needString, const char *weaponAlias, int weaponIndex) {
// if we need to return the string, find by weapon id
if (needString && weaponIndex != -1) {
for (size_t i = 0; i < cr::arrsize (weaponTab); i++) {
if (weaponTab[i].weaponIndex == weaponIndex) { // is weapon id found?
return MAKE_STRING (weaponTab[i].alias);
for (auto &tab : weaponTab) {
if (tab.weaponIndex == weaponIndex) { // is weapon id found?
return MAKE_STRING (tab.alias);
}
}
return MAKE_STRING ("(none)"); // return none
}
// else search weapon by name and return weapon id
for (size_t i = 0; i < cr::arrsize (weaponTab); i++) {
if (strncmp (weaponTab[i].alias, weaponAlias, strlen (weaponTab[i].alias)) == 0) {
return weaponTab[i].weaponIndex;
for (auto &tab : weaponTab) {
if (strncmp (tab.alias, weaponAlias, strlen (tab.alias)) == 0) {
return tab.weaponIndex;
}
}
return -1; // no weapon was found return -1