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