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