parent
b50d6b198c
commit
68f2f010fd
24 changed files with 8524 additions and 8166 deletions
|
|
@ -9,327 +9,295 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
static constexpr int MAXBUF = 4096, PADDING = 18, THRESHOLD = 2, NIL = MAXBUF;
|
// see https://github.com/encode84/ulz/
|
||||||
|
class FastLZ final : NonCopyable {
|
||||||
|
public:
|
||||||
|
static constexpr int EXCESS = 16;
|
||||||
|
static constexpr int WINDOW_BITS = 17;
|
||||||
|
static constexpr int WINDOW_SIZE = cr::bit (WINDOW_BITS);
|
||||||
|
static constexpr int WINDOW_MASK = WINDOW_SIZE - 1;
|
||||||
|
|
||||||
class Compress {
|
static constexpr int MIN_MATCH = 4;
|
||||||
protected:
|
static constexpr int MAX_CHAIN = cr::bit (5);
|
||||||
unsigned long int m_csize;
|
|
||||||
|
|
||||||
uint8 m_buffer[MAXBUF + PADDING - 1];
|
static constexpr int HASH_BITS = 19;
|
||||||
int m_matchPos;
|
static constexpr int HASH_SIZE = cr::bit (HASH_BITS);
|
||||||
int m_matchLen;
|
static constexpr int NIL = -1;
|
||||||
|
static constexpr int UNCOMPRESS_RESULT_FAILED = -1;
|
||||||
int m_left[MAXBUF + 1];
|
|
||||||
int m_right[MAXBUF + 257];
|
|
||||||
int m_parent[MAXBUF + 1];
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initTrees (void) {
|
int *m_hashTable;
|
||||||
for (int i = MAXBUF + 1; i <= MAXBUF + 256; i++) {
|
int *m_prevTable;
|
||||||
m_right[i] = NIL;
|
|
||||||
|
public:
|
||||||
|
FastLZ (void) {
|
||||||
|
m_hashTable = new int[HASH_SIZE];
|
||||||
|
m_prevTable = new int[WINDOW_SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 0; j < MAXBUF; j++) {
|
~FastLZ (void) {
|
||||||
m_parent[j] = NIL;
|
delete [] m_hashTable;
|
||||||
}
|
delete [] m_prevTable;
|
||||||
}
|
|
||||||
|
|
||||||
void insert (int node) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
int compare = 1;
|
|
||||||
|
|
||||||
uint8 *key = &m_buffer[node];
|
|
||||||
int temp = MAXBUF + 1 + key[0];
|
|
||||||
|
|
||||||
m_right[node] = m_left[node] = NIL;
|
|
||||||
m_matchLen = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (compare >= 0) {
|
|
||||||
if (m_right[temp] != NIL) {
|
|
||||||
temp = m_right[temp];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_right[temp] = node;
|
|
||||||
m_parent[node] = temp;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (m_left[temp] != NIL) {
|
|
||||||
temp = m_left[temp];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_left[temp] = node;
|
|
||||||
m_parent[node] = temp;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i < PADDING; i++) {
|
|
||||||
if ((compare = key[i] - m_buffer[temp + i]) != 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i > m_matchLen) {
|
|
||||||
m_matchPos = temp;
|
|
||||||
|
|
||||||
if ((m_matchLen = i) >= PADDING) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_parent[node] = m_parent[temp];
|
|
||||||
m_left[node] = m_left[temp];
|
|
||||||
m_right[node] = m_right[temp];
|
|
||||||
m_parent[m_left[temp]] = node;
|
|
||||||
m_parent[m_right[temp]] = node;
|
|
||||||
|
|
||||||
if (m_right[m_parent[temp]] == temp) {
|
|
||||||
m_right[m_parent[temp]] = node;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_left[m_parent[temp]] = node;
|
|
||||||
}
|
|
||||||
m_parent[temp] = NIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void erase (int node) {
|
|
||||||
int temp;
|
|
||||||
|
|
||||||
if (m_parent[node] == NIL) {
|
|
||||||
return; // not in tree
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_right[node] == NIL) {
|
|
||||||
temp = m_left[node];
|
|
||||||
}
|
|
||||||
else if (m_left[node] == NIL) {
|
|
||||||
temp = m_right[node];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
temp = m_left[node];
|
|
||||||
|
|
||||||
if (m_right[temp] != NIL) {
|
|
||||||
do {
|
|
||||||
temp = m_right[temp];
|
|
||||||
} while (m_right[temp] != NIL);
|
|
||||||
|
|
||||||
m_right[m_parent[temp]] = m_left[temp];
|
|
||||||
m_parent[m_left[temp]] = m_parent[temp];
|
|
||||||
m_left[temp] = m_left[node];
|
|
||||||
m_parent[m_left[node]] = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_right[temp] = m_right[node];
|
|
||||||
m_parent[m_right[node]] = temp;
|
|
||||||
}
|
|
||||||
m_parent[temp] = m_parent[node];
|
|
||||||
|
|
||||||
if (m_right[m_parent[node]] == node) {
|
|
||||||
m_right[m_parent[node]] = temp;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_left[m_parent[node]] = temp;
|
|
||||||
}
|
|
||||||
m_parent[node] = NIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Compress (void) : m_csize (0), m_matchPos (0), m_matchLen (0) {
|
int compress (uint8 *in, int inLength, uint8 *out) {
|
||||||
memset (m_right, 0, sizeof (m_right));
|
for (int i = 0; i < HASH_SIZE; i++) {
|
||||||
memset (m_left, 0, sizeof (m_left));
|
m_hashTable[i] = NIL;
|
||||||
memset (m_parent, 0, sizeof (m_parent));
|
}
|
||||||
memset (m_buffer, 0, sizeof (m_buffer));
|
uint8 *op = out;
|
||||||
|
|
||||||
|
int anchor = 0;
|
||||||
|
int cur = 0;
|
||||||
|
|
||||||
|
while (cur < inLength) {
|
||||||
|
const int maxMatch = inLength - cur;
|
||||||
|
|
||||||
|
int bestLength = 0;
|
||||||
|
int dist = 0;
|
||||||
|
|
||||||
|
if (maxMatch >= MIN_MATCH) {
|
||||||
|
const int limit = cr::max (cur - WINDOW_SIZE, NIL);
|
||||||
|
|
||||||
|
int chainLength = MAX_CHAIN;
|
||||||
|
int lookup = m_hashTable[hash32 (&in[cur])];
|
||||||
|
|
||||||
|
while (lookup > limit) {
|
||||||
|
if (in[lookup + bestLength] == in[cur + bestLength] && load32 (&in[lookup]) == load32 (&in[cur])) {
|
||||||
|
int length = MIN_MATCH;
|
||||||
|
|
||||||
|
while (length < maxMatch && in[lookup + length] == in[cur + length]) {
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
~Compress (void) = default;
|
if (length > bestLength) {
|
||||||
|
bestLength = length;
|
||||||
|
dist = cur - lookup;
|
||||||
|
|
||||||
int encode_ (const char *fileName, uint8 *header, int headerSize, uint8 *buffer, int bufferSize) {
|
if (length == maxMatch) {
|
||||||
File fp (fileName, "wb");
|
break;
|
||||||
|
}
|
||||||
if (!fp.isValid ()) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
int i, length, node, ptr, last, cbp, bp = 0;
|
|
||||||
uint8 cb[17] = {0, }, mask, bit;
|
|
||||||
|
|
||||||
fp.write (header, headerSize, 1);
|
|
||||||
initTrees ();
|
|
||||||
|
|
||||||
cb[0] = 0;
|
|
||||||
cbp = mask = 1;
|
|
||||||
ptr = 0;
|
|
||||||
node = MAXBUF - PADDING;
|
|
||||||
|
|
||||||
for (i = ptr; i < node; i++)
|
|
||||||
m_buffer[i] = ' ';
|
|
||||||
|
|
||||||
for (length = 0; (length < PADDING) && (bp < bufferSize); length++) {
|
|
||||||
bit = buffer[bp++];
|
|
||||||
m_buffer[node + length] = bit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == 0) {
|
if (--chainLength == 0) {
|
||||||
return -1;
|
break;
|
||||||
|
}
|
||||||
|
lookup = m_prevTable[lookup & WINDOW_MASK];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 1; i <= PADDING; i++) {
|
if (bestLength == MIN_MATCH && (cur - anchor) >= (7 + 128)) {
|
||||||
insert (node - i);
|
bestLength = 0;
|
||||||
}
|
}
|
||||||
insert (node);
|
|
||||||
|
|
||||||
do {
|
if (bestLength >= MIN_MATCH && bestLength < maxMatch && (cur - anchor) != 6) {
|
||||||
if (m_matchLen > length) {
|
const int next = cur + 1;
|
||||||
m_matchLen = length;
|
const int targetLength = bestLength + 1;
|
||||||
|
const int limit = cr::max (next - WINDOW_SIZE, NIL);
|
||||||
|
|
||||||
|
int chainLength = MAX_CHAIN;
|
||||||
|
int lookup = m_hashTable[hash32 (&in[next])];
|
||||||
|
|
||||||
|
while (lookup > limit) {
|
||||||
|
if (in[lookup + bestLength] == in[next + bestLength] && load32 (&in[lookup]) == load32 (&in[next])) {
|
||||||
|
int length = MIN_MATCH;
|
||||||
|
|
||||||
|
while (length < targetLength && in[lookup + length] == in[next + length]) {
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
if (m_matchLen <= THRESHOLD) {
|
|
||||||
m_matchLen = 1;
|
|
||||||
|
|
||||||
cb[0] |= mask;
|
if (length == targetLength) {
|
||||||
cb[cbp++] = m_buffer[node];
|
bestLength = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--chainLength == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lookup = m_prevTable[lookup & WINDOW_MASK];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestLength >= MIN_MATCH) {
|
||||||
|
const int length = bestLength - MIN_MATCH;
|
||||||
|
const int token = ((dist >> 12) & 16) + cr::min (length, 15);
|
||||||
|
|
||||||
|
if (anchor != cur) {
|
||||||
|
const int run = cur - anchor;
|
||||||
|
|
||||||
|
if (run >= 7) {
|
||||||
|
add (op, (7 << 5) + token);
|
||||||
|
encode (op, run - 7);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cb[cbp++] = (uint8) (m_matchPos & 0xff);
|
add (op, (run << 5) + token);
|
||||||
cb[cbp++] = (uint8) (((m_matchPos >> 4) & 0xf0) | (m_matchLen - (THRESHOLD + 1)));
|
|
||||||
}
|
}
|
||||||
|
copy (op, &in[anchor], run);
|
||||||
if ((mask <<= 1) == 0) {
|
op += run;
|
||||||
for (i = 0; i < cbp; i++) {
|
|
||||||
fp.putch (cb[i]);
|
|
||||||
}
|
|
||||||
m_csize += cbp;
|
|
||||||
cb[0] = 0;
|
|
||||||
cbp = mask = 1;
|
|
||||||
}
|
|
||||||
last = m_matchLen;
|
|
||||||
|
|
||||||
for (i = 0; (i < last) && (bp < bufferSize); i++) {
|
|
||||||
bit = buffer[bp++];
|
|
||||||
erase (ptr);
|
|
||||||
|
|
||||||
m_buffer[ptr] = bit;
|
|
||||||
|
|
||||||
if (ptr < PADDING - 1) {
|
|
||||||
m_buffer[ptr + MAXBUF] = bit;
|
|
||||||
}
|
|
||||||
ptr = (ptr + 1) & (MAXBUF - 1);
|
|
||||||
node = (node + 1) & (MAXBUF - 1);
|
|
||||||
insert (node);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (i++ < last) {
|
|
||||||
erase (ptr);
|
|
||||||
|
|
||||||
ptr = (ptr + 1) & (MAXBUF - 1);
|
|
||||||
node = (node + 1) & (MAXBUF - 1);
|
|
||||||
|
|
||||||
if (length--) {
|
|
||||||
insert (node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (length > 0);
|
|
||||||
|
|
||||||
if (cbp > 1) {
|
|
||||||
for (i = 0; i < cbp; i++) {
|
|
||||||
fp.putch (cb[i]);
|
|
||||||
}
|
|
||||||
m_csize += cbp;
|
|
||||||
}
|
|
||||||
fp.close ();
|
|
||||||
|
|
||||||
return m_csize;
|
|
||||||
}
|
|
||||||
|
|
||||||
int decode_ (const char *fileName, int headerSize, uint8 *buffer, int bufferSize) {
|
|
||||||
int i, j, k, node;
|
|
||||||
unsigned int flags;
|
|
||||||
int bp = 0;
|
|
||||||
|
|
||||||
uint8 bit;
|
|
||||||
|
|
||||||
File fp (fileName, "rb");
|
|
||||||
|
|
||||||
if (!fp.isValid ()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
fp.seek (headerSize, SEEK_SET);
|
|
||||||
|
|
||||||
node = MAXBUF - PADDING;
|
|
||||||
|
|
||||||
for (i = 0; i < node; i++) {
|
|
||||||
m_buffer[i] = ' ';
|
|
||||||
}
|
|
||||||
flags = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (((flags >>= 1) & 256) == 0) {
|
|
||||||
int read = fp.getch ();
|
|
||||||
|
|
||||||
if (read == EOF) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bit = static_cast <uint8> (read);
|
|
||||||
flags = bit | 0xff00;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & 1) {
|
|
||||||
int read = fp.getch ();
|
|
||||||
|
|
||||||
if (read == EOF) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bit = static_cast <uint8> (read);
|
|
||||||
buffer[bp++] = bit;
|
|
||||||
|
|
||||||
if (bp > bufferSize) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
m_buffer[node++] = bit;
|
|
||||||
node &= (MAXBUF - 1);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ((i = fp.getch ()) == EOF) {
|
add (op, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length >= 15) {
|
||||||
|
encode (op, length - 15);
|
||||||
|
}
|
||||||
|
store16 (op, dist);
|
||||||
|
op += 2;
|
||||||
|
|
||||||
|
while (bestLength-- != 0) {
|
||||||
|
const uint32 hash = hash32 (&in[cur]);
|
||||||
|
|
||||||
|
m_prevTable[cur & WINDOW_MASK] = m_hashTable[hash];
|
||||||
|
m_hashTable[hash] = cur++;
|
||||||
|
}
|
||||||
|
anchor = cur;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const uint32 hash = hash32 (&in[cur]);
|
||||||
|
|
||||||
|
m_prevTable[cur & WINDOW_MASK] = m_hashTable[hash];
|
||||||
|
m_hashTable[hash] = cur++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anchor != cur) {
|
||||||
|
const int run = cur - anchor;
|
||||||
|
|
||||||
|
if (run >= 7) {
|
||||||
|
add (op, 7 << 5);
|
||||||
|
encode (op, run - 7);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
add (op, run << 5);
|
||||||
|
}
|
||||||
|
copy (op, &in[anchor], run);
|
||||||
|
op += run;
|
||||||
|
}
|
||||||
|
return op - out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uncompress (uint8 *in, int inLength, uint8 *out, int outLength) {
|
||||||
|
uint8 *op = out;
|
||||||
|
uint8 *ip = in;
|
||||||
|
|
||||||
|
const uint8 *opEnd = op + outLength;
|
||||||
|
const uint8 *ipEnd = ip + inLength;
|
||||||
|
|
||||||
|
while (ip < ipEnd) {
|
||||||
|
const int token = *ip++;
|
||||||
|
|
||||||
|
if (token >= 32) {
|
||||||
|
int run = token >> 5;
|
||||||
|
|
||||||
|
if (run == 7) {
|
||||||
|
run += decode (ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((opEnd - op) < run || (ipEnd - ip) < run) {
|
||||||
|
return UNCOMPRESS_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
copy (op, ip, run);
|
||||||
|
|
||||||
|
op += run;
|
||||||
|
ip += run;
|
||||||
|
|
||||||
|
if (ip >= ipEnd) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
int length = (token & 15) + MIN_MATCH;
|
||||||
|
|
||||||
if ((j = fp.getch ()) == EOF) {
|
if (length == (15 + MIN_MATCH)) {
|
||||||
|
length += decode (ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((opEnd - op) < length) {
|
||||||
|
return UNCOMPRESS_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
const int dist = ((token & 16) << 12) + load16 (ip);
|
||||||
|
ip += 2;
|
||||||
|
|
||||||
|
uint8 *cp = op - dist;
|
||||||
|
|
||||||
|
if ((op - out) < dist) {
|
||||||
|
return UNCOMPRESS_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dist >= 8) {
|
||||||
|
copy (op, cp, length);
|
||||||
|
op += length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
*op++ = *cp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (length-- != 4) {
|
||||||
|
*op++ = *cp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (ip == ipEnd) ? op - out : UNCOMPRESS_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline uint16 load16 (void *ptr) {
|
||||||
|
return *reinterpret_cast <const uint16 *> (ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32 load32 (void *ptr) {
|
||||||
|
return *reinterpret_cast <const uint32 *> (ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void store16 (void *ptr, int val) {
|
||||||
|
*reinterpret_cast <uint16 *> (ptr) = static_cast <uint16> (val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void copy64 (void *dst, void *src) {
|
||||||
|
*reinterpret_cast <uint64 *> (dst) = *reinterpret_cast <const uint64 *> (src);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32 hash32 (void *ptr) {
|
||||||
|
return (load32 (ptr) * 0x9E3779B9) >> (32 - HASH_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void copy (uint8 *dst, uint8 *src, int count) {
|
||||||
|
copy64 (dst, src);
|
||||||
|
|
||||||
|
for (int i = 8; i < count; i += 8) {
|
||||||
|
copy64 (dst + i, src + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void add (uint8 *&dst, int val) {
|
||||||
|
*dst++ = static_cast <uint8> (val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void encode (uint8 *&ptr, uint32 val) {
|
||||||
|
while (val >= 128) {
|
||||||
|
val -= 128;
|
||||||
|
|
||||||
|
*ptr++ = 128 + (val & 127);
|
||||||
|
val >>= 7;
|
||||||
|
}
|
||||||
|
*ptr++ = static_cast <uint8> (val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32 decode (uint8 *&ptr) {
|
||||||
|
uint32 val = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i <= 21; i += 7) {
|
||||||
|
const uint32 cur = *ptr++;
|
||||||
|
val += cur << i;
|
||||||
|
|
||||||
|
if (cur < 128) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i |= ((j & 0xf0) << 4);
|
|
||||||
j = (j & 0x0f) + THRESHOLD;
|
|
||||||
|
|
||||||
for (k = 0; k <= j; k++) {
|
|
||||||
bit = m_buffer[(i + k) & (MAXBUF - 1)];
|
|
||||||
buffer[bp++] = bit;
|
|
||||||
|
|
||||||
if (bp > bufferSize) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
m_buffer[node++] = bit;
|
return val;
|
||||||
node &= (MAXBUF - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fp.close ();
|
|
||||||
|
|
||||||
return bp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// external decoder
|
|
||||||
static int decode (const char *fileName, int headerSize, uint8 *buffer, int bufferSize) {
|
|
||||||
static Compress compressor;
|
|
||||||
return compressor.decode_ (fileName, headerSize, buffer, bufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// external encoder
|
|
||||||
static int encode (const char *fileName, uint8 *header, int headerSize, uint8 *buffer, int bufferSize) {
|
|
||||||
static Compress compressor;
|
|
||||||
return compressor.encode_ (fileName, header, headerSize, buffer, bufferSize);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -54,8 +54,9 @@ using int32 = signed long;
|
||||||
using uint8 = unsigned char;
|
using uint8 = unsigned char;
|
||||||
using uint16 = unsigned short;
|
using uint16 = unsigned short;
|
||||||
using uint32 = unsigned long;
|
using uint32 = unsigned long;
|
||||||
|
using uint64 = unsigned long long;
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
using namespace cr::types;
|
using namespace cr::types;
|
||||||
|
|
||||||
|
|
@ -71,13 +72,14 @@ constexpr float D2R = PI / 180.0f;
|
||||||
constexpr float R2D = 180.0f / PI;
|
constexpr float R2D = 180.0f / PI;
|
||||||
|
|
||||||
// from metamod-p
|
// from metamod-p
|
||||||
static inline bool checkptr (const void *ptr) {
|
static inline bool checkptr (void *ptr) {
|
||||||
#ifdef PLATFORM_WIN32
|
#ifdef PLATFORM_WIN32
|
||||||
if (IsBadCodePtr (reinterpret_cast <FARPROC> (ptr)))
|
if (IsBadCodePtr (reinterpret_cast <FARPROC> (ptr))) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) (ptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(void)(ptr);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +111,10 @@ template <typename T> constexpr T abs (const T a) {
|
||||||
return a > 0 ? a : -a;
|
return a > 0 ? a : -a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> constexpr T bit (const T a) {
|
||||||
|
return 1 << a;
|
||||||
|
}
|
||||||
|
|
||||||
static inline float powf (const float x, const float y) {
|
static inline float powf (const float x, const float y) {
|
||||||
union {
|
union {
|
||||||
float d;
|
float d;
|
||||||
|
|
@ -124,7 +130,7 @@ static inline float sqrtf (const float value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float sinf (const float value) {
|
static inline float sinf (const float value) {
|
||||||
const signed long sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
const auto sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
||||||
const float calc = (value - static_cast <float> (sign) * PI);
|
const float calc = (value - static_cast <float> (sign) * PI);
|
||||||
|
|
||||||
const float sqr = square (calc);
|
const float sqr = square (calc);
|
||||||
|
|
@ -135,7 +141,7 @@ static inline float sinf (const float value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float cosf (const float value) {
|
static inline float cosf (const float value) {
|
||||||
const signed long sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
const auto sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
||||||
const float calc = (value - static_cast <float> (sign) * PI);
|
const float calc = (value - static_cast <float> (sign) * PI);
|
||||||
|
|
||||||
const float sqr = square (calc);
|
const float sqr = square (calc);
|
||||||
|
|
@ -303,7 +309,7 @@ protected:
|
||||||
NonCopyable (void) = default;
|
NonCopyable (void) = default;
|
||||||
~NonCopyable (void) = default;
|
~NonCopyable (void) = default;
|
||||||
|
|
||||||
private:
|
public:
|
||||||
NonCopyable (const NonCopyable &) = delete;
|
NonCopyable (const NonCopyable &) = delete;
|
||||||
NonCopyable &operator = (const NonCopyable &) = delete;
|
NonCopyable &operator = (const NonCopyable &) = delete;
|
||||||
};
|
};
|
||||||
|
|
@ -323,33 +329,33 @@ public:
|
||||||
// see: https://github.com/preshing/RandomSequence/
|
// see: https://github.com/preshing/RandomSequence/
|
||||||
class RandomSequence : public Singleton <RandomSequence> {
|
class RandomSequence : public Singleton <RandomSequence> {
|
||||||
private:
|
private:
|
||||||
unsigned int m_index;
|
uint32 m_index;
|
||||||
unsigned int m_intermediateOffset;
|
uint32 m_intermediateOffset;
|
||||||
unsigned long long m_divider;
|
uint64 m_divider;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int premute (unsigned int x) {
|
uint32 premute (uint32 x) {
|
||||||
static constexpr unsigned int prime = 4294967291u;
|
static constexpr uint32 prime = 4294967291u;
|
||||||
|
|
||||||
if (x >= prime) {
|
if (x >= prime) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
const unsigned int residue = (static_cast <unsigned long long> (x) * x) % prime;
|
const uint32 residue = (static_cast <unsigned long long> (x) * x) % prime;
|
||||||
return (x <= prime / 2) ? residue : prime - residue;
|
return (x <= prime / 2) ? residue : prime - residue;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int random (void) {
|
uint32 random (void) {
|
||||||
return premute ((premute (m_index++) + m_intermediateOffset) ^ 0x5bf03635);
|
return premute ((premute (m_index++) + m_intermediateOffset) ^ 0x5bf03635);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RandomSequence (void) {
|
RandomSequence (void) {
|
||||||
const unsigned int seedBase = static_cast <unsigned int> (time (nullptr));
|
const auto seedBase = static_cast <uint32> (time (nullptr));
|
||||||
const unsigned int seedOffset = seedBase + 1;
|
const auto seedOffset = seedBase + 1;
|
||||||
|
|
||||||
m_index = premute (premute (seedBase) + 0x682f0161);
|
m_index = premute (premute (seedBase) + 0x682f0161);
|
||||||
m_intermediateOffset = premute (premute (seedOffset) + 0x46790905);
|
m_intermediateOffset = premute (premute (seedOffset) + 0x46790905);
|
||||||
m_divider = (static_cast <unsigned long long> (1)) << 32;
|
m_divider = (static_cast <uint64> (1)) << 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename U> inline U getInt (U low, U high) {
|
template <typename U> inline U getInt (U low, U high) {
|
||||||
|
|
@ -367,7 +373,7 @@ public:
|
||||||
|
|
||||||
class SimpleColor final : private NonCopyable {
|
class SimpleColor final : private NonCopyable {
|
||||||
public:
|
public:
|
||||||
int red, green, blue;
|
int red = 0, green = 0, blue = 0;
|
||||||
|
|
||||||
inline void reset (void) {
|
inline void reset (void) {
|
||||||
red = green = blue = 0;
|
red = green = blue = 0;
|
||||||
|
|
@ -390,7 +396,7 @@ public:
|
||||||
inline Vector (float scaler = 0.0f) : x (scaler), y (scaler), z (scaler) {}
|
inline Vector (float scaler = 0.0f) : x (scaler), y (scaler), z (scaler) {}
|
||||||
inline Vector (float inputX, float inputY, float inputZ) : x (inputX), y (inputY), z (inputZ) {}
|
inline Vector (float inputX, float inputY, float inputZ) : x (inputX), y (inputY), z (inputZ) {}
|
||||||
inline Vector (float *other) : x (other[0]), y (other[1]), z (other[2]) {}
|
inline Vector (float *other) : x (other[0]), y (other[1]), z (other[2]) {}
|
||||||
inline Vector (const Vector &right) : x (right.x), y (right.y), z (right.z) {}
|
inline Vector (const Vector &right) = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline operator float * (void) {
|
inline operator float * (void) {
|
||||||
|
|
@ -401,33 +407,33 @@ public:
|
||||||
return &x;
|
return &x;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Vector operator + (const Vector &right) const {
|
inline Vector operator + (const Vector &right) const {
|
||||||
return Vector (x + right.x, y + right.y, z + right.z);
|
return Vector (x + right.x, y + right.y, z + right.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Vector operator - (const Vector &right) const {
|
inline Vector operator - (const Vector &right) const {
|
||||||
return Vector (x - right.x, y - right.y, z - right.z);
|
return Vector (x - right.x, y - right.y, z - right.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Vector operator - (void) const {
|
inline Vector operator - (void) const {
|
||||||
return Vector (-x, -y, -z);
|
return Vector (-x, -y, -z);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend inline const Vector operator* (const float vec, const Vector &right) {
|
friend inline Vector operator * (const float vec, const Vector &right) {
|
||||||
return Vector (right.x * vec, right.y * vec, right.z * vec);
|
return Vector (right.x * vec, right.y * vec, right.z * vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Vector operator * (float vec) const {
|
inline Vector operator * (float vec) const {
|
||||||
return Vector (vec * x, vec * y, vec * z);
|
return Vector (vec * x, vec * y, vec * z);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Vector operator / (float vec) const {
|
inline Vector operator / (float vec) const {
|
||||||
const float inv = 1 / vec;
|
const float inv = 1 / vec;
|
||||||
return Vector (inv * x, inv * y, inv * z);
|
return Vector (inv * x, inv * y, inv * z);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cross product
|
// cross product
|
||||||
inline const Vector operator ^ (const Vector &right) const {
|
inline Vector operator ^ (const Vector &right) const {
|
||||||
return Vector (y * right.z - z * right.y, z * right.x - x * right.z, x * right.y - y * right.x);
|
return Vector (y * right.z - z * right.y, z * right.x - x * right.z, x * right.y - y * right.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,13 +484,7 @@ public:
|
||||||
return !fequal (x, right.x) && !fequal (y, right.y) && !fequal (z, right.z);
|
return !fequal (x, right.x) && !fequal (y, right.y) && !fequal (z, right.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Vector &operator = (const Vector &right) {
|
inline Vector &operator = (const Vector &right) = default;
|
||||||
x = right.x;
|
|
||||||
y = right.y;
|
|
||||||
z = right.z;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline float length (void) const {
|
inline float length (void) const {
|
||||||
|
|
@ -572,7 +572,7 @@ public:
|
||||||
float cosines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
float cosines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
// compute the sine and cosine compontents
|
// compute the sine and cosine compontents
|
||||||
sincosf (deg2rad (x), deg2rad (y), deg2rad (y), sines, cosines);
|
sincosf (deg2rad (x), deg2rad (y), deg2rad (z), sines, cosines);
|
||||||
|
|
||||||
if (forward) {
|
if (forward) {
|
||||||
forward->x = cosines[pitch] * cosines[yaw];
|
forward->x = cosines[pitch] * cosines[yaw];
|
||||||
|
|
@ -596,12 +596,12 @@ public:
|
||||||
|
|
||||||
class Library final : private NonCopyable {
|
class Library final : private NonCopyable {
|
||||||
private:
|
private:
|
||||||
void *m_ptr;
|
void *m_ptr = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Library (void) : m_ptr (nullptr) { }
|
explicit Library (void) = default;
|
||||||
|
|
||||||
Library (const char *filename) : m_ptr (nullptr) {
|
Library (const char *filename) {
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -637,12 +637,13 @@ public:
|
||||||
if (!isValid ()) {
|
if (!isValid ()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return (R)
|
return reinterpret_cast <R> (
|
||||||
#ifdef PLATFORM_WIN32
|
#ifdef PLATFORM_WIN32
|
||||||
GetProcAddress (static_cast <HMODULE> (m_ptr), function);
|
GetProcAddress (static_cast <HMODULE> (m_ptr), function)
|
||||||
#else
|
#else
|
||||||
dlsym (m_ptr, function);
|
dlsym (m_ptr, function)
|
||||||
#endif
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename R> R handle (void) {
|
template <typename R> R handle (void) {
|
||||||
|
|
@ -656,11 +657,11 @@ public:
|
||||||
|
|
||||||
template <typename A, typename B> class Pair final {
|
template <typename A, typename B> class Pair final {
|
||||||
public:
|
public:
|
||||||
A first;
|
A first = move (A ());
|
||||||
B second;
|
B second = move (B ());
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Pair (const A &a, const B &b) : first (cr::move (a)), second (cr::move (b)) {
|
Pair (const A &a, const B &b) : first (move (a)), second (move (b)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -673,13 +674,12 @@ public:
|
||||||
static constexpr size_t INVALID_INDEX = static_cast <size_t> (-1);
|
static constexpr size_t INVALID_INDEX = static_cast <size_t> (-1);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
T *m_data;
|
T *m_data = nullptr;
|
||||||
size_t m_capacity;
|
size_t m_capacity = 0;
|
||||||
size_t m_length;
|
size_t m_length = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Array (void) : m_data (nullptr), m_capacity (0), m_length (0) {
|
Array (void) = default;
|
||||||
}
|
|
||||||
|
|
||||||
Array (Array &&other) noexcept {
|
Array (Array &&other) noexcept {
|
||||||
m_data = other.m_data;
|
m_data = other.m_data;
|
||||||
|
|
@ -689,7 +689,7 @@ public:
|
||||||
other.reset ();
|
other.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~Array (void) {
|
~Array (void) {
|
||||||
destroy ();
|
destroy ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -709,7 +709,7 @@ public:
|
||||||
if (m_length + growSize < m_capacity) {
|
if (m_length + growSize < m_capacity) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
size_t maxSize = max <size_t> (m_capacity + sizeof (T), static_cast <size_t> (16));
|
auto maxSize = max <size_t> (m_capacity + sizeof (T), static_cast <size_t> (16));
|
||||||
|
|
||||||
while (m_length + growSize > maxSize) {
|
while (m_length + growSize > maxSize) {
|
||||||
maxSize *= 2;
|
maxSize *= 2;
|
||||||
|
|
@ -738,16 +738,16 @@ public:
|
||||||
bool res = reserve (newSize);
|
bool res = reserve (newSize);
|
||||||
|
|
||||||
while (m_length < newSize) {
|
while (m_length < newSize) {
|
||||||
push (T ());
|
push (move (T ()));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t length (void) const {
|
size_t length (void) const {
|
||||||
return m_length;
|
return m_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t capacity (void) const {
|
size_t capacity (void) const {
|
||||||
return m_capacity;
|
return m_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,7 +765,11 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &at (size_t index) {
|
T &at (size_t index) {
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &at (size_t index) const {
|
||||||
return m_data[index];
|
return m_data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -861,7 +865,7 @@ public:
|
||||||
m_length = 0;
|
m_length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool empty (void) const {
|
bool empty (void) const {
|
||||||
return m_length == 0;
|
return m_length == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -922,6 +926,12 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void shuffle (void) {
|
||||||
|
for (size_t i = m_length; i >= 1; i--) {
|
||||||
|
swap (m_data[i - 1], m_data[RandomSequence::ref ().getInt (i, m_length - 2)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void reverse (void) {
|
void reverse (void) {
|
||||||
for (size_t i = 0; i < m_length / 2; i++) {
|
for (size_t i = 0; i < m_length / 2; i++) {
|
||||||
swap (m_data[i], m_data[m_length - 1 - i]);
|
swap (m_data[i], m_data[m_length - 1 - i]);
|
||||||
|
|
@ -1010,15 +1020,15 @@ public:
|
||||||
return value.first;
|
return value.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool empty (void) const {
|
bool empty (void) const {
|
||||||
return !length ();
|
return !length ();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t length (void) const {
|
size_t length (void) const {
|
||||||
return m_length;
|
return m_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void clear (void) {
|
void clear (void) {
|
||||||
base::clear ();
|
base::clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1081,7 +1091,12 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// some fast string class
|
||||||
class String final : private Array <char> {
|
class String final : private Array <char> {
|
||||||
|
public:
|
||||||
|
static constexpr size_t INVALID_INDEX = Array <char>::INVALID_INDEX;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using base = Array <char>;
|
using base = Array <char>;
|
||||||
|
|
||||||
|
|
@ -1097,9 +1112,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
String (String &&other) noexcept : base (move (other)) { }
|
||||||
|
|
||||||
String (void) = default;
|
String (void) = default;
|
||||||
String (String &&other) noexcept : base (move (other)) { }
|
|
||||||
~String (void) = default;
|
~String (void) = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -1119,12 +1134,12 @@ public:
|
||||||
String &assign (const char *str, size_t length = 0) {
|
String &assign (const char *str, size_t length = 0) {
|
||||||
length = length > 0 ? length : strlen (str);
|
length = length > 0 ? length : strlen (str);
|
||||||
|
|
||||||
clear ();
|
base::clear ();
|
||||||
resize (length);
|
base::reserve (length + 1);
|
||||||
|
|
||||||
memcpy (m_data, str, length);
|
if (base::push (const_cast <char *> (str), length)) {
|
||||||
terminate ();
|
terminate ();
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1133,7 +1148,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
String &assign (const char chr) {
|
String &assign (const char chr) {
|
||||||
resize (2);
|
resize (1);
|
||||||
m_data[0] = chr;
|
m_data[0] = chr;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -1143,11 +1158,9 @@ public:
|
||||||
if (empty ()) {
|
if (empty ()) {
|
||||||
return assign (str);
|
return assign (str);
|
||||||
}
|
}
|
||||||
const size_t maxLength = strlen (str) + 1;
|
if (push (const_cast <char *> (str), strlen (str))) {
|
||||||
|
terminate ();
|
||||||
resize (length () + maxLength);
|
};
|
||||||
strncat (m_data, str, maxLength);
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1168,6 +1181,10 @@ public:
|
||||||
return base::length ();
|
return base::length ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t capacity (void) const {
|
||||||
|
return base::capacity ();
|
||||||
|
}
|
||||||
|
|
||||||
bool empty (void) const {
|
bool empty (void) const {
|
||||||
return !length ();
|
return !length ();
|
||||||
}
|
}
|
||||||
|
|
@ -1198,11 +1215,19 @@ public:
|
||||||
return atoi (chars ());
|
return atoi (chars ());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void terminate (void) {
|
float toFloat (void) const {
|
||||||
|
return static_cast <float> (atof (chars ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminate (void) {
|
||||||
m_data[m_length] = '\0';
|
m_data[m_length] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char &at (size_t index) {
|
char &at (size_t index) {
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char &at (size_t index) const {
|
||||||
return m_data[index];
|
return m_data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1211,33 +1236,30 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 compare (const char *what) const {
|
int32 compare (const char *what) const {
|
||||||
return strcmp (begin (), what);
|
return strcmp (m_data, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains (const String &what) const {
|
bool contains (const String &what) const {
|
||||||
return strstr (m_data, what.begin ()) != nullptr;
|
return strstr (m_data, what.begin ()) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t BufferSize = 512> void format (const char *fmt, ...) {
|
template <typename ...Args> void assign (const char *fmt, Args ...args) {
|
||||||
va_list ap;
|
const size_t size = snprintf (nullptr, 0, fmt, args...);
|
||||||
char buffer[BufferSize];
|
|
||||||
|
|
||||||
va_start (ap, fmt);
|
reserve (size + 1);
|
||||||
vsnprintf (buffer, bufsize (buffer), fmt, ap);
|
resize (size);
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
assign (buffer);
|
snprintf (&m_data[0], size + 1, fmt, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t BufferSize = 512> void formatAppend (const char *fmt, ...) {
|
template <typename ...Args> void append (const char *fmt, Args ...args) {
|
||||||
va_list ap;
|
const size_t size = snprintf (nullptr, 0, fmt, args...) + m_length;
|
||||||
char buffer[BufferSize];
|
const size_t len = m_length;
|
||||||
|
|
||||||
va_start (ap, fmt);
|
reserve (size + 1);
|
||||||
vsnprintf (buffer, bufsize (buffer), fmt, ap);
|
resize (size);
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
append (buffer);
|
snprintf (&m_data[len], size + 1, fmt, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t insert (size_t at, const String &str) {
|
size_t insert (size_t at, const String &str) {
|
||||||
|
|
@ -1248,14 +1270,32 @@ public:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t find (const String &search, size_t pos) const {
|
size_t find (char val, size_t pos) const {
|
||||||
if (pos > length ()) {
|
for (size_t i = pos; i < m_length; i++) {
|
||||||
|
if (m_data[i] == val) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
return INVALID_INDEX;
|
return INVALID_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t find (const String &search, size_t pos) const {
|
||||||
size_t len = search.length ();
|
size_t len = search.length ();
|
||||||
|
|
||||||
for (size_t i = pos; len + i <= length (); i++) {
|
if (len > m_length || pos > m_length) {
|
||||||
if (strncmp (m_data + i, search.chars (), len) == 0) {
|
return INVALID_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = pos; i <= m_length - len; i++) {
|
||||||
|
size_t at = 0;
|
||||||
|
|
||||||
|
for (; search.m_data[at]; at++) {
|
||||||
|
if (m_data[i + at] != search.m_data[at]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!search.m_data[at]) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1268,7 +1308,7 @@ public:
|
||||||
}
|
}
|
||||||
size_t numReplaced = 0, posIndex = 0;
|
size_t numReplaced = 0, posIndex = 0;
|
||||||
|
|
||||||
while (posIndex < length ()) {
|
while (posIndex < m_length) {
|
||||||
posIndex = find (what, posIndex);
|
posIndex = find (what, posIndex);
|
||||||
|
|
||||||
if (posIndex == INVALID_INDEX) {
|
if (posIndex == INVALID_INDEX) {
|
||||||
|
|
@ -1283,28 +1323,96 @@ public:
|
||||||
return numReplaced;
|
return numReplaced;
|
||||||
}
|
}
|
||||||
|
|
||||||
String substr (size_t start, size_t count = INVALID_INDEX) {
|
String substr (size_t start, size_t count = INVALID_INDEX) const {
|
||||||
String result;
|
String result;
|
||||||
|
|
||||||
if (start >= length () || empty ()) {
|
if (start >= m_length || empty ()) {
|
||||||
return result;
|
return move (result);
|
||||||
}
|
}
|
||||||
if (count == INVALID_INDEX) {
|
if (count == INVALID_INDEX) {
|
||||||
count = length () - start;
|
count = m_length - start;
|
||||||
}
|
}
|
||||||
else if (start + count >= length ()) {
|
else if (start + count >= m_length) {
|
||||||
count = length () - start;
|
count = m_length - start;
|
||||||
}
|
}
|
||||||
result.resize (count);
|
result.resize (count);
|
||||||
|
|
||||||
transfer (&result[0], &m_data[start], count);
|
// copy, not move
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
result[i] = m_data[start + i];
|
||||||
|
}
|
||||||
result[count] = '\0';
|
result[count] = '\0';
|
||||||
|
return move (result);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char &operator[] (size_t index) {
|
String &rtrim (const char *chars = "\r\n\t ") {
|
||||||
return at (index);
|
if (empty ()) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
char *str = end () - 1;
|
||||||
|
|
||||||
|
while (*str != 0) {
|
||||||
|
if (isTrimChar (*str, chars)) {
|
||||||
|
erase (str - begin ());
|
||||||
|
str--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String <rim (const char *chars = "\r\n\t ") {
|
||||||
|
if (empty ()) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
char *str = begin ();
|
||||||
|
|
||||||
|
while (isTrimChar (*str, chars)) {
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (begin () != str) {
|
||||||
|
erase (0, str - begin ());
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &trim (const char *chars = "\r\n\t ") {
|
||||||
|
return rtrim (chars).ltrim (chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array <String> split (const String &delim) const {
|
||||||
|
Array <String> tokens;
|
||||||
|
size_t prev = 0, pos = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
pos = find (delim, prev);
|
||||||
|
|
||||||
|
if (pos == INVALID_INDEX) {
|
||||||
|
pos = m_length;
|
||||||
|
}
|
||||||
|
tokens.push (move (substr (prev, pos - prev)));
|
||||||
|
|
||||||
|
prev = pos + delim.length ();
|
||||||
|
} while (pos < m_length && prev < m_length);
|
||||||
|
|
||||||
|
return move (tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
String &lowercase (void) {
|
||||||
|
for (auto &ch : *this) {
|
||||||
|
ch = static_cast <char> (tolower (ch));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &uppercase (void) {
|
||||||
|
for (auto &ch : *this) {
|
||||||
|
ch = static_cast <char> (toupper (ch));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend String operator + (const String &lhs, const String &rhs) {
|
friend String operator + (const String &lhs, const String &rhs) {
|
||||||
|
|
@ -1384,79 +1492,51 @@ public:
|
||||||
return append (rhs);
|
return append (rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
String &trimRight (const char *chars = "\r\n\t ") {
|
char &operator [] (size_t index) {
|
||||||
if (empty ()) {
|
return at (index);
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
char *str = end () - 1;
|
|
||||||
|
|
||||||
while (*str != 0) {
|
|
||||||
if (isTrimChar (*str, chars)) {
|
|
||||||
erase (str - begin ());
|
|
||||||
str--;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String &trimLeft (const char *chars = "\r\n\t ") {
|
const char &operator [] (size_t index) const {
|
||||||
if (empty ()) {
|
return at (index);
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
char *str = begin ();
|
|
||||||
|
|
||||||
while (isTrimChar (*str, chars)) {
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (begin () != str) {
|
|
||||||
erase (0, str - begin ());
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &trim (const char *chars = "\r\n\t ") {
|
|
||||||
return trimLeft (chars).trimRight (chars);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array <String> split (const char *delimiter) {
|
|
||||||
Array <String> tokens;
|
|
||||||
size_t len, index = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
index += strspn (&m_data[index], delimiter);
|
|
||||||
len = strcspn (&m_data[index], delimiter);
|
|
||||||
|
|
||||||
if (len > 0) {
|
|
||||||
tokens.push (move (substr (index, len)));
|
|
||||||
}
|
|
||||||
index += len;
|
|
||||||
} while (len > 0);
|
|
||||||
|
|
||||||
return tokens;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void trimChars (char *str) {
|
char *begin (void) {
|
||||||
size_t pos = 0;
|
return base::begin ();
|
||||||
char *dest = str;
|
|
||||||
|
|
||||||
while (str[pos] <= ' ' && str[pos] > 0) {
|
|
||||||
pos++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (str[pos]) {
|
char *begin (void) const {
|
||||||
*(dest++) = str[pos];
|
return base::begin ();
|
||||||
pos++;
|
|
||||||
}
|
}
|
||||||
*(dest--) = '\0';
|
|
||||||
|
|
||||||
while (dest >= str && *dest <= ' ' && *dest > 0) {
|
char *end (void) {
|
||||||
*(dest--) = '\0';
|
return base::end ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *end (void) const {
|
||||||
|
return base::end ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static String join (const Array <String> &sequence, const String &delim, size_t start = 0) {
|
||||||
|
if (sequence.empty ()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequence.length () == 1) {
|
||||||
|
return sequence.at (0);
|
||||||
|
}
|
||||||
|
String result;
|
||||||
|
|
||||||
|
for (size_t index = start; index < sequence.length (); index++) {
|
||||||
|
if (index != start) {
|
||||||
|
result += delim + sequence[index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result += sequence[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return move (result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1553,17 +1633,16 @@ public:
|
||||||
|
|
||||||
class File : private NonCopyable {
|
class File : private NonCopyable {
|
||||||
private:
|
private:
|
||||||
FILE *m_handle;
|
FILE *m_handle = nullptr;
|
||||||
size_t m_size;
|
size_t m_size = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File (void) : m_handle (nullptr), m_size (0) {}
|
File (void) = default;
|
||||||
|
File (const String &fileName, const String &mode = "rt") {
|
||||||
File (const String &fileName, const String &mode = "rt") : m_handle (nullptr), m_size (0){
|
|
||||||
open (fileName, mode);
|
open (fileName, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~File (void) {
|
~File (void) {
|
||||||
close ();
|
close ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1594,14 +1673,24 @@ public:
|
||||||
return fflush (m_handle) ? false : true;
|
return fflush (m_handle) ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getch (void) {
|
char getch (void) {
|
||||||
return fgetc (m_handle);
|
return static_cast <char> (fgetc (m_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *gets (char *buffer, int count) {
|
char *gets (char *buffer, int count) {
|
||||||
return fgets (buffer, count, m_handle);
|
return fgets (buffer, count, m_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getLine (String &line) {
|
||||||
|
line.clear ();
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
while ((ch = getch ()) != '\n' && ch != EOF && !eof ()) {
|
||||||
|
line += ch;
|
||||||
|
}
|
||||||
|
return !eof ();
|
||||||
|
}
|
||||||
|
|
||||||
int writeFormat (const char *format, ...) {
|
int writeFormat (const char *format, ...) {
|
||||||
assert (m_handle != nullptr);
|
assert (m_handle != nullptr);
|
||||||
|
|
||||||
|
|
@ -1714,14 +1803,13 @@ public:
|
||||||
|
|
||||||
class MemFile : private NonCopyable {
|
class MemFile : private NonCopyable {
|
||||||
private:
|
private:
|
||||||
size_t m_size;
|
size_t m_size = 0;
|
||||||
size_t m_pos;
|
size_t m_pos = 0;
|
||||||
uint8 *m_buffer;
|
uint8 *m_buffer = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MemFile (void) : m_size (0) , m_pos (0) , m_buffer (nullptr) {}
|
MemFile (void) = default;
|
||||||
|
MemFile (const String &filename) {
|
||||||
MemFile (const String &filename) : m_size (0) , m_pos (0) , m_buffer (nullptr) {
|
|
||||||
open (filename);
|
open (filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1748,14 +1836,14 @@ public:
|
||||||
m_buffer = nullptr;
|
m_buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getch (void) {
|
char getch (void) {
|
||||||
if (!m_buffer || m_pos >= m_size) {
|
if (!m_buffer || m_pos >= m_size) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int readCh = m_buffer[m_pos];
|
int readCh = m_buffer[m_pos];
|
||||||
m_pos++;
|
m_pos++;
|
||||||
|
|
||||||
return readCh;
|
return static_cast <char> (readCh);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *gets (char *buffer, size_t count) {
|
char *gets (char *buffer, size_t count) {
|
||||||
|
|
@ -1781,6 +1869,16 @@ public:
|
||||||
return index ? buffer : nullptr;
|
return index ? buffer : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getLine (String &line) {
|
||||||
|
line.clear ();
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
while ((ch = getch ()) != '\n' && ch != EOF && m_pos < m_size) {
|
||||||
|
line += ch;
|
||||||
|
}
|
||||||
|
return m_pos < m_size;
|
||||||
|
}
|
||||||
|
|
||||||
size_t read (void *buffer, size_t size, size_t count = 1) {
|
size_t read (void *buffer, size_t size, size_t count = 1) {
|
||||||
if (!m_buffer || m_size <= m_pos || !buffer || !size || !count) {
|
if (!m_buffer || m_size <= m_pos || !buffer || !size || !count) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1818,7 +1916,7 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t getSize (void) const {
|
size_t getSize (void) const {
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1827,8 +1925,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}}
|
}};
|
||||||
|
|
||||||
|
|
||||||
namespace cr {
|
namespace cr {
|
||||||
namespace types {
|
namespace types {
|
||||||
|
|
@ -1836,4 +1933,4 @@ namespace types {
|
||||||
using StringArray = cr::classes::Array <cr::classes::String>;
|
using StringArray = cr::classes::Array <cr::classes::String>;
|
||||||
using IntArray = cr::classes::Array <int>;
|
using IntArray = cr::classes::Array <int>;
|
||||||
|
|
||||||
}}
|
}};
|
||||||
|
|
|
||||||
243
include/engine.h
243
include/engine.h
|
|
@ -10,22 +10,22 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// line draw
|
// line draw
|
||||||
enum DrawLineType {
|
enum DrawLineType : int {
|
||||||
DRAW_SIMPLE,
|
DRAW_SIMPLE,
|
||||||
DRAW_ARROW,
|
DRAW_ARROW,
|
||||||
DRAW_NUM
|
DRAW_NUM
|
||||||
};
|
};
|
||||||
|
|
||||||
// trace ignore
|
// trace ignore
|
||||||
enum TraceIgnore {
|
enum TraceIgnore : int {
|
||||||
TRACE_IGNORE_NONE = 0,
|
TRACE_IGNORE_NONE = 0,
|
||||||
TRACE_IGNORE_GLASS = (1 << 0),
|
TRACE_IGNORE_GLASS = cr::bit (0),
|
||||||
TRACE_IGNORE_MONSTERS = (1 << 1),
|
TRACE_IGNORE_MONSTERS = cr::bit (1),
|
||||||
TRACE_IGNORE_EVERYTHING = TRACE_IGNORE_GLASS | TRACE_IGNORE_MONSTERS
|
TRACE_IGNORE_EVERYTHING = TRACE_IGNORE_GLASS | TRACE_IGNORE_MONSTERS
|
||||||
};
|
};
|
||||||
|
|
||||||
// variable type
|
// variable type
|
||||||
enum VarType {
|
enum VarType : int {
|
||||||
VT_NORMAL = 0,
|
VT_NORMAL = 0,
|
||||||
VT_READONLY,
|
VT_READONLY,
|
||||||
VT_PASSWORD,
|
VT_PASSWORD,
|
||||||
|
|
@ -34,7 +34,7 @@ enum VarType {
|
||||||
};
|
};
|
||||||
|
|
||||||
// netmessage functions
|
// netmessage functions
|
||||||
enum NetMsgId {
|
enum NetMsgId : int {
|
||||||
NETMSG_UNDEFINED = -1,
|
NETMSG_UNDEFINED = -1,
|
||||||
NETMSG_VGUI = 1,
|
NETMSG_VGUI = 1,
|
||||||
NETMSG_SHOWMENU = 2,
|
NETMSG_SHOWMENU = 2,
|
||||||
|
|
@ -61,6 +61,36 @@ enum NetMsgId {
|
||||||
NETMSG_NUM = 25
|
NETMSG_NUM = 25
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// supported cs's
|
||||||
|
enum GameFlags : int {
|
||||||
|
GAME_CSTRIKE16 = cr::bit (0), // counter-strike 1.6 and above
|
||||||
|
GAME_XASH_ENGINE = cr::bit (1), // counter-strike 1.6 under the xash engine (additional flag)
|
||||||
|
GAME_CZERO = cr::bit (2), // counter-strike: condition zero
|
||||||
|
GAME_LEGACY = cr::bit (3), // counter-strike 1.3-1.5 with/without steam
|
||||||
|
GAME_MOBILITY = cr::bit (4), // additional flag that bot is running on android (additional flag)
|
||||||
|
GAME_OFFICIAL_CSBOT = cr::bit (5), // additional flag that indicates official cs bots are in game
|
||||||
|
GAME_METAMOD = cr::bit (6), // game running under meta\mod
|
||||||
|
GAME_CSDM = cr::bit (7), // csdm mod currently in use
|
||||||
|
GAME_CSDM_FFA = cr::bit (8), // csdm mod with ffa mode
|
||||||
|
GAME_REGAMEDLL = cr::bit (9), // server dll is a regamedll
|
||||||
|
GAME_SUPPORT_SVC_PINGS = cr::bit (10), // on that game version we can fake bots pings
|
||||||
|
GAME_SUPPORT_BOT_VOICE = cr::bit (11) // on that game version we can use chatter
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// defines map type
|
||||||
|
enum MapFlags : int {
|
||||||
|
MAP_AS = cr::bit (0),
|
||||||
|
MAP_CS = cr::bit (1),
|
||||||
|
MAP_DE = cr::bit (2),
|
||||||
|
MAP_ES = cr::bit (3),
|
||||||
|
MAP_KA = cr::bit (4),
|
||||||
|
MAP_FY = cr::bit (5),
|
||||||
|
|
||||||
|
// additional flags
|
||||||
|
MAP_HAS_DOORS = cr::bit (6)
|
||||||
|
};
|
||||||
|
|
||||||
// variable reg pair
|
// variable reg pair
|
||||||
struct VarPair {
|
struct VarPair {
|
||||||
VarType type;
|
VarType type;
|
||||||
|
|
@ -79,6 +109,14 @@ struct MessageBlock {
|
||||||
int regMsgs[NETMSG_NUM];
|
int regMsgs[NETMSG_NUM];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// referentia vector info
|
||||||
|
struct RefVector {
|
||||||
|
Vector forward, right, up;
|
||||||
|
};
|
||||||
|
|
||||||
|
// entity prototype
|
||||||
|
using EntityFunction = void (*) (entvars_t *);
|
||||||
|
|
||||||
// compare language
|
// compare language
|
||||||
struct LangComprarer {
|
struct LangComprarer {
|
||||||
size_t operator () (const String &key) const {
|
size_t operator () (const String &key) const {
|
||||||
|
|
@ -96,15 +134,14 @@ struct LangComprarer {
|
||||||
};
|
};
|
||||||
|
|
||||||
// provides utility functions to not call original engine (less call-cost)
|
// provides utility functions to not call original engine (less call-cost)
|
||||||
class Engine : public Singleton <Engine> {
|
class Game final : public Singleton <Game> {
|
||||||
private:
|
private:
|
||||||
int m_drawModels[DRAW_NUM];
|
int m_drawModels[DRAW_NUM];
|
||||||
int m_spawnCount[TEAM_UNASSIGNED];
|
int m_spawnCount[TEAM_UNASSIGNED];
|
||||||
|
|
||||||
// bot client command
|
// bot client command
|
||||||
char m_arguments[256];
|
|
||||||
bool m_isBotCommand;
|
bool m_isBotCommand;
|
||||||
int m_argumentCount;
|
StringArray m_botArgs;
|
||||||
|
|
||||||
edict_t *m_startEntity;
|
edict_t *m_startEntity;
|
||||||
edict_t *m_localEntity;
|
edict_t *m_localEntity;
|
||||||
|
|
@ -112,19 +149,27 @@ private:
|
||||||
Array <VarPair> m_cvars;
|
Array <VarPair> m_cvars;
|
||||||
HashMap <String, String, LangComprarer> m_language;
|
HashMap <String, String, LangComprarer> m_language;
|
||||||
|
|
||||||
|
Library m_gameLib;
|
||||||
MessageBlock m_msgBlock;
|
MessageBlock m_msgBlock;
|
||||||
bool m_precached;
|
bool m_precached;
|
||||||
|
|
||||||
|
int m_gameFlags;
|
||||||
|
int m_mapFlags;
|
||||||
|
|
||||||
|
float m_slowFrame; // per second updated frame
|
||||||
public:
|
public:
|
||||||
Engine (void);
|
RefVector vec;
|
||||||
~Engine (void);
|
|
||||||
|
public:
|
||||||
|
Game (void);
|
||||||
|
~Game (void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// precaches internal stuff
|
// precaches internal stuff
|
||||||
void precache (void);
|
void precache (void);
|
||||||
|
|
||||||
// initialize levels
|
// initialize levels
|
||||||
void levelInitialize (void);
|
void levelInitialize (edict_t *ents, int max);
|
||||||
|
|
||||||
// prints data to servers console
|
// prints data to servers console
|
||||||
void print (const char *fmt, ...);
|
void print (const char *fmt, ...);
|
||||||
|
|
@ -135,6 +180,9 @@ public:
|
||||||
// prints center message to all players
|
// prints center message to all players
|
||||||
void centerPrint (const char *fmt, ...);
|
void centerPrint (const char *fmt, ...);
|
||||||
|
|
||||||
|
// prints center message to specified player
|
||||||
|
void centerPrint (edict_t *ent, const char *fmt, ...);
|
||||||
|
|
||||||
// prints message to client console
|
// prints message to client console
|
||||||
void clientPrint (edict_t *ent, const char *fmt, ...);
|
void clientPrint (edict_t *ent, const char *fmt, ...);
|
||||||
|
|
||||||
|
|
@ -189,122 +237,170 @@ public:
|
||||||
// checks whether softwared rendering is enabled
|
// checks whether softwared rendering is enabled
|
||||||
bool isSoftwareRenderer (void);
|
bool isSoftwareRenderer (void);
|
||||||
|
|
||||||
|
// load the cs binary in non metamod mode
|
||||||
|
bool loadCSBinary (void);
|
||||||
|
|
||||||
|
// do post-load stuff
|
||||||
|
bool postload (void);
|
||||||
|
|
||||||
|
// detects if csdm mod is in use
|
||||||
|
void detectDeathmatch (void);
|
||||||
|
|
||||||
|
// executes stuff every 1 second
|
||||||
|
void slowFrame (void);
|
||||||
|
|
||||||
|
// begin message handler
|
||||||
|
void beginMessage (edict_t *ent, int dest, int type);
|
||||||
|
|
||||||
// public inlines
|
// public inlines
|
||||||
public:
|
public:
|
||||||
// get the current time on server
|
// get the current time on server
|
||||||
inline float timebase (void) {
|
float timebase (void) const {
|
||||||
return g_pGlobals->time;
|
return globals->time;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get "maxplayers" limit on server
|
// get "maxplayers" limit on server
|
||||||
inline int maxClients (void) {
|
int maxClients (void) const {
|
||||||
return g_pGlobals->maxClients;
|
return globals->maxClients;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the fakeclient command interface
|
// get the fakeclient command interface
|
||||||
inline bool isBotCmd (void) {
|
bool isBotCmd (void) const {
|
||||||
return m_isBotCommand;
|
return m_isBotCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets custom engine args for client command
|
// gets custom engine args for client command
|
||||||
inline const char *botArgs (void) {
|
const char *botArgs (void) const {
|
||||||
if (strncmp ("say ", m_arguments, 4) == 0) {
|
static String args;
|
||||||
return &m_arguments[4];
|
args = String::join (m_botArgs, " ", m_botArgs[0] == "say" || m_botArgs[0] == "say_team" ? 1 : 0);
|
||||||
}
|
|
||||||
else if (strncmp ("say_team ", m_arguments, 9) == 0) {
|
return args.chars ();
|
||||||
return &m_arguments[9];
|
|
||||||
}
|
|
||||||
return m_arguments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets custom engine argv for client command
|
// gets custom engine argv for client command
|
||||||
inline const char *botArgv (int num) {
|
const char *botArgv (size_t index) const {
|
||||||
return getField (m_arguments, static_cast <size_t> (num));
|
if (index >= m_botArgs.length ()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return m_botArgs[index].chars ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets custom engine argc for client command
|
// gets custom engine argc for client command
|
||||||
inline int botArgc (void) {
|
int botArgc (void) const {
|
||||||
return m_argumentCount;
|
return m_botArgs.length ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets edict pointer out of entity index
|
// gets edict pointer out of entity index
|
||||||
inline edict_t *entityOfIndex (const int index) {
|
edict_t *entityOfIndex (const int index) {
|
||||||
return static_cast <edict_t *> (m_startEntity + index);
|
return static_cast <edict_t *> (m_startEntity + index);
|
||||||
};
|
};
|
||||||
|
|
||||||
// gets edict index out of it's pointer
|
// gets edict index out of it's pointer
|
||||||
inline int indexOfEntity (const edict_t *ent) {
|
int indexOfEntity (const edict_t *ent) {
|
||||||
return static_cast <int> (ent - m_startEntity);
|
return static_cast <int> (ent - m_startEntity);
|
||||||
};
|
};
|
||||||
|
|
||||||
// verify entity isn't null
|
// verify entity isn't null
|
||||||
inline bool isNullEntity (const edict_t *ent) {
|
bool isNullEntity (const edict_t *ent) {
|
||||||
return !ent || !indexOfEntity (ent) || ent->free;
|
return !ent || !indexOfEntity (ent) || ent->free;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the wroldspawn entity
|
// get the wroldspawn entity
|
||||||
inline edict_t *getStartEntity (void) {
|
edict_t *getStartEntity (void) {
|
||||||
return m_startEntity;
|
return m_startEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get spawn count for team
|
// get spawn count for team
|
||||||
inline int getSpawnCount (int team) {
|
int getSpawnCount (int team) const {
|
||||||
return m_spawnCount[team];
|
return m_spawnCount[team];
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the player team
|
// gets the player team
|
||||||
inline int getTeam (edict_t *ent) {
|
int getTeam (edict_t *ent);
|
||||||
extern Client g_clients[MAX_ENGINE_PLAYERS];
|
|
||||||
return g_clients[indexOfEntity (ent) - 1].team;
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds translation pair from config
|
// adds translation pair from config
|
||||||
inline void addTranslation (const String &original, const String &translated) {
|
void addTranslation (const String &original, const String &translated) {
|
||||||
m_language.put (original, translated);
|
m_language.put (original, translated);
|
||||||
}
|
}
|
||||||
|
|
||||||
// resets the message capture mechanism
|
// resets the message capture mechanism
|
||||||
inline void resetMessages (void) {
|
void resetMessages (void) {
|
||||||
m_msgBlock.msg = NETMSG_UNDEFINED;
|
m_msgBlock.msg = NETMSG_UNDEFINED;
|
||||||
m_msgBlock.state = 0;
|
m_msgBlock.state = 0;
|
||||||
m_msgBlock.bot = 0;
|
m_msgBlock.bot = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// sets the currently executed message
|
// sets the currently executed message
|
||||||
inline void setCurrentMessageId (int message) {
|
void setCurrentMessageId (int message) {
|
||||||
m_msgBlock.msg = message;
|
m_msgBlock.msg = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the bot entity that receive this message
|
// set the bot entity that receive this message
|
||||||
inline void setCurrentMessageOwner (int id) {
|
void setCurrentMessageOwner (int id) {
|
||||||
m_msgBlock.bot = id;
|
m_msgBlock.bot = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find registered message id
|
// find registered message id
|
||||||
inline int getMessageId (int type) {
|
int getMessageId (int type) {
|
||||||
return m_msgBlock.regMsgs[type];
|
return m_msgBlock.regMsgs[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
// assigns message id for message type
|
// assigns message id for message type
|
||||||
inline void setMessageId (int type, int id) {
|
void setMessageId (int type, int id) {
|
||||||
m_msgBlock.regMsgs[type] = id;
|
m_msgBlock.regMsgs[type] = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tries to set needed message id
|
// tries to set needed message id
|
||||||
inline void captureMessage (int type, int msgId) {
|
void captureMessage (int type, int msgId) {
|
||||||
if (type == m_msgBlock.regMsgs[msgId]) {
|
if (type == m_msgBlock.regMsgs[msgId]) {
|
||||||
setCurrentMessageId (msgId);
|
setCurrentMessageId (msgId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets the precache to uninitialize
|
// sets the precache to uninitialize
|
||||||
inline void setUnprecached (void) {
|
void setUnprecached (void) {
|
||||||
m_precached = false;
|
m_precached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static utility functions
|
// gets the local entity (host edict)
|
||||||
private:
|
edict_t *getLocalEntity (void) {
|
||||||
const char *getField (const char *string, size_t id);
|
return m_localEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets the local entity (host edict)
|
||||||
|
void setLocalEntity (edict_t *ent) {
|
||||||
|
m_localEntity = ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// builds referential vector
|
||||||
|
void makeVectors (const Vector &in) {
|
||||||
|
in.makeVectors (&vec.forward, &vec.right, &vec.up);
|
||||||
|
}
|
||||||
|
|
||||||
|
// what kind of map we're running ?
|
||||||
|
bool isMap (const int map) const {
|
||||||
|
return (m_mapFlags & map) == map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// what kind of game engine / game dll / mod / tool we're running ?
|
||||||
|
bool is (const int type) const {
|
||||||
|
return (m_gameFlags & type) == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds game flag
|
||||||
|
void addGameFlag (const int type) {
|
||||||
|
m_gameFlags |= type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the map type
|
||||||
|
bool mapIs (const int type) const {
|
||||||
|
return (m_mapFlags & type) == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get loaded gamelib
|
||||||
|
Library &getLib (void) {
|
||||||
|
return m_gameLib;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// simplify access for console variables
|
// simplify access for console variables
|
||||||
|
|
@ -314,35 +410,35 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConVar (const char *name, const char *initval, VarType type = VT_NOSERVER, bool regMissing = false, const char *regVal = nullptr) : m_eptr (nullptr) {
|
ConVar (const char *name, const char *initval, VarType type = VT_NOSERVER, bool regMissing = false, const char *regVal = nullptr) : m_eptr (nullptr) {
|
||||||
Engine::ref ().pushVarToRegStack (name, initval, type, regMissing, regVal, this);
|
Game::ref ().pushVarToRegStack (name, initval, type, regMissing, regVal, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool boolean (void) const {
|
bool boolean (void) const {
|
||||||
return m_eptr->value > 0.0f;
|
return m_eptr->value > 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int integer (void) const {
|
int integer (void) const {
|
||||||
return static_cast <int> (m_eptr->value);
|
return static_cast <int> (m_eptr->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float flt (void) const {
|
float flt (void) const {
|
||||||
return m_eptr->value;
|
return m_eptr->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char *str (void) const {
|
const char *str (void) const {
|
||||||
return m_eptr->string;
|
return m_eptr->string;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set (float val) const {
|
void set (float val) {
|
||||||
g_engfuncs.pfnCVarSetFloat (m_eptr->name, val);
|
engfuncs.pfnCVarSetFloat (m_eptr->name, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set (int val) const {
|
void set (int val) {
|
||||||
set (static_cast <float> (val));
|
set (static_cast <float> (val));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set (const char *val) const {
|
void set (const char *val) {
|
||||||
g_engfuncs.pfnCvar_DirectSet (m_eptr, const_cast <char *> (val));
|
engfuncs.pfnCvar_DirectSet (m_eptr, const_cast <char *> (val));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -366,36 +462,36 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MessageWriter &start (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) {
|
MessageWriter &start (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) {
|
||||||
g_engfuncs.pfnMessageBegin (dest, type, pos, to);
|
engfuncs.pfnMessageBegin (dest, type, pos, to);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void end (void) {
|
void end (void) {
|
||||||
g_engfuncs.pfnMessageEnd ();
|
engfuncs.pfnMessageEnd ();
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageWriter &writeByte (int val) {
|
MessageWriter &writeByte (int val) {
|
||||||
g_engfuncs.pfnWriteByte (val);
|
engfuncs.pfnWriteByte (val);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageWriter &writeChar (int val) {
|
MessageWriter &writeChar (int val) {
|
||||||
g_engfuncs.pfnWriteChar (val);
|
engfuncs.pfnWriteChar (val);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageWriter &writeShort (int val) {
|
MessageWriter &writeShort (int val) {
|
||||||
g_engfuncs.pfnWriteShort (val);
|
engfuncs.pfnWriteShort (val);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageWriter &writeCoord (float val) {
|
MessageWriter &writeCoord (float val) {
|
||||||
g_engfuncs.pfnWriteCoord (val);
|
engfuncs.pfnWriteCoord (val);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageWriter &writeString (const char *val) {
|
MessageWriter &writeString (const char *val) {
|
||||||
g_engfuncs.pfnWriteString (val);
|
engfuncs.pfnWriteString (val);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -409,26 +505,25 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LightMeasure final : public Singleton <LightMeasure> {
|
class LightMeasure final : public Singleton <LightMeasure> {
|
||||||
private:
|
private:
|
||||||
lightstyle_t m_lightstyle[MAX_LIGHTSTYLES];
|
lightstyle_t m_lightstyle[MAX_LIGHTSTYLES];
|
||||||
int m_lightstyleValue[MAX_LIGHTSTYLEVALUE];
|
int m_lightstyleValue[MAX_LIGHTSTYLEVALUE];
|
||||||
bool m_doAnimation;
|
bool m_doAnimation = false;
|
||||||
|
|
||||||
SimpleColor m_point;
|
SimpleColor m_point;
|
||||||
model_t *m_worldModel;
|
model_t *m_worldModel = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LightMeasure (void) : m_doAnimation (false), m_worldModel (nullptr) {
|
LightMeasure (void) {
|
||||||
initializeLightstyles ();
|
initializeLightstyles ();
|
||||||
|
|
||||||
m_point.reset ();
|
m_point.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void initializeLightstyles (void);
|
void initializeLightstyles (void);
|
||||||
void animateLight (void);
|
void animateLight (void);
|
||||||
|
void updateLight (int style, char *value);
|
||||||
|
|
||||||
float getLightLevel (const Vector &point);
|
float getLightLevel (const Vector &point);
|
||||||
float getSkyColor (void);
|
float getSkyColor (void);
|
||||||
|
|
@ -437,18 +532,18 @@ private:
|
||||||
template <typename S, typename M> bool recursiveLightPoint (const M *node, const Vector &start, const Vector &end);
|
template <typename S, typename M> bool recursiveLightPoint (const M *node, const Vector &start, const Vector &end);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline void resetWorldModel (void) {
|
void resetWorldModel (void) {
|
||||||
m_worldModel = nullptr;
|
m_worldModel = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setWorldModel (model_t *model) {
|
void setWorldModel (model_t *model) {
|
||||||
if (m_worldModel) {
|
if (m_worldModel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_worldModel = model;
|
m_worldModel = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void enableAnimation (bool enable) {
|
void enableAnimation (bool enable) {
|
||||||
m_doAnimation = enable;
|
m_doAnimation = enable;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -112,7 +112,6 @@ typedef struct {
|
||||||
|
|
||||||
extern gamedll_funcs_t *gpGamedllFuncs;
|
extern gamedll_funcs_t *gpGamedllFuncs;
|
||||||
extern mutil_funcs_t *gpMetaUtilFuncs;
|
extern mutil_funcs_t *gpMetaUtilFuncs;
|
||||||
extern meta_globals_t *gpMetaGlobals;
|
|
||||||
extern metamod_funcs_t gMetaFunctionTable;
|
extern metamod_funcs_t gMetaFunctionTable;
|
||||||
|
|
||||||
#define MDLL_FUNC gpGamedllFuncs->dllapi_table
|
#define MDLL_FUNC gpGamedllFuncs->dllapi_table
|
||||||
|
|
|
||||||
|
|
@ -16,23 +16,24 @@
|
||||||
#ifndef SDKUTIL_H
|
#ifndef SDKUTIL_H
|
||||||
#define SDKUTIL_H
|
#define SDKUTIL_H
|
||||||
|
|
||||||
extern globalvars_t *g_pGlobals;
|
extern globalvars_t *globals;
|
||||||
extern enginefuncs_t g_engfuncs;
|
extern enginefuncs_t engfuncs;
|
||||||
|
extern gamefuncs_t dllapi;
|
||||||
|
|
||||||
// Use this instead of ALLOC_STRING on constant strings
|
// Use this instead of ALLOC_STRING on constant strings
|
||||||
#define STRING(offset) (const char *)(g_pGlobals->pStringBase + (int)offset)
|
#define STRING(offset) (const char *)(globals->pStringBase + (int)offset)
|
||||||
|
|
||||||
// form fwgs-hlsdk
|
// form fwgs-hlsdk
|
||||||
static inline int MAKE_STRING (const char *val) {
|
static inline int MAKE_STRING (const char *val) {
|
||||||
long long ptrdiff = val - STRING (0);
|
long long ptrdiff = val - STRING (0);
|
||||||
|
|
||||||
if (ptrdiff > INT_MAX || ptrdiff < INT_MIN) {
|
if (ptrdiff > INT_MAX || ptrdiff < INT_MIN) {
|
||||||
return g_engfuncs.pfnAllocString (val);
|
return engfuncs.pfnAllocString (val);
|
||||||
}
|
}
|
||||||
return static_cast <int> (ptrdiff);
|
return static_cast <int> (ptrdiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ENGINE_STR(str) (const_cast <char *> (STRING (g_engfuncs.pfnAllocString (str))))
|
#define ENGINE_STR(str) (const_cast <char *> (STRING (engfuncs.pfnAllocString (str))))
|
||||||
|
|
||||||
// Dot products for view cone checking
|
// Dot products for view cone checking
|
||||||
#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees
|
#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
//
|
|
||||||
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
|
||||||
// Copyright (c) YaPB Development Team.
|
|
||||||
//
|
|
||||||
// This software is licensed under the BSD-style license.
|
|
||||||
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
|
||||||
// https://yapb.ru/license
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
extern bool g_canSayBombPlanted;
|
|
||||||
extern bool g_bombPlanted;
|
|
||||||
extern bool g_bombSayString;
|
|
||||||
extern bool g_roundEnded;
|
|
||||||
extern bool g_waypointOn;
|
|
||||||
extern bool g_autoWaypoint;
|
|
||||||
extern bool g_botsCanPause;
|
|
||||||
extern bool g_editNoclip;
|
|
||||||
extern bool g_gameWelcomeSent;
|
|
||||||
|
|
||||||
extern float g_autoPathDistance;
|
|
||||||
extern float g_timeBombPlanted;
|
|
||||||
extern float g_timeNextBombUpdate;
|
|
||||||
extern float g_lastChatTime;
|
|
||||||
extern float g_timeRoundEnd;
|
|
||||||
extern float g_timeRoundMid;
|
|
||||||
extern float g_timeRoundStart;
|
|
||||||
extern float g_timePerSecondUpdate;
|
|
||||||
extern float g_lastRadioTime[MAX_TEAM_COUNT];
|
|
||||||
|
|
||||||
extern int g_mapFlags;
|
|
||||||
extern int g_gameFlags;
|
|
||||||
|
|
||||||
extern int g_highestDamageCT;
|
|
||||||
extern int g_highestDamageT;
|
|
||||||
extern int g_highestKills;
|
|
||||||
|
|
||||||
extern int g_normalWeaponPrefs[NUM_WEAPONS];
|
|
||||||
extern int g_rusherWeaponPrefs[NUM_WEAPONS];
|
|
||||||
extern int g_carefulWeaponPrefs[NUM_WEAPONS];
|
|
||||||
extern int g_grenadeBuyPrecent[NUM_WEAPONS - 23];
|
|
||||||
extern int g_botBuyEconomyTable[NUM_WEAPONS - 15];
|
|
||||||
extern int g_radioSelect[MAX_ENGINE_PLAYERS];
|
|
||||||
extern int g_lastRadio[MAX_TEAM_COUNT];
|
|
||||||
extern int g_storeAddbotVars[4];
|
|
||||||
extern int *g_weaponPrefs[];
|
|
||||||
|
|
||||||
extern Array <StringArray> g_chatFactory;
|
|
||||||
extern Array <Array <ChatterItem>> g_chatterFactory;
|
|
||||||
extern Array <BotName> g_botNames;
|
|
||||||
extern Array <KeywordFactory> g_replyFactory;
|
|
||||||
|
|
||||||
extern WeaponSelect g_weaponSelect[NUM_WEAPONS + 1];
|
|
||||||
extern WeaponProperty g_weaponDefs[MAX_WEAPONS + 1];
|
|
||||||
|
|
||||||
extern Client g_clients[MAX_ENGINE_PLAYERS];
|
|
||||||
extern MenuText g_menus[BOT_MENU_TOTAL_MENUS];
|
|
||||||
extern Task g_taskFilters[TASK_MAX];
|
|
||||||
|
|
||||||
extern Experience *g_experienceData;
|
|
||||||
|
|
||||||
extern edict_t *g_hostEntity;
|
|
||||||
extern Library g_gameLib;
|
|
||||||
|
|
||||||
extern gamefuncs_t g_functionTable;
|
|
||||||
|
|
||||||
static inline bool isEmptyStr (const char *input) {
|
|
||||||
if (input == nullptr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return *input == '\0';
|
|
||||||
}
|
|
||||||
|
|
@ -87,8 +87,11 @@
|
||||||
// @todo: sse should be working ok on x86 android?
|
// @todo: sse should be working ok on x86 android?
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#define PLATFORM_ANDROID
|
#define PLATFORM_ANDROID
|
||||||
|
|
||||||
|
#if defined (__arm__) || defined (__aarch64__ )
|
||||||
#undef PLATFORM_HAS_SSE2
|
#undef PLATFORM_HAS_SSE2
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#error "Platform unrecognized."
|
#error "Platform unrecognized."
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
// general product information
|
// general product information
|
||||||
#define PRODUCT_NAME "Yet Another POD-Bot"
|
#define PRODUCT_NAME "Yet Another POD-Bot"
|
||||||
#define PRODUCT_SHORT_NAME "YaPB"
|
#define PRODUCT_SHORT_NAME "YaPB"
|
||||||
#define PRODUCT_VERSION "2.10"
|
#define PRODUCT_VERSION "2.91"
|
||||||
#define PRODUCT_AUTHOR "YaPB Dev Team"
|
#define PRODUCT_AUTHOR "YaPB Dev Team"
|
||||||
#define PRODUCT_URL "https://yapb.ru/"
|
#define PRODUCT_URL "https://yapb.ru/"
|
||||||
#define PRODUCT_EMAIL "d@entix.io"
|
#define PRODUCT_EMAIL "d@entix.io"
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
#define PRODUCT_GIT_HASH "unspecified_hash"
|
#define PRODUCT_GIT_HASH "unspecified_hash"
|
||||||
#define PRODUCT_GIT_COMMIT_AUTHOR "unspecified_author"
|
#define PRODUCT_GIT_COMMIT_AUTHOR "unspecified_author"
|
||||||
#define PRODUCT_GIT_COMMIT_ID 0000
|
#define PRODUCT_GIT_COMMIT_ID 0000
|
||||||
#define PRODUCT_VERSION_DWORD_INTERNAL 2, 10
|
#define PRODUCT_VERSION_DWORD_INTERNAL 2, 91
|
||||||
#define PRODUCT_VERSION_DWORD PRODUCT_VERSION_DWORD_INTERNAL, PRODUCT_GIT_COMMIT_ID
|
#define PRODUCT_VERSION_DWORD PRODUCT_VERSION_DWORD_INTERNAL, PRODUCT_GIT_COMMIT_ID
|
||||||
#define PRODUCT_SUPPORT_VERSION "Beta 6.6 - Condition Zero"
|
#define PRODUCT_SUPPORT_VERSION "Beta 6.6 - Condition Zero"
|
||||||
#define PRODUCT_COMMENTS "http://github.com/jeefo/yapb/"
|
#define PRODUCT_COMMENTS "http://github.com/jeefo/yapb/"
|
||||||
|
|
|
||||||
1809
include/yapb.h
1809
include/yapb.h
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@
|
||||||
//
|
//
|
||||||
// This software is licensed under the BSD-style license.
|
// This software is licensed under the BSD-style license.
|
||||||
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
||||||
// https://yapb.jeefo.net/license
|
// https://yapb.ru/license
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <winver.h>
|
#include <winver.h>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
<ClInclude Include="..\include\engine\meta_api.h" />
|
<ClInclude Include="..\include\engine\meta_api.h" />
|
||||||
<ClInclude Include="..\include\engine\progdefs.h" />
|
<ClInclude Include="..\include\engine\progdefs.h" />
|
||||||
<ClInclude Include="..\include\engine\util.h" />
|
<ClInclude Include="..\include\engine\util.h" />
|
||||||
<ClInclude Include="..\include\globals.h" />
|
|
||||||
<ClInclude Include="..\include\platform.h" />
|
<ClInclude Include="..\include\platform.h" />
|
||||||
<ClInclude Include="..\include\resource.h" />
|
<ClInclude Include="..\include\resource.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
@ -32,7 +31,7 @@
|
||||||
<ClCompile Include="..\source\engine.cpp" />
|
<ClCompile Include="..\source\engine.cpp" />
|
||||||
<ClCompile Include="..\source\manager.cpp" />
|
<ClCompile Include="..\source\manager.cpp" />
|
||||||
<ClCompile Include="..\source\chatlib.cpp" />
|
<ClCompile Include="..\source\chatlib.cpp" />
|
||||||
<ClCompile Include="..\source\globals.cpp" />
|
<ClCompile Include="..\source\control.cpp" />
|
||||||
<ClCompile Include="..\source\interface.cpp" />
|
<ClCompile Include="..\source\interface.cpp" />
|
||||||
<ClCompile Include="..\source\navigate.cpp" />
|
<ClCompile Include="..\source\navigate.cpp" />
|
||||||
<ClCompile Include="..\source\support.cpp" />
|
<ClCompile Include="..\source\support.cpp" />
|
||||||
|
|
@ -193,7 +192,7 @@
|
||||||
<ProgramDataBaseFileName>.\release\inf\</ProgramDataBaseFileName>
|
<ProgramDataBaseFileName>.\release\inf\</ProgramDataBaseFileName>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>None</DebugInformationFormat>
|
||||||
<CompileAs>CompileAsCpp</CompileAs>
|
<CompileAs>CompileAsCpp</CompileAs>
|
||||||
<InterproceduralOptimization>SingleFile</InterproceduralOptimization>
|
<InterproceduralOptimization>SingleFile</InterproceduralOptimization>
|
||||||
<FlushDenormalResultsToZero>true</FlushDenormalResultsToZero>
|
<FlushDenormalResultsToZero>true</FlushDenormalResultsToZero>
|
||||||
|
|
@ -209,6 +208,8 @@
|
||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<StringPooling>true</StringPooling>
|
<StringPooling>true</StringPooling>
|
||||||
|
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ResourceCompile>
|
<ResourceCompile>
|
||||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
|
@ -228,8 +229,8 @@
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<GenerateMapFile>false</GenerateMapFile>
|
<GenerateMapFile>false</GenerateMapFile>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
<OptimizeReferences>false</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<EnableCOMDATFolding>false</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<SetChecksum>false</SetChecksum>
|
<SetChecksum>false</SetChecksum>
|
||||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||||
<DataExecutionPrevention />
|
<DataExecutionPrevention />
|
||||||
|
|
@ -241,8 +242,9 @@
|
||||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||||
<ImageHasSafeExceptionHandlers>
|
<ImageHasSafeExceptionHandlers>
|
||||||
</ImageHasSafeExceptionHandlers>
|
</ImageHasSafeExceptionHandlers>
|
||||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
<LinkTimeCodeGeneration>UseFastLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||||
<FullProgramDatabaseFile>true</FullProgramDatabaseFile>
|
<FullProgramDatabaseFile>true</FullProgramDatabaseFile>
|
||||||
|
<ShowProgress>NotSet</ShowProgress>
|
||||||
</Link>
|
</Link>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
<Command>
|
<Command>
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,6 @@
|
||||||
<ClInclude Include="..\include\corelib.h">
|
<ClInclude Include="..\include\corelib.h">
|
||||||
<Filter>include</Filter>
|
<Filter>include</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\include\globals.h">
|
|
||||||
<Filter>include</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\include\resource.h">
|
<ClInclude Include="..\include\resource.h">
|
||||||
<Filter>include</Filter>
|
<Filter>include</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
@ -62,9 +59,6 @@
|
||||||
<ClCompile Include="..\source\chatlib.cpp">
|
<ClCompile Include="..\source\chatlib.cpp">
|
||||||
<Filter>source</Filter>
|
<Filter>source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\source\globals.cpp">
|
|
||||||
<Filter>source</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\source\interface.cpp">
|
<ClCompile Include="..\source\interface.cpp">
|
||||||
<Filter>source</Filter>
|
<Filter>source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
@ -89,6 +83,9 @@
|
||||||
<ClCompile Include="..\source\basecode.cpp">
|
<ClCompile Include="..\source\basecode.cpp">
|
||||||
<Filter>source</Filter>
|
<Filter>source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\source\control.cpp">
|
||||||
|
<Filter>source</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="yapb.rc">
|
<ResourceCompile Include="yapb.rc">
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ LOCAL_SRC_FILES := \
|
||||||
manager.cpp \
|
manager.cpp \
|
||||||
chatlib.cpp \
|
chatlib.cpp \
|
||||||
combat.cpp \
|
combat.cpp \
|
||||||
globals.cpp \
|
control.cpp \
|
||||||
engine.cpp \
|
engine.cpp \
|
||||||
interface.cpp \
|
interface.cpp \
|
||||||
navigate.cpp \
|
navigate.cpp \
|
||||||
|
|
|
||||||
1477
source/basecode.cpp
1477
source/basecode.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -11,331 +11,80 @@
|
||||||
|
|
||||||
ConVar yb_chat ("yb_chat", "1");
|
ConVar yb_chat ("yb_chat", "1");
|
||||||
|
|
||||||
void stripClanTags (char *buffer) {
|
void BotUtils::stripTags (String &line) {
|
||||||
// this function strips 'clan' tags specified below in given string buffer
|
if (line.empty ()) {
|
||||||
|
|
||||||
if (isEmptyStr (buffer)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t nameLength = strlen (buffer);
|
|
||||||
|
|
||||||
if (nameLength < 4) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const char *open[] = { "-=", "-[", "-]", "-}", "-{", "<[", "<]", "[-", "]-", "{-", "}-", "[[", "[", "{", "]", "}", "<", ">", "-", "|", "=", "+", "(", ")" };
|
for (const auto &tag : m_tags) {
|
||||||
constexpr const char *close[] = { "=-", "]-", "[-", "{-", "}-", "]>", "[>", "-]", "-[", "-}", "-{", "]]", "]", "}", "[", "{", ">", "<", "-", "|", "=", "+", ")", "(" };
|
const size_t start = line.find (tag.first, 0);
|
||||||
|
|
||||||
// for each known tag...
|
if (start != String::INVALID_INDEX) {
|
||||||
for (size_t i = 0; i < cr::arrsize (open); i++) {
|
const size_t end = line.find (tag.second, start);
|
||||||
size_t start = strstr (buffer, open[i]) - buffer; // look for a tag start
|
const size_t diff = end - start;
|
||||||
|
|
||||||
// have we found a tag start?
|
if (end != String::INVALID_INDEX && end > start && diff < 32 && diff > 4) {
|
||||||
if (start < 32) {
|
line.erase (start, diff + tag.second.length ());
|
||||||
size_t stop = strstr (buffer, close[i]) - buffer; // look for a tag stop
|
break;
|
||||||
|
|
||||||
// have we found a tag stop?
|
|
||||||
if (stop > start && stop < 32) {
|
|
||||||
size_t tag = strlen (close[i]);
|
|
||||||
size_t j = start;
|
|
||||||
|
|
||||||
for (; j < nameLength - (stop + tag - start); j++) {
|
|
||||||
buffer[j] = buffer[j + (stop + tag - start)]; // overwrite the buffer with the stripped string
|
|
||||||
}
|
|
||||||
buffer[j] = '\0'; // terminate the string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// have we stripped too much (all the stuff)?
|
|
||||||
if (isEmptyStr (buffer)) {
|
|
||||||
String::trimChars (buffer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String::trimChars (buffer); // if so, string is just a tag
|
|
||||||
|
|
||||||
// strip just the tag part...
|
|
||||||
for (size_t i = 0; i < cr::arrsize (open); i++) {
|
|
||||||
size_t start = strstr (buffer, open[i]) - buffer; // look for a tag start
|
|
||||||
|
|
||||||
// have we found a tag start?
|
|
||||||
if (start < 32) {
|
|
||||||
size_t tag = strlen (open[i]);
|
|
||||||
size_t j = start;
|
|
||||||
|
|
||||||
for (; j < nameLength - tag; j++) {
|
|
||||||
buffer[j] = buffer[j + tag]; // overwrite the buffer with the stripped string
|
|
||||||
}
|
|
||||||
buffer[j] = '\0'; // terminate the string
|
|
||||||
start = strstr (buffer, close[i]) - buffer; // look for a tag stop
|
|
||||||
|
|
||||||
// have we found a tag stop ?
|
|
||||||
if (start < 32) {
|
|
||||||
tag = strlen (close[i]);
|
|
||||||
j = start;
|
|
||||||
|
|
||||||
for (; j < nameLength - tag; j++) {
|
|
||||||
buffer[j] = buffer[j + tag]; // overwrite the buffer with the stripped string
|
|
||||||
}
|
|
||||||
buffer[j] = 0; // terminate the string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String::trimChars (buffer); // to finish, strip eventual blanks after and before the tag marks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *humanizeName (char *name) {
|
void BotUtils::humanizePlayerName (String &playerName) {
|
||||||
// this function humanize player name (i.e. trim clan and switch to lower case (sometimes))
|
if (playerName.empty ()) {
|
||||||
|
return;
|
||||||
static char outputName[64]; // create return name buffer
|
}
|
||||||
strncpy (outputName, name, cr::bufsize (outputName)); // copy name to new buffer
|
|
||||||
|
|
||||||
// drop tag marks, 80 percent of time
|
// drop tag marks, 80 percent of time
|
||||||
if (rng.chance (80)) {
|
if (rng.chance (80)) {
|
||||||
stripClanTags (outputName);
|
stripTags (playerName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String::trimChars (outputName);
|
playerName.trim ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// sometimes switch name to lower characters
|
// sometimes switch name to lower characters, only valid for the english languge
|
||||||
// note: since we're using russian names written in english, we reduce this shit to 6 percent
|
if (rng.chance (15) && strcmp (yb_language.str (), "en") == 0) {
|
||||||
if (rng.chance (7)) {
|
playerName.lowercase ();
|
||||||
for (size_t i = 0; i < strlen (outputName); i++) {
|
|
||||||
outputName[i] = static_cast <char> (tolower (static_cast <int> (outputName[i]))); // to lower case
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return &outputName[0]; // return terminated string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addChatErrors (char *buffer) {
|
void BotUtils::addChatErrors (String &line) {
|
||||||
// this function humanize chat string to be more handwritten
|
// sometimes switch name to lower characters, only valid for the english languge
|
||||||
|
if (rng.chance (15) && strcmp (yb_language.str (), "en") == 0) {
|
||||||
size_t length = strlen (buffer); // get length of string
|
line.lowercase ();
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
// sometimes switch text to lowercase
|
|
||||||
// note: since we're using russian chat written in English, we reduce this shit to 4 percent
|
|
||||||
if (rng.chance (5)) {
|
|
||||||
for (i = 0; i < length; i++) {
|
|
||||||
buffer[i] = static_cast <char> (tolower (static_cast <int> (buffer[i]))); // switch to lowercase
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
auto length = line.length ();
|
||||||
|
|
||||||
if (length > 15) {
|
if (length > 15) {
|
||||||
size_t percentile = length / 2;
|
size_t percentile = line.length () / 2;
|
||||||
|
|
||||||
// "length / 2" percent of time drop a character
|
// "length / 2" percent of time drop a character
|
||||||
if (rng.getInt (1u, 100u) < percentile) {
|
if (rng.chance (percentile)) {
|
||||||
size_t pos = rng.getInt (length / 8, length - length / 8); // chose random position in string
|
line.erase (rng.getInt (length / 8, length - length / 8));
|
||||||
|
|
||||||
for (i = pos; i < length - 1; i++) {
|
|
||||||
buffer[i] = buffer[i + 1]; // overwrite the buffer with stripped string
|
|
||||||
}
|
|
||||||
buffer[i] = '\0'; // terminate string;
|
|
||||||
length--; // update new string length
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// "length" / 4 precent of time swap character
|
// "length" / 4 precent of time swap character
|
||||||
if (rng.getInt (1u, 100u) < percentile / 2) {
|
if (rng.chance (percentile / 2)) {
|
||||||
size_t pos = rng.getInt (length / 8, 3 * length / 8); // choose random position in string
|
size_t pos = rng.getInt (length / 8, 3 * length / 8); // choose random position in string
|
||||||
char ch = buffer[pos]; // swap characters
|
cr::swap (line[pos], line[pos + 1]);
|
||||||
|
|
||||||
buffer[pos] = buffer[pos + 1];
|
|
||||||
buffer[pos + 1] = ch;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer[length] = 0; // terminate string
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bot::prepareChatMessage (char *text) {
|
|
||||||
// this function parses messages from the botchat, replaces keywords and converts names into a more human style
|
|
||||||
|
|
||||||
if (!yb_chat.boolean () || isEmptyStr (text)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_tempStrings.clear ();
|
|
||||||
|
|
||||||
char *textStart = text;
|
|
||||||
char *pattern = text;
|
|
||||||
|
|
||||||
edict_t *talkEntity = nullptr;
|
|
||||||
|
|
||||||
auto getHumanizedName = [] (edict_t *ent) {
|
|
||||||
if (!engine.isNullEntity (ent)) {
|
|
||||||
return const_cast <const char *> (humanizeName (const_cast <char *> (STRING (ent->v.netname))));
|
|
||||||
}
|
|
||||||
return "unknown";
|
|
||||||
};
|
|
||||||
|
|
||||||
while (pattern != nullptr) {
|
|
||||||
// all replacement placeholders start with a %
|
|
||||||
pattern = strchr (textStart, '%');
|
|
||||||
|
|
||||||
if (pattern != nullptr) {
|
|
||||||
size_t length = pattern - textStart;
|
|
||||||
|
|
||||||
if (length > 0) {
|
|
||||||
m_tempStrings = String (textStart, length);
|
|
||||||
}
|
|
||||||
pattern++;
|
|
||||||
|
|
||||||
// player with most frags?
|
|
||||||
if (*pattern == 'f') {
|
|
||||||
int highestFrags = -9000; // just pick some start value
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < engine.maxClients (); i++) {
|
|
||||||
const Client &client = g_clients[i];
|
|
||||||
|
|
||||||
if (!(client.flags & CF_USED) || client.ent == ent ()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int frags = static_cast <int> (client.ent->v.frags);
|
|
||||||
|
|
||||||
if (frags > highestFrags) {
|
|
||||||
highestFrags = frags;
|
|
||||||
index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
talkEntity = g_clients[index].ent;
|
|
||||||
m_tempStrings += getHumanizedName (talkEntity);
|
|
||||||
}
|
|
||||||
// mapname?
|
|
||||||
else if (*pattern == 'm') {
|
|
||||||
m_tempStrings += engine.getMapName ();
|
|
||||||
}
|
|
||||||
// roundtime?
|
|
||||||
else if (*pattern == 'r') {
|
|
||||||
int time = static_cast <int> (g_timeRoundEnd - engine.timebase ());
|
|
||||||
m_tempStrings.formatAppend ("%02d:%02d", time / 60, time % 60);
|
|
||||||
}
|
|
||||||
// chat reply?
|
|
||||||
else if (*pattern == 's') {
|
|
||||||
talkEntity = engine.entityOfIndex (m_sayTextBuffer.entityIndex);
|
|
||||||
m_tempStrings += getHumanizedName (talkEntity);
|
|
||||||
}
|
|
||||||
// teammate alive?
|
|
||||||
else if (*pattern == 't') {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < engine.maxClients (); i++) {
|
|
||||||
const Client &client = g_clients[i];
|
|
||||||
|
|
||||||
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == ent ()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < engine.maxClients ()) {
|
|
||||||
if (isPlayer (pev->dmg_inflictor) && m_team == engine.getTeam (pev->dmg_inflictor)) {
|
|
||||||
talkEntity = pev->dmg_inflictor;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
talkEntity = g_clients[i].ent;
|
|
||||||
}
|
|
||||||
m_tempStrings += getHumanizedName (talkEntity);
|
|
||||||
}
|
|
||||||
else // no teammates alive...
|
|
||||||
{
|
|
||||||
for (i = 0; i < engine.maxClients (); i++) {
|
|
||||||
const Client &client = g_clients[i];
|
|
||||||
|
|
||||||
if (!(client.flags & CF_USED) || client.team != m_team || client.ent == ent ()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < engine.maxClients ()) {
|
|
||||||
talkEntity = g_clients[i].ent;
|
|
||||||
m_tempStrings += getHumanizedName (talkEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (*pattern == 'e') {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < engine.maxClients (); i++) {
|
|
||||||
const Client &client = g_clients[i];
|
|
||||||
|
|
||||||
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team || client.ent == ent ()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < engine.maxClients ()) {
|
|
||||||
talkEntity = g_clients[i].ent;
|
|
||||||
m_tempStrings += getHumanizedName (talkEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// no teammates alive ?
|
|
||||||
else {
|
|
||||||
for (i = 0; i < engine.maxClients (); i++) {
|
|
||||||
const Client &client = g_clients[i];
|
|
||||||
|
|
||||||
if (!(client.flags & CF_USED) || client.team == m_team || client.ent == ent ()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i < engine.maxClients ()) {
|
|
||||||
talkEntity = g_clients[i].ent;
|
|
||||||
m_tempStrings += getHumanizedName (talkEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (*pattern == 'd') {
|
|
||||||
if (g_gameFlags & GAME_CZERO) {
|
|
||||||
if (rng.chance (30)) {
|
|
||||||
m_tempStrings += "CZ";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_tempStrings += "Condition Zero";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((g_gameFlags & GAME_CSTRIKE16) || (g_gameFlags & GAME_LEGACY)) {
|
|
||||||
if (rng.chance (30)) {
|
|
||||||
m_tempStrings += "CS";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_tempStrings += "Counter-Strike";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (*pattern == 'v') {
|
|
||||||
talkEntity = m_lastVictim;
|
|
||||||
m_tempStrings += getHumanizedName (talkEntity);
|
|
||||||
}
|
|
||||||
pattern++;
|
|
||||||
textStart = pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textStart != nullptr) {
|
|
||||||
// let the bots make some mistakes...
|
|
||||||
char tempString[160];
|
|
||||||
strncpy (tempString, textStart, cr::bufsize (tempString));
|
|
||||||
|
|
||||||
addChatErrors (tempString);
|
|
||||||
m_tempStrings += tempString;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkForKeywords (char *message, char *reply) {
|
bool BotUtils::checkKeywords (const String &line, String &reply) {
|
||||||
// this function checks is string contain keyword, and generates reply to it
|
// this function checks is string contain keyword, and generates reply to it
|
||||||
|
|
||||||
if (!yb_chat.boolean () || isEmptyStr (message)) {
|
if (!yb_chat.boolean () || line.empty ()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &factory : g_replyFactory) {
|
for (auto &factory : conf.getReplies ()) {
|
||||||
for (auto &keyword : factory.keywords) {
|
for (auto &keyword : factory.keywords) {
|
||||||
|
|
||||||
// check is keyword has occurred in message
|
// check is keyword has occurred in message
|
||||||
if (strstr (message, keyword.chars ()) != nullptr) {
|
if (line.find (keyword, 0) != String::INVALID_INDEX) {
|
||||||
StringArray &usedReplies = factory.usedReplies;
|
StringArray &usedReplies = factory.usedReplies;
|
||||||
|
|
||||||
if (usedReplies.length () >= factory.replies.length () / 2) {
|
if (usedReplies.length () >= factory.replies.length () / 2) {
|
||||||
|
|
@ -356,7 +105,7 @@ bool checkForKeywords (char *message, char *reply) {
|
||||||
|
|
||||||
// reply not used, so use it
|
// reply not used, so use it
|
||||||
if (!replyUsed) {
|
if (!replyUsed) {
|
||||||
strcpy (reply, choosenReply.chars ()); // update final buffer
|
reply.assign (choosenReply); // update final buffer
|
||||||
usedReplies.push (choosenReply); // add to ignore list
|
usedReplies.push (choosenReply); // add to ignore list
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -365,45 +114,234 @@ bool checkForKeywords (char *message, char *reply) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto &chat = conf.getChat ();
|
||||||
|
|
||||||
// didn't find a keyword? 70% of the time use some universal reply
|
// didn't find a keyword? 70% of the time use some universal reply
|
||||||
if (rng.chance (70) && !g_chatFactory[CHAT_NOKW].empty ()) {
|
if (rng.chance (70) && !chat[CHAT_NOKW].empty ()) {
|
||||||
strcpy (reply, g_chatFactory[CHAT_NOKW].random ().chars ());
|
reply.assign (chat[CHAT_NOKW].random ());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::processChatKeywords (char *reply) {
|
void Bot::prepareChatMessage (const String &message) {
|
||||||
|
// this function parses messages from the botchat, replaces keywords and converts names into a more human style
|
||||||
|
|
||||||
|
if (!yb_chat.boolean () || message.empty ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_chatBuffer = message;
|
||||||
|
|
||||||
|
// must be called before return or on the end
|
||||||
|
auto finishPreparation = [&] (void) {
|
||||||
|
if (!m_chatBuffer.empty ()) {
|
||||||
|
util.addChatErrors (m_chatBuffer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// need to check if we're have special symbols
|
||||||
|
size_t pos = message.find ('%', 0);
|
||||||
|
|
||||||
|
// nothing found, bail out
|
||||||
|
if (pos == String::INVALID_INDEX || pos >= message.length ()) {
|
||||||
|
finishPreparation ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the humanized name out of client
|
||||||
|
auto humanizedName = [] (const Client &client) -> String {
|
||||||
|
if (!util.isPlayer (client.ent)) {
|
||||||
|
return cr::move (String ("unknown"));
|
||||||
|
}
|
||||||
|
String playerName = STRING (client.ent->v.netname);
|
||||||
|
util.humanizePlayerName (playerName);
|
||||||
|
|
||||||
|
return cr::move (playerName);
|
||||||
|
};
|
||||||
|
|
||||||
|
// find highfrag player
|
||||||
|
auto getHighfragPlayer = [&] (void) -> String {
|
||||||
|
int highestFrags = -1;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < game.maxClients (); i++) {
|
||||||
|
const Client &client = util.getClient (i);
|
||||||
|
|
||||||
|
if (!(client.flags & CF_USED) || client.ent == ent ()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int frags = static_cast <int> (client.ent->v.frags);
|
||||||
|
|
||||||
|
if (frags > highestFrags) {
|
||||||
|
highestFrags = frags;
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return humanizedName (util.getClient (index));
|
||||||
|
};
|
||||||
|
|
||||||
|
// get roundtime
|
||||||
|
auto getRoundTime = [] (void) -> String {
|
||||||
|
int roundTimeSecs = static_cast <int> (bots.getRoundEndTime () - game.timebase ());
|
||||||
|
|
||||||
|
String roundTime;
|
||||||
|
roundTime.assign ("%02d:%02d", roundTimeSecs / 60, cr::abs (roundTimeSecs) % 60);
|
||||||
|
|
||||||
|
return cr::move (roundTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
// get bot's victim
|
||||||
|
auto getMyVictim = [&] (void) -> String {
|
||||||
|
for (const Client &client : util.getClients ()) {
|
||||||
|
if (client.ent == m_lastVictim) {
|
||||||
|
return humanizedName (client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cr::move (String ("unknown"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// get the game name alias
|
||||||
|
auto getGameName = [] (void) -> String {
|
||||||
|
String gameName;
|
||||||
|
|
||||||
|
if (game.is (GAME_CZERO)) {
|
||||||
|
if (rng.chance (30)) {
|
||||||
|
gameName = "CZ";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gameName = "Condition Zero";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (game.is (GAME_CSTRIKE16) || game.is (GAME_LEGACY)) {
|
||||||
|
if (rng.chance (30)) {
|
||||||
|
gameName = "CS";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gameName = "Counter-Strike";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cr::move (gameName);
|
||||||
|
};
|
||||||
|
|
||||||
|
// get enemy or teammate alive
|
||||||
|
auto getPlayerAlive = [&] (bool needsEnemy) -> String {
|
||||||
|
int index;
|
||||||
|
|
||||||
|
for (index = 0; index < game.maxClients (); index++) {
|
||||||
|
const Client &client = util.getClient (index);
|
||||||
|
|
||||||
|
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.ent == ent ()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((needsEnemy && m_team == client.team) || (!needsEnemy && m_team != client.team)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < game.maxClients ()) {
|
||||||
|
if (!needsEnemy && util.isPlayer (pev->dmg_inflictor) && m_team == game.getTeam (pev->dmg_inflictor)) {
|
||||||
|
return humanizedName (util.getClient (game.indexOfEntity (pev->dmg_inflictor) - 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return humanizedName (util.getClient (index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (index = 0; index < game.maxClients (); index++) {
|
||||||
|
const Client &client = util.getClient (index);
|
||||||
|
|
||||||
|
if (!(client.flags & CF_USED) || client.team != m_team || client.ent == ent ()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((needsEnemy && m_team != client.team) || (!needsEnemy && m_team == client.team)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < game.maxClients ()) {
|
||||||
|
return humanizedName (util.getClient (index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cr::move (String ("unknown"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// found one, let's do replace
|
||||||
|
switch (message[pos + 1]) {
|
||||||
|
|
||||||
|
// the highest frag player
|
||||||
|
case 'f':
|
||||||
|
m_chatBuffer.replace ("%f", getHighfragPlayer ());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// current map name
|
||||||
|
case 'm':
|
||||||
|
m_chatBuffer.replace ("%m", game.getMapName ());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// round time
|
||||||
|
case 'r':
|
||||||
|
m_chatBuffer.replace ("%r", getRoundTime ());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// chat reply
|
||||||
|
case 's':
|
||||||
|
if (m_sayTextBuffer.entityIndex != -1) {
|
||||||
|
m_chatBuffer.replace ("%s", humanizedName (util.getClient (m_sayTextBuffer.entityIndex)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_chatBuffer.replace ("%s", getHighfragPlayer ());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// last bot victim
|
||||||
|
case 'v':
|
||||||
|
m_chatBuffer.replace ("%v", getMyVictim ());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// game name
|
||||||
|
case 'd':
|
||||||
|
m_chatBuffer.replace ("%d", getGameName ());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// teammate alive
|
||||||
|
case 't':
|
||||||
|
m_chatBuffer.replace ("%t", getPlayerAlive (false));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// enemy alive
|
||||||
|
case 'e':
|
||||||
|
m_chatBuffer.replace ("%e", getPlayerAlive (true));
|
||||||
|
break;
|
||||||
|
|
||||||
|
};
|
||||||
|
finishPreparation ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bot::checkChatKeywords (String &reply) {
|
||||||
// this function parse chat buffer, and prepare buffer to keyword searching
|
// this function parse chat buffer, and prepare buffer to keyword searching
|
||||||
|
|
||||||
char tempMessage[512];
|
String message = m_sayTextBuffer.sayText;
|
||||||
size_t maxLength = cr::bufsize (tempMessage);
|
return util.checkKeywords (message.uppercase (), reply);
|
||||||
|
|
||||||
strncpy (tempMessage, m_sayTextBuffer.sayText.chars (), maxLength); // copy to safe place
|
|
||||||
|
|
||||||
// text to uppercase for keyword parsing
|
|
||||||
for (size_t i = 0; i < maxLength; i++) {
|
|
||||||
tempMessage[i] = static_cast <char> (toupper (static_cast <int> (tempMessage[i])));
|
|
||||||
}
|
|
||||||
return checkForKeywords (tempMessage, reply);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::isReplyingToChat (void) {
|
bool Bot::isReplyingToChat (void) {
|
||||||
// this function sends reply to a player
|
// this function sends reply to a player
|
||||||
|
|
||||||
if (m_sayTextBuffer.entityIndex != -1 && !m_sayTextBuffer.sayText.empty ()) {
|
if (m_sayTextBuffer.entityIndex != -1 && !m_sayTextBuffer.sayText.empty ()) {
|
||||||
char text[256];
|
String message;
|
||||||
|
|
||||||
// check is time to chat is good
|
// check is time to chat is good
|
||||||
if (m_sayTextBuffer.timeNextChat < engine.timebase ()) {
|
if (m_sayTextBuffer.timeNextChat < game.timebase ()) {
|
||||||
if (rng.chance (m_sayTextBuffer.chatProbability + rng.getInt (15, 35)) && processChatKeywords (reinterpret_cast <char *> (&text))) {
|
if (rng.chance (m_sayTextBuffer.chatProbability + rng.getInt (25, 45)) && checkChatKeywords (message)) {
|
||||||
prepareChatMessage (text);
|
prepareChatMessage (message);
|
||||||
pushMsgQueue (GAME_MSG_SAY_CMD);
|
pushMsgQueue (GAME_MSG_SAY_CMD);
|
||||||
|
|
||||||
|
|
||||||
m_sayTextBuffer.entityIndex = -1;
|
m_sayTextBuffer.entityIndex = -1;
|
||||||
m_sayTextBuffer.timeNextChat = engine.timebase () + m_sayTextBuffer.chatDelay;
|
m_sayTextBuffer.timeNextChat = game.timebase () + m_sayTextBuffer.chatDelay;
|
||||||
m_sayTextBuffer.sayText.clear ();
|
m_sayTextBuffer.sayText.clear ();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -415,20 +353,61 @@ bool Bot::isReplyingToChat (void) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bot::checkForChat (void) {
|
||||||
|
|
||||||
|
|
||||||
|
// bot chatting turned on?
|
||||||
|
if (!m_notKilled && yb_chat.boolean () && m_lastChatTime + 10.0 < game.timebase () && bots.getLastChatTimestamp () + 5.0f < game.timebase () && !isReplyingToChat ()) {
|
||||||
|
auto &chat = conf.getChat ();
|
||||||
|
|
||||||
|
// say a text every now and then
|
||||||
|
if (rng.chance (50)) {
|
||||||
|
m_lastChatTime = game.timebase ();
|
||||||
|
bots.setLastChatTimestamp (game.timebase ());
|
||||||
|
|
||||||
|
if (!chat[CHAT_DEAD].empty ()) {
|
||||||
|
const String &phrase = chat[CHAT_DEAD].random ();
|
||||||
|
bool sayBufferExists = false;
|
||||||
|
|
||||||
|
// search for last messages, sayed
|
||||||
|
for (auto &sentence : m_sayTextBuffer.lastUsedSentences) {
|
||||||
|
if (strncmp (sentence.chars (), phrase.chars (), sentence.length ()) == 0) {
|
||||||
|
sayBufferExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sayBufferExists) {
|
||||||
|
prepareChatMessage (phrase);
|
||||||
|
pushMsgQueue (GAME_MSG_SAY_CMD);
|
||||||
|
|
||||||
|
// add to ignore list
|
||||||
|
m_sayTextBuffer.lastUsedSentences.push (phrase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the used line buffer every now and then
|
||||||
|
if (static_cast <int> (m_sayTextBuffer.lastUsedSentences.length ()) > rng.getInt (4, 6)) {
|
||||||
|
m_sayTextBuffer.lastUsedSentences.clear ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Bot::say (const char *text) {
|
void Bot::say (const char *text) {
|
||||||
// this function prints saytext message to all players
|
// this function prints saytext message to all players
|
||||||
|
|
||||||
if (isEmptyStr (text) || !yb_chat.boolean ()) {
|
if (util.isEmptyStr (text) || !yb_chat.boolean ()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
engine.execBotCmd (ent (), "say \"%s\"", text);
|
game.execBotCmd (ent (), "say \"%s\"", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::sayTeam (const char *text) {
|
void Bot::sayTeam (const char *text) {
|
||||||
// this function prints saytext message only for teammates
|
// this function prints saytext message only for teammates
|
||||||
|
|
||||||
if (isEmptyStr (text) || !yb_chat.boolean ()) {
|
if (util.isEmptyStr (text) || !yb_chat.boolean ()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
engine.execBotCmd (ent (), "say_team \"%s\"", text);
|
game.execBotCmd (ent (), "say_team \"%s\"", text);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
2031
source/control.cpp
Normal file
2031
source/control.cpp
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,379 +0,0 @@
|
||||||
//
|
|
||||||
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
|
||||||
// Copyright (c) YaPB Development Team.
|
|
||||||
//
|
|
||||||
// This software is licensed under the BSD-style license.
|
|
||||||
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
|
||||||
// https://yapb.ru/license
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <yapb.h>
|
|
||||||
|
|
||||||
bool g_canSayBombPlanted = true;
|
|
||||||
bool g_roundEnded = true;
|
|
||||||
bool g_botsCanPause = false;
|
|
||||||
bool g_bombPlanted = false;
|
|
||||||
bool g_bombSayString = false;
|
|
||||||
bool g_gameWelcomeSent = false;
|
|
||||||
|
|
||||||
bool g_editNoclip = false;
|
|
||||||
bool g_waypointOn = false;
|
|
||||||
bool g_autoWaypoint = false;
|
|
||||||
|
|
||||||
float g_lastChatTime = 0.0f;
|
|
||||||
float g_timeRoundStart = 0.0f;
|
|
||||||
float g_timeRoundEnd = 0.0f;
|
|
||||||
float g_timeRoundMid = 0.0f;
|
|
||||||
float g_timeNextBombUpdate = 0.0f;
|
|
||||||
float g_timeBombPlanted = 0.0f;
|
|
||||||
float g_timePerSecondUpdate = 0.0f;
|
|
||||||
float g_lastRadioTime[MAX_TEAM_COUNT] = { 0.0f, };
|
|
||||||
float g_autoPathDistance = 250.0f;
|
|
||||||
|
|
||||||
int g_lastRadio[MAX_TEAM_COUNT];
|
|
||||||
int g_storeAddbotVars[4];
|
|
||||||
int g_radioSelect[MAX_ENGINE_PLAYERS];
|
|
||||||
int g_gameFlags = 0;
|
|
||||||
int g_mapFlags = 0;
|
|
||||||
|
|
||||||
int g_highestDamageCT = 1;
|
|
||||||
int g_highestDamageT = 1;
|
|
||||||
int g_highestKills = 1;
|
|
||||||
|
|
||||||
Array <StringArray> g_chatFactory;
|
|
||||||
Array <Array <ChatterItem>> g_chatterFactory;
|
|
||||||
Array <BotName> g_botNames;
|
|
||||||
Array <KeywordFactory> g_replyFactory;
|
|
||||||
Library g_gameLib;
|
|
||||||
|
|
||||||
meta_globals_t *gpMetaGlobals = nullptr;
|
|
||||||
gamedll_funcs_t *gpGamedllFuncs = nullptr;
|
|
||||||
mutil_funcs_t *gpMetaUtilFuncs = nullptr;
|
|
||||||
|
|
||||||
gamefuncs_t g_functionTable;
|
|
||||||
|
|
||||||
enginefuncs_t g_engfuncs;
|
|
||||||
Client g_clients[MAX_ENGINE_PLAYERS];
|
|
||||||
WeaponProperty g_weaponDefs[MAX_WEAPONS + 1];
|
|
||||||
|
|
||||||
edict_t *g_hostEntity = nullptr;
|
|
||||||
globalvars_t *g_pGlobals = nullptr;
|
|
||||||
Experience *g_experienceData = nullptr;
|
|
||||||
|
|
||||||
// default tables for personality weapon preferences, overridden by weapons.cfg
|
|
||||||
int g_normalWeaponPrefs[NUM_WEAPONS] = {0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16};
|
|
||||||
int g_rusherWeaponPrefs[NUM_WEAPONS] = {0, 2, 1, 4, 5, 6, 3, 24, 19, 22, 23, 20, 21, 10, 12, 13, 7, 8, 11, 9, 18, 17, 19, 25, 15, 16};
|
|
||||||
int g_carefulWeaponPrefs[NUM_WEAPONS] = {0, 2, 1, 4, 25, 6, 3, 7, 8, 12, 10, 13, 11, 9, 24, 18, 14, 17, 16, 15, 19, 20, 21, 22, 23, 5};
|
|
||||||
int g_grenadeBuyPrecent[NUM_WEAPONS - 23] = {95, 85, 60};
|
|
||||||
int g_botBuyEconomyTable[NUM_WEAPONS - 15] = {1900, 2100, 2100, 4000, 6000, 7000, 16000, 1200, 800, 1000, 3000};
|
|
||||||
int *g_weaponPrefs[] = {g_normalWeaponPrefs, g_rusherWeaponPrefs, g_carefulWeaponPrefs};
|
|
||||||
|
|
||||||
// metamod plugin information
|
|
||||||
plugin_info_t Plugin_info = {
|
|
||||||
META_INTERFACE_VERSION, // interface version
|
|
||||||
PRODUCT_SHORT_NAME, // plugin name
|
|
||||||
PRODUCT_VERSION, // plugin version
|
|
||||||
PRODUCT_DATE, // date of creation
|
|
||||||
PRODUCT_AUTHOR, // plugin author
|
|
||||||
PRODUCT_URL, // plugin URL
|
|
||||||
PRODUCT_LOGTAG, // plugin logtag
|
|
||||||
PT_CHANGELEVEL, // when loadable
|
|
||||||
PT_ANYTIME, // when unloadable
|
|
||||||
};
|
|
||||||
|
|
||||||
// table with all available actions for the bots (filtered in & out in Bot::setConditions) some of them have subactions included
|
|
||||||
Task g_taskFilters[TASK_MAX] = {
|
|
||||||
{ TASK_NORMAL, 0, INVALID_WAYPOINT_INDEX, 0.0f, true },
|
|
||||||
{ TASK_PAUSE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_MOVETOPOSITION, 0, INVALID_WAYPOINT_INDEX, 0.0f, true },
|
|
||||||
{ TASK_FOLLOWUSER, 0, INVALID_WAYPOINT_INDEX, 0.0f, true },
|
|
||||||
{ TASK_PICKUPITEM, 0, INVALID_WAYPOINT_INDEX, 0.0f, true },
|
|
||||||
{ TASK_CAMP, 0, INVALID_WAYPOINT_INDEX, 0.0f, true },
|
|
||||||
{ TASK_PLANTBOMB, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_DEFUSEBOMB, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_ATTACK, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_HUNTENEMY, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_SEEKCOVER, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_THROWHEGRENADE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_THROWFLASHBANG, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_THROWSMOKE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_DOUBLEJUMP, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_ESCAPEFROMBOMB, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_SHOOTBREAKABLE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_HIDE, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_BLINDED, 0, INVALID_WAYPOINT_INDEX, 0.0f, false },
|
|
||||||
{ TASK_SPRAY, 0, INVALID_WAYPOINT_INDEX, 0.0f, false }
|
|
||||||
};
|
|
||||||
|
|
||||||
// weapons and their specifications
|
|
||||||
WeaponSelect g_weaponSelect[NUM_WEAPONS + 1] = {
|
|
||||||
{ WEAPON_KNIFE, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, true },
|
|
||||||
{ WEAPON_USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, false },
|
|
||||||
{ WEAPON_GLOCK, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, false },
|
|
||||||
{ WEAPON_DEAGLE, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, false },
|
|
||||||
{ WEAPON_P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0 , false },
|
|
||||||
{ WEAPON_ELITE, "weapon_elite", "elite.mdl", 800, 1, 0, 0, 1, 5, 5, 5, 0, false },
|
|
||||||
{ WEAPON_FIVESEVEN, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, false },
|
|
||||||
{ WEAPON_M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, false },
|
|
||||||
{ WEAPON_XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, false },
|
|
||||||
{ WEAPON_MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, true },
|
|
||||||
{ WEAPON_TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, true },
|
|
||||||
{ WEAPON_P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, true },
|
|
||||||
{ WEAPON_MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, true },
|
|
||||||
{ WEAPON_UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, true },
|
|
||||||
{ WEAPON_AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, true },
|
|
||||||
{ WEAPON_SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, true },
|
|
||||||
{ WEAPON_M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, true },
|
|
||||||
{ WEAPON_GALIL, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, true },
|
|
||||||
{ WEAPON_FAMAS, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, true },
|
|
||||||
{ WEAPON_AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, true },
|
|
||||||
{ WEAPON_SCOUT, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, false },
|
|
||||||
{ WEAPON_AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, false },
|
|
||||||
{ WEAPON_G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, false },
|
|
||||||
{ WEAPON_SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, false },
|
|
||||||
{ WEAPON_M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, true },
|
|
||||||
{ WEAPON_SHIELD, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, false },
|
|
||||||
{ 0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, false }
|
|
||||||
};
|
|
||||||
|
|
||||||
void setupBotMenus (void) {
|
|
||||||
int counter = 0;
|
|
||||||
|
|
||||||
auto buildKeys = [](int numKeys) {
|
|
||||||
int keys = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < numKeys; i++) {
|
|
||||||
keys |= (1 << i);
|
|
||||||
}
|
|
||||||
keys |= (1 << 9);
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
};
|
|
||||||
|
|
||||||
// bots main menu
|
|
||||||
g_menus[counter] = {
|
|
||||||
BOT_MENU_MAIN, buildKeys (4),
|
|
||||||
"\\yMain Menu\\w\n\n"
|
|
||||||
"1. Control Bots\n"
|
|
||||||
"2. Features\n\n"
|
|
||||||
"3. Fill Server\n"
|
|
||||||
"4. End Round\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// bots features menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_FEATURES, buildKeys (5),
|
|
||||||
"\\yBots Features\\w\n\n"
|
|
||||||
"1. Weapon Mode Menu\n"
|
|
||||||
"2. Waypoint Menu\n"
|
|
||||||
"3. Select Personality\n\n"
|
|
||||||
"4. Toggle Debug Mode\n"
|
|
||||||
"5. Command Menu\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// bot control menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_CONTROL, buildKeys (5),
|
|
||||||
"\\yBots Control Menu\\w\n\n"
|
|
||||||
"1. Add a Bot, Quick\n"
|
|
||||||
"2. Add a Bot, Specified\n\n"
|
|
||||||
"3. Remove Random Bot\n"
|
|
||||||
"4. Remove All Bots\n\n"
|
|
||||||
"5. Remove Bot Menu\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// weapon mode select menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WEAPON_MODE, buildKeys (7),
|
|
||||||
"\\yBots Weapon Mode\\w\n\n"
|
|
||||||
"1. Knives only\n"
|
|
||||||
"2. Pistols only\n"
|
|
||||||
"3. Shotguns only\n"
|
|
||||||
"4. Machine Guns only\n"
|
|
||||||
"5. Rifles only\n"
|
|
||||||
"6. Sniper Weapons only\n"
|
|
||||||
"7. All Weapons\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// personality select menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_PERSONALITY, buildKeys (4),
|
|
||||||
"\\yBots Personality\\w\n\n"
|
|
||||||
"1. Random\n"
|
|
||||||
"2. Normal\n"
|
|
||||||
"3. Aggressive\n"
|
|
||||||
"4. Careful\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// difficulty select menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_DIFFICULTY, buildKeys (5),
|
|
||||||
"\\yBots Difficulty Level\\w\n\n"
|
|
||||||
"1. Newbie\n"
|
|
||||||
"2. Average\n"
|
|
||||||
"3. Normal\n"
|
|
||||||
"4. Professional\n"
|
|
||||||
"5. Godlike\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// team select menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_TEAM_SELECT, buildKeys (5),
|
|
||||||
"\\ySelect a team\\w\n\n"
|
|
||||||
"1. Terrorist Force\n"
|
|
||||||
"2. Counter-Terrorist Force\n\n"
|
|
||||||
"5. Auto-select\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// terrorist model select menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_TERRORIST_SELECT, buildKeys (5),
|
|
||||||
"\\ySelect an appearance\\w\n\n"
|
|
||||||
"1. Phoenix Connexion\n"
|
|
||||||
"2. L337 Krew\n"
|
|
||||||
"3. Arctic Avengers\n"
|
|
||||||
"4. Guerilla Warfare\n\n"
|
|
||||||
"5. Auto-select\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// counter-terrorist model select menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_CT_SELECT, buildKeys (5),
|
|
||||||
"\\ySelect an appearance\\w\n\n"
|
|
||||||
"1. Seal Team 6 (DEVGRU)\n"
|
|
||||||
"2. German GSG-9\n"
|
|
||||||
"3. UK SAS\n"
|
|
||||||
"4. French GIGN\n\n"
|
|
||||||
"5. Auto-select\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// command menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_COMMANDS, buildKeys (4),
|
|
||||||
"\\yBot Command Menu\\w\n\n"
|
|
||||||
"1. Make Double Jump\n"
|
|
||||||
"2. Finish Double Jump\n\n"
|
|
||||||
"3. Drop the C4 Bomb\n"
|
|
||||||
"4. Drop the Weapon\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// main waypoint menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WAYPOINT_MAIN_PAGE1, buildKeys (9),
|
|
||||||
"\\yWaypoint Operations (Page 1)\\w\n\n"
|
|
||||||
"1. Show/Hide waypoints\n"
|
|
||||||
"2. Cache waypoint\n"
|
|
||||||
"3. Create path\n"
|
|
||||||
"4. Delete path\n"
|
|
||||||
"5. Add waypoint\n"
|
|
||||||
"6. Delete waypoint\n"
|
|
||||||
"7. Set Autopath Distance\n"
|
|
||||||
"8. Set Radius\n\n"
|
|
||||||
"9. Next...\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// main waypoint menu (page 2)
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WAYPOINT_MAIN_PAGE2, buildKeys (9),
|
|
||||||
"\\yWaypoint Operations (Page 2)\\w\n\n"
|
|
||||||
"1. Waypoint stats\n"
|
|
||||||
"2. Autowaypoint on/off\n"
|
|
||||||
"3. Set flags\n"
|
|
||||||
"4. Save waypoints\n"
|
|
||||||
"5. Save without checking\n"
|
|
||||||
"6. Load waypoints\n"
|
|
||||||
"7. Check waypoints\n"
|
|
||||||
"8. Noclip cheat on/off\n\n"
|
|
||||||
"9. Previous...\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// select waypoint radius menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WAYPOINT_RADIUS, buildKeys (9),
|
|
||||||
"\\yWaypoint Radius\\w\n\n"
|
|
||||||
"1. SetRadius 0\n"
|
|
||||||
"2. SetRadius 8\n"
|
|
||||||
"3. SetRadius 16\n"
|
|
||||||
"4. SetRadius 32\n"
|
|
||||||
"5. SetRadius 48\n"
|
|
||||||
"6. SetRadius 64\n"
|
|
||||||
"7. SetRadius 80\n"
|
|
||||||
"8. SetRadius 96\n"
|
|
||||||
"9. SetRadius 128\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// waypoint add menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WAYPOINT_TYPE, buildKeys (9),
|
|
||||||
"\\yWaypoint Type\\w\n\n"
|
|
||||||
"1. Normal\n"
|
|
||||||
"\\r2. Terrorist Important\n"
|
|
||||||
"3. Counter-Terrorist Important\n"
|
|
||||||
"\\w4. Block with hostage / Ladder\n"
|
|
||||||
"\\y5. Rescue Zone\n"
|
|
||||||
"\\w6. Camping\n"
|
|
||||||
"7. Camp End\n"
|
|
||||||
"\\r8. Map Goal\n"
|
|
||||||
"\\w9. Jump\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// set waypoint flag menu
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WAYPOINT_FLAG, buildKeys (5),
|
|
||||||
"\\yToggle Waypoint Flags\\w\n\n"
|
|
||||||
"1. Block with Hostage\n"
|
|
||||||
"2. Terrorists Specific\n"
|
|
||||||
"3. CTs Specific\n"
|
|
||||||
"4. Use Elevator\n"
|
|
||||||
"5. Sniper Point (\\yFor Camp Points Only!\\w)\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// auto-path max distance
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WAYPOINT_AUTOPATH, buildKeys (7),
|
|
||||||
"\\yAutoPath Distance\\w\n\n"
|
|
||||||
"1. Distance 0\n"
|
|
||||||
"2. Distance 100\n"
|
|
||||||
"3. Distance 130\n"
|
|
||||||
"4. Distance 160\n"
|
|
||||||
"5. Distance 190\n"
|
|
||||||
"6. Distance 220\n"
|
|
||||||
"7. Distance 250 (Default)\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
|
|
||||||
// path connections
|
|
||||||
g_menus[++counter] = {
|
|
||||||
BOT_MENU_WAYPOINT_PATH, buildKeys (3),
|
|
||||||
"\\yCreate Path (Choose Direction)\\w\n\n"
|
|
||||||
"1. Outgoing Path\n"
|
|
||||||
"2. Incoming Path\n"
|
|
||||||
"3. Bidirectional (Both Ways)\n\n"
|
|
||||||
"0. Exit"
|
|
||||||
};
|
|
||||||
const String &empty = "";
|
|
||||||
|
|
||||||
// kick menus
|
|
||||||
g_menus[++counter] = { BOT_MENU_KICK_PAGE_1, 0x0, empty, };
|
|
||||||
g_menus[++counter] = { BOT_MENU_KICK_PAGE_2, 0x0, empty, };
|
|
||||||
g_menus[++counter] = { BOT_MENU_KICK_PAGE_3, 0x0, empty, };
|
|
||||||
g_menus[++counter] = { BOT_MENU_KICK_PAGE_4, 0x0, empty, };
|
|
||||||
}
|
|
||||||
|
|
||||||
// bot menus
|
|
||||||
MenuText g_menus[BOT_MENU_TOTAL_MENUS];
|
|
||||||
3454
source/interface.cpp
3454
source/interface.cpp
File diff suppressed because it is too large
Load diff
1126
source/manager.cpp
1126
source/manager.cpp
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,3 @@
|
||||||
//
|
|
||||||
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
||||||
// Copyright (c) YaPB Development Team.
|
// Copyright (c) YaPB Development Team.
|
||||||
//
|
//
|
||||||
|
|
@ -9,13 +8,57 @@
|
||||||
|
|
||||||
#include <yapb.h>
|
#include <yapb.h>
|
||||||
|
|
||||||
ConVar yb_display_menu_text ("yb_display_menu_text", "1");
|
|
||||||
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1");
|
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1");
|
||||||
|
|
||||||
ConVar mp_roundtime ("mp_roundtime", nullptr, VT_NOREGISTER);
|
BotUtils::BotUtils (void) {
|
||||||
ConVar mp_freezetime ("mp_freezetime", nullptr, VT_NOREGISTER, true, "0");
|
m_needToSendWelcome = false;
|
||||||
|
m_welcomeReceiveTime = 0.0f;
|
||||||
|
|
||||||
const char *format (const char *format, ...) {
|
// add default messages
|
||||||
|
m_sentences.push ("hello user,communication is acquired");
|
||||||
|
m_sentences.push ("your presence is acknowledged");
|
||||||
|
m_sentences.push ("high man, your in command now");
|
||||||
|
m_sentences.push ("blast your hostile for good");
|
||||||
|
m_sentences.push ("high man, kill some idiot here");
|
||||||
|
m_sentences.push ("is there a doctor in the area");
|
||||||
|
m_sentences.push ("warning, experimental materials detected");
|
||||||
|
m_sentences.push ("high amigo, shoot some but");
|
||||||
|
m_sentences.push ("attention, hours of work software, detected");
|
||||||
|
m_sentences.push ("time for some bad ass explosion");
|
||||||
|
m_sentences.push ("bad ass son of a breach device activated");
|
||||||
|
m_sentences.push ("high, do not question this great service");
|
||||||
|
m_sentences.push ("engine is operative, hello and goodbye");
|
||||||
|
m_sentences.push ("high amigo, your administration has been great last day");
|
||||||
|
m_sentences.push ("attention, expect experimental armed hostile presence");
|
||||||
|
m_sentences.push ("warning, medical attention required");
|
||||||
|
|
||||||
|
m_tags.push ({ "[[", "]]" });
|
||||||
|
m_tags.push ({ "-=", "=-" });
|
||||||
|
m_tags.push ({ "-[", "]-" });
|
||||||
|
m_tags.push ({ "-]", "[-" });
|
||||||
|
m_tags.push ({ "-}", "{-" });
|
||||||
|
m_tags.push ({ "-{", "}-" });
|
||||||
|
m_tags.push ({ "<[", "]>" });
|
||||||
|
m_tags.push ({ "<]", "[>" });
|
||||||
|
m_tags.push ({ "[-", "-]" });
|
||||||
|
m_tags.push ({ "]-", "-[" });
|
||||||
|
m_tags.push ({ "{-", "-}" });
|
||||||
|
m_tags.push ({ "}-", "-{" });
|
||||||
|
m_tags.push ({ "[", "]" });
|
||||||
|
m_tags.push ({ "{", "}" });
|
||||||
|
m_tags.push ({ "<", "[" });
|
||||||
|
m_tags.push ({ ">", "<" });
|
||||||
|
m_tags.push ({ "-", "-" });
|
||||||
|
m_tags.push ({ "|", "|" });
|
||||||
|
m_tags.push ({ "=", "=" });
|
||||||
|
m_tags.push ({ "+", "+" });
|
||||||
|
m_tags.push ({ "(", ")" });
|
||||||
|
m_tags.push ({ ")", "(" });
|
||||||
|
|
||||||
|
m_clients.resize (MAX_ENGINE_PLAYERS + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *BotUtils::format (const char *format, ...) {
|
||||||
static char strBuffer[2][MAX_PRINT_BUFFER];
|
static char strBuffer[2][MAX_PRINT_BUFFER];
|
||||||
static int rotator = 0;
|
static int rotator = 0;
|
||||||
|
|
||||||
|
|
@ -32,30 +75,30 @@ const char *format (const char *format, ...) {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAlive (edict_t *ent) {
|
bool BotUtils::isAlive (edict_t *ent) {
|
||||||
if (engine.isNullEntity (ent)) {
|
if (game.isNullEntity (ent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return ent->v.deadflag == DEAD_NO && ent->v.health > 0 && ent->v.movetype != MOVETYPE_NOCLIP;
|
return ent->v.deadflag == DEAD_NO && ent->v.health > 0 && ent->v.movetype != MOVETYPE_NOCLIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getShootingConeDeviation (edict_t *ent, const Vector &position) {
|
float BotUtils::getShootingCone (edict_t *ent, const Vector &position) {
|
||||||
makeVectors (ent->v.v_angle);
|
game.makeVectors (ent->v.v_angle);
|
||||||
|
|
||||||
// he's facing it, he meant it
|
// he's facing it, he meant it
|
||||||
return g_pGlobals->v_forward | (position - (ent->v.origin + ent->v.view_ofs)).normalize ();
|
return game.vec.forward | (position - (ent->v.origin + ent->v.view_ofs)).normalize ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInViewCone (const Vector &origin, edict_t *ent) {
|
bool BotUtils::isInViewCone (const Vector &origin, edict_t *ent) {
|
||||||
return getShootingConeDeviation (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
|
return getShootingCone (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isVisible (const Vector &origin, edict_t *ent) {
|
bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
|
||||||
if (engine.isNullEntity (ent)) {
|
if (game.isNullEntity (ent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TraceResult tr;
|
TraceResult tr;
|
||||||
engine.testLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr);
|
game.testLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr);
|
||||||
|
|
||||||
if (tr.flFraction != 1.0f) {
|
if (tr.flFraction != 1.0f) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -63,81 +106,7 @@ bool isVisible (const Vector &origin, edict_t *ent) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void showMenu (edict_t *ent, MenuId menu) {
|
void BotUtils::traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
|
||||||
static bool s_menusParsed = false;
|
|
||||||
|
|
||||||
// make menus looks like we need only once
|
|
||||||
if (!s_menusParsed) {
|
|
||||||
extern void setupBotMenus (void);
|
|
||||||
setupBotMenus ();
|
|
||||||
|
|
||||||
for (int i = 0; i < BOT_MENU_TOTAL_MENUS; i++) {
|
|
||||||
auto parsed = &g_menus[i];
|
|
||||||
const String &translated = engine.translate (parsed->text.chars ());
|
|
||||||
|
|
||||||
// translate all the things
|
|
||||||
parsed->text = translated;
|
|
||||||
|
|
||||||
// make menu looks best
|
|
||||||
if (!(g_gameFlags & GAME_LEGACY)) {
|
|
||||||
for (int j = 0; j < 10; j++) {
|
|
||||||
parsed->text.replace (format ("%d.", j), format ("\\r%d.\\w", j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s_menusParsed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPlayer (ent)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Client &client = g_clients[engine.indexOfEntity (ent) - 1];
|
|
||||||
|
|
||||||
if (menu == BOT_MENU_INVALID) {
|
|
||||||
MessageWriter (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent)
|
|
||||||
.writeShort (0)
|
|
||||||
.writeChar (0)
|
|
||||||
.writeByte (0)
|
|
||||||
.writeString ("");
|
|
||||||
|
|
||||||
client.menu = menu;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int menuIndex = 0;
|
|
||||||
|
|
||||||
for (; menuIndex < BOT_MENU_TOTAL_MENUS; menuIndex++) {
|
|
||||||
if (g_menus[menuIndex].id == menu) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto &menuText = g_menus[menuIndex];
|
|
||||||
const char *text = ((g_gameFlags & (GAME_XASH_ENGINE | GAME_MOBILITY)) && !yb_display_menu_text.boolean ()) ? " " : menuText.text.chars ();
|
|
||||||
MessageWriter msg;
|
|
||||||
|
|
||||||
while (strlen (text) >= 64) {
|
|
||||||
msg.start (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent)
|
|
||||||
.writeShort (menuText.slots)
|
|
||||||
.writeChar (-1)
|
|
||||||
.writeByte (1);
|
|
||||||
|
|
||||||
for (int i = 0; i < 64; i++) {
|
|
||||||
msg.writeChar (text[i]);
|
|
||||||
}
|
|
||||||
msg.end ();
|
|
||||||
text += 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageWriter (MSG_ONE_UNRELIABLE, engine.getMessageId (NETMSG_SHOWMENU), Vector::null (), ent)
|
|
||||||
.writeShort (menuText.slots)
|
|
||||||
.writeChar (-1)
|
|
||||||
.writeByte (0)
|
|
||||||
.writeString (text);
|
|
||||||
|
|
||||||
client.menu = menu;
|
|
||||||
g_engfuncs.pfnClientCommand (ent, "speak \"player/geiger1\"\n"); // Stops others from hearing menu sounds..
|
|
||||||
}
|
|
||||||
|
|
||||||
void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
|
|
||||||
// this function draw spraypaint depending on the tracing results.
|
// this function draw spraypaint depending on the tracing results.
|
||||||
|
|
||||||
static StringArray logotypes;
|
static StringArray logotypes;
|
||||||
|
|
@ -146,18 +115,18 @@ void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
|
||||||
logotypes = String ("{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r").split (";");
|
logotypes = String ("{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r").split (";");
|
||||||
}
|
}
|
||||||
int entityIndex = -1, message = TE_DECAL;
|
int entityIndex = -1, message = TE_DECAL;
|
||||||
int decalIndex = g_engfuncs.pfnDecalIndex (logotypes[logotypeIndex].chars ());
|
int decalIndex = engfuncs.pfnDecalIndex (logotypes[logotypeIndex].chars ());
|
||||||
|
|
||||||
if (decalIndex < 0) {
|
if (decalIndex < 0) {
|
||||||
decalIndex = g_engfuncs.pfnDecalIndex ("{lambda06");
|
decalIndex = engfuncs.pfnDecalIndex ("{lambda06");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trace->flFraction == 1.0f) {
|
if (trace->flFraction == 1.0f) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!engine.isNullEntity (trace->pHit)) {
|
if (!game.isNullEntity (trace->pHit)) {
|
||||||
if (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) {
|
if (trace->pHit->v.solid == SOLID_BSP || trace->pHit->v.movetype == MOVETYPE_PUSHSTEP) {
|
||||||
entityIndex = engine.indexOfEntity (trace->pHit);
|
entityIndex = game.indexOfEntity (trace->pHit);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -185,11 +154,11 @@ void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
|
||||||
if (logotypes[logotypeIndex].contains ("{")) {
|
if (logotypes[logotypeIndex].contains ("{")) {
|
||||||
MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY)
|
MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY)
|
||||||
.writeByte (TE_PLAYERDECAL)
|
.writeByte (TE_PLAYERDECAL)
|
||||||
.writeByte (engine.indexOfEntity (pev->pContainingEntity))
|
.writeByte (game.indexOfEntity (pev->pContainingEntity))
|
||||||
.writeCoord (trace->vecEndPos.x)
|
.writeCoord (trace->vecEndPos.x)
|
||||||
.writeCoord (trace->vecEndPos.y)
|
.writeCoord (trace->vecEndPos.y)
|
||||||
.writeCoord (trace->vecEndPos.z)
|
.writeCoord (trace->vecEndPos.z)
|
||||||
.writeShort (static_cast <short> (engine.indexOfEntity (trace->pHit)))
|
.writeShort (static_cast <short> (game.indexOfEntity (trace->pHit)))
|
||||||
.writeByte (decalIndex);
|
.writeByte (decalIndex);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -209,185 +178,8 @@ void traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupGarbage (void) {
|
bool BotUtils::isPlayer (edict_t *ent) {
|
||||||
// this function free's all allocated memory
|
if (game.isNullEntity (ent)) {
|
||||||
waypoints.init (); // frees waypoint data
|
|
||||||
|
|
||||||
delete[] g_experienceData;
|
|
||||||
g_experienceData = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateGlobalExperience (void) {
|
|
||||||
// this function called after each end of the round to update knowledge about most dangerous waypoints for each team.
|
|
||||||
|
|
||||||
// no waypoints, no experience used or waypoints edited or being edited?
|
|
||||||
if (waypoints.length () < 1 || waypoints.hasChanged ()) {
|
|
||||||
return; // no action
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16 maxDamage; // maximum damage
|
|
||||||
uint16 actDamage; // actual damage
|
|
||||||
|
|
||||||
int bestIndex; // best index to store
|
|
||||||
bool recalcKills = false;
|
|
||||||
|
|
||||||
// get the most dangerous waypoint for this position for terrorist team
|
|
||||||
for (int i = 0; i < waypoints.length (); i++) {
|
|
||||||
maxDamage = 0;
|
|
||||||
bestIndex = INVALID_WAYPOINT_INDEX;
|
|
||||||
|
|
||||||
for (int j = 0; j < waypoints.length (); j++) {
|
|
||||||
if (i == j) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
actDamage = (g_experienceData + (i * waypoints.length ()) + j)->team0Damage;
|
|
||||||
|
|
||||||
if (actDamage > maxDamage) {
|
|
||||||
maxDamage = actDamage;
|
|
||||||
bestIndex = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxDamage > MAX_DAMAGE_VALUE) {
|
|
||||||
recalcKills = true;
|
|
||||||
}
|
|
||||||
(g_experienceData + (i * waypoints.length ()) + i)->team0DangerIndex = static_cast <short> (bestIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the most dangerous waypoint for this position for counter-terrorist team
|
|
||||||
for (int i = 0; i < waypoints.length (); i++) {
|
|
||||||
maxDamage = 0;
|
|
||||||
bestIndex = INVALID_WAYPOINT_INDEX;
|
|
||||||
|
|
||||||
for (int j = 0; j < waypoints.length (); j++) {
|
|
||||||
if (i == j) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
actDamage = (g_experienceData + (i * waypoints.length ()) + j)->team1Damage;
|
|
||||||
|
|
||||||
if (actDamage > maxDamage) {
|
|
||||||
maxDamage = actDamage;
|
|
||||||
bestIndex = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxDamage > MAX_DAMAGE_VALUE) {
|
|
||||||
recalcKills = true;
|
|
||||||
}
|
|
||||||
(g_experienceData + (i * waypoints.length ()) + i)->team1DangerIndex = static_cast <short> (bestIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust values if overflow is about to happen
|
|
||||||
if (recalcKills) {
|
|
||||||
for (int i = 0; i < waypoints.length (); i++) {
|
|
||||||
for (int j = 0; j < waypoints.length (); j++) {
|
|
||||||
if (i == j) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int clip = (g_experienceData + (i * waypoints.length ()) + j)->team0Damage;
|
|
||||||
clip -= static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
|
|
||||||
|
|
||||||
if (clip < 0) {
|
|
||||||
clip = 0;
|
|
||||||
}
|
|
||||||
(g_experienceData + (i * waypoints.length ()) + j)->team0Damage = static_cast <uint16> (clip);
|
|
||||||
|
|
||||||
clip = (g_experienceData + (i * waypoints.length ()) + j)->team1Damage;
|
|
||||||
clip -= static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
|
|
||||||
|
|
||||||
if (clip < 0) {
|
|
||||||
clip = 0;
|
|
||||||
}
|
|
||||||
(g_experienceData + (i * waypoints.length ()) + j)->team1Damage = static_cast <uint16> (clip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_highestKills++;
|
|
||||||
|
|
||||||
int clip = g_highestDamageT - static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
|
|
||||||
|
|
||||||
if (clip < 1) {
|
|
||||||
clip = 1;
|
|
||||||
}
|
|
||||||
g_highestDamageT = clip;
|
|
||||||
|
|
||||||
clip = (int)g_highestDamageCT - static_cast <int> (MAX_DAMAGE_VALUE * 0.5);
|
|
||||||
|
|
||||||
if (clip < 1) {
|
|
||||||
clip = 1;
|
|
||||||
}
|
|
||||||
g_highestDamageCT = clip;
|
|
||||||
|
|
||||||
if (g_highestKills == MAX_KILL_HISTORY) {
|
|
||||||
for (int i = 0; i < waypoints.length (); i++) {
|
|
||||||
(g_experienceData + (i * waypoints.length ()) + i)->team0Damage /= static_cast <uint16> (engine.maxClients () * 0.5);
|
|
||||||
(g_experienceData + (i * waypoints.length ()) + i)->team1Damage /= static_cast <uint16> (engine.maxClients () * 0.5);
|
|
||||||
}
|
|
||||||
g_highestKills = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initRound (void) {
|
|
||||||
// this is called at the start of each round
|
|
||||||
|
|
||||||
g_roundEnded = false;
|
|
||||||
g_canSayBombPlanted = true;
|
|
||||||
|
|
||||||
// check team economics
|
|
||||||
for (int team = 0; team < MAX_TEAM_COUNT; team++) {
|
|
||||||
bots.updateTeamEconomics (team);
|
|
||||||
bots.selectLeaders (team, true);
|
|
||||||
}
|
|
||||||
bots.reset ();
|
|
||||||
|
|
||||||
for (int i = 0; i < engine.maxClients (); i++) {
|
|
||||||
auto bot = bots.getBot (i);
|
|
||||||
|
|
||||||
if (bot != nullptr) {
|
|
||||||
bot->newRound ();
|
|
||||||
}
|
|
||||||
g_radioSelect[i] = 0;
|
|
||||||
}
|
|
||||||
waypoints.setBombPos (true);
|
|
||||||
waypoints.clearVisited ();
|
|
||||||
|
|
||||||
g_bombSayString = false;
|
|
||||||
g_timeBombPlanted = 0.0f;
|
|
||||||
g_timeNextBombUpdate = 0.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_TEAM_COUNT; i++) {
|
|
||||||
g_lastRadioTime[i] = 0.0f;
|
|
||||||
}
|
|
||||||
g_botsCanPause = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < TASK_MAX; i++) {
|
|
||||||
g_taskFilters[i].time = 0.0f;
|
|
||||||
}
|
|
||||||
updateGlobalExperience (); // update experience data on round start
|
|
||||||
|
|
||||||
// calculate the round mid/end in world time
|
|
||||||
g_timeRoundStart = engine.timebase () + mp_freezetime.flt ();
|
|
||||||
g_timeRoundMid = g_timeRoundStart + mp_roundtime.flt () * 60.0f * 0.5f;
|
|
||||||
g_timeRoundEnd = g_timeRoundStart + mp_roundtime.flt () * 60.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getWeaponPenetrationPower (int id) {
|
|
||||||
// returns if weapon can pierce through a wall
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
while (g_weaponSelect[i].id) {
|
|
||||||
if (g_weaponSelect[i].id == id) {
|
|
||||||
return g_weaponSelect[i].penetratePower;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPlayer (edict_t *ent) {
|
|
||||||
if (engine.isNullEntity (ent)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,25 +193,25 @@ bool isPlayer (edict_t *ent) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isPlayerVIP (edict_t *ent) {
|
bool BotUtils::isPlayerVIP (edict_t *ent) {
|
||||||
if (!(g_mapFlags & MAP_AS)) {
|
if (!game.mapIs (MAP_AS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPlayer (ent)) {
|
if (!isPlayer (ent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return *(g_engfuncs.pfnInfoKeyValue (g_engfuncs.pfnGetInfoKeyBuffer (ent), "model")) == 'v';
|
return *(engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (ent), "model")) == 'v';
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFakeClient (edict_t *ent) {
|
bool BotUtils::isFakeClient (edict_t *ent) {
|
||||||
if (bots.getBot (ent) != nullptr || (!engine.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
|
if (bots.getBot (ent) != nullptr || (!game.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
|
bool BotUtils::openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
|
||||||
if (outFile->isValid ()) {
|
if (outFile->isValid ()) {
|
||||||
outFile->close ();
|
outFile->close ();
|
||||||
}
|
}
|
||||||
|
|
@ -446,11 +238,13 @@ bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *ou
|
||||||
// unload and reopen file using MemoryFile
|
// unload and reopen file using MemoryFile
|
||||||
outFile->open (langConfig);
|
outFile->open (langConfig);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
outFile->open (format ("%s/lang/en_%s", configDir, fileName));
|
outFile->open (format ("%s/lang/en_%s", configDir, fileName));
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
outFile->open (format ("%s/%s", configDir, fileName));
|
outFile->open (format ("%s/%s", configDir, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
if (!outFile->isValid ()) {
|
if (!outFile->isValid ()) {
|
||||||
logEntry (true, LL_ERROR, errorIfNotExists);
|
logEntry (true, LL_ERROR, errorIfNotExists);
|
||||||
|
|
@ -459,57 +253,31 @@ bool openConfig (const char *fileName, const char *errorIfNotExists, MemFile *ou
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkWelcome (void) {
|
void BotUtils::checkWelcome (void) {
|
||||||
// the purpose of this function, is to send quick welcome message, to the listenserver entity.
|
// the purpose of this function, is to send quick welcome message, to the listenserver entity.
|
||||||
|
|
||||||
if (engine.isDedicated ())
|
if (game.isDedicated () || !yb_display_welcome_text.boolean () || !m_needToSendWelcome) {
|
||||||
return;
|
|
||||||
|
|
||||||
static bool messageSent = !yb_display_welcome_text.boolean ();
|
|
||||||
static float receiveTime = 0.0f;
|
|
||||||
|
|
||||||
if (messageSent) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_welcomeReceiveTime = 0.0f;
|
||||||
|
|
||||||
if (g_gameFlags & GAME_LEGACY) {
|
if (game.is (GAME_LEGACY)) {
|
||||||
g_gameWelcomeSent = true;
|
m_needToSendWelcome = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool needToSendMsg = (waypoints.length () > 0 ? m_needToSendWelcome : true);
|
||||||
|
|
||||||
|
if (isAlive (game.getLocalEntity ()) && m_welcomeReceiveTime < 1.0 && needToSendMsg) {
|
||||||
|
m_welcomeReceiveTime = game.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing
|
||||||
}
|
}
|
||||||
|
|
||||||
static StringArray sentences;
|
if (m_welcomeReceiveTime > 0.0f && needToSendMsg) {
|
||||||
|
if (!game.is (GAME_MOBILITY | GAME_XASH_ENGINE)) {
|
||||||
if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE)) && sentences.empty ()) {
|
game.execCmd ("speak \"%s\"", m_sentences.random ().chars ());
|
||||||
// add default messages
|
|
||||||
sentences.push ("hello user,communication is acquired");
|
|
||||||
sentences.push ("your presence is acknowledged");
|
|
||||||
sentences.push ("high man, your in command now");
|
|
||||||
sentences.push ("blast your hostile for good");
|
|
||||||
sentences.push ("high man, kill some idiot here");
|
|
||||||
sentences.push ("is there a doctor in the area");
|
|
||||||
sentences.push ("warning, experimental materials detected");
|
|
||||||
sentences.push ("high amigo, shoot some but");
|
|
||||||
sentences.push ("attention, hours of work software, detected");
|
|
||||||
sentences.push ("time for some bad ass explosion");
|
|
||||||
sentences.push ("bad ass son of a breach device activated");
|
|
||||||
sentences.push ("high, do not question this great service");
|
|
||||||
sentences.push ("engine is operative, hello and goodbye");
|
|
||||||
sentences.push ("high amigo, your administration has been great last day");
|
|
||||||
sentences.push ("attention, expect experimental armed hostile presence");
|
|
||||||
sentences.push ("warning, medical attention required");
|
|
||||||
}
|
}
|
||||||
bool needToSendMsg = (waypoints.length () > 0 ? g_gameWelcomeSent : true);
|
game.chatPrint ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL);
|
||||||
|
|
||||||
if (isAlive (g_hostEntity) && receiveTime < 1.0 && needToSendMsg) {
|
MessageWriter (MSG_ONE, SVC_TEMPENTITY, Vector::null (), game.getLocalEntity ())
|
||||||
receiveTime = engine.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receiveTime > 0.0f && receiveTime < engine.timebase () && needToSendMsg) {
|
|
||||||
if (!(g_gameFlags & (GAME_MOBILITY | GAME_XASH_ENGINE))) {
|
|
||||||
engine.execCmd ("speak \"%s\"", sentences.random ().chars ());
|
|
||||||
}
|
|
||||||
engine.chatPrint ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL);
|
|
||||||
|
|
||||||
MessageWriter (MSG_ONE, SVC_TEMPENTITY, Vector::null (), g_hostEntity)
|
|
||||||
.writeByte (TE_TEXTMESSAGE)
|
.writeByte (TE_TEXTMESSAGE)
|
||||||
.writeByte (1)
|
.writeByte (1)
|
||||||
.writeShort (MessageWriter::fs16 (-1, 1 << 13))
|
.writeShort (MessageWriter::fs16 (-1, 1 << 13))
|
||||||
|
|
@ -529,12 +297,12 @@ void checkWelcome (void) {
|
||||||
.writeShort (MessageWriter::fu16 (0.1f, 1 << 8))
|
.writeShort (MessageWriter::fu16 (0.1f, 1 << 8))
|
||||||
.writeString (format ("\nServer is running %s v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, waypoints.getAuthor ()));
|
.writeString (format ("\nServer is running %s v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, waypoints.getAuthor ()));
|
||||||
|
|
||||||
receiveTime = 0.0;
|
m_welcomeReceiveTime = 0.0f;
|
||||||
messageSent = true;
|
m_needToSendWelcome = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
|
void BotUtils::logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
|
||||||
// this function logs a message to the message log file root directory.
|
// this function logs a message to the message log file root directory.
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
@ -563,7 +331,7 @@ void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputToConsole) {
|
if (outputToConsole) {
|
||||||
engine.print ("%s%s", levelString, buffer);
|
game.print ("%s%s", levelString, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check if logging disabled
|
// now check if logging disabled
|
||||||
|
|
@ -599,7 +367,7 @@ void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
|
||||||
|
|
||||||
if (logLevel == LL_FATAL) {
|
if (logLevel == LL_FATAL) {
|
||||||
bots.kickEveryone (true);
|
bots.kickEveryone (true);
|
||||||
cleanupGarbage ();
|
waypoints.init ();
|
||||||
|
|
||||||
#if defined(PLATFORM_WIN32)
|
#if defined(PLATFORM_WIN32)
|
||||||
DestroyWindow (GetForegroundWindow ());
|
DestroyWindow (GetForegroundWindow ());
|
||||||
|
|
@ -616,7 +384,7 @@ void logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool isAlive, bool needDrawn, bool needBotWithC4) {
|
bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool needAlive, bool needDrawn, bool needBotWithC4) {
|
||||||
// this function finds nearest to to, player with set of parameters, like his
|
// this function finds nearest to to, player with set of parameters, like his
|
||||||
// team, live status, search distance etc. if needBot is true, then pvHolder, will
|
// team, live status, search distance etc. if needBot is true, then pvHolder, will
|
||||||
// be filled with bot pointer, else with edict pointer(!).
|
// be filled with bot pointer, else with edict pointer(!).
|
||||||
|
|
@ -624,16 +392,14 @@ bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool
|
||||||
edict_t *survive = nullptr; // pointer to temporally & survive entity
|
edict_t *survive = nullptr; // pointer to temporally & survive entity
|
||||||
float nearestPlayer = 4096.0f; // nearest player
|
float nearestPlayer = 4096.0f; // nearest player
|
||||||
|
|
||||||
int toTeam = engine.getTeam (to);
|
int toTeam = game.getTeam (to);
|
||||||
|
|
||||||
for (int i = 0; i < engine.maxClients (); i++) {
|
|
||||||
const Client &client = g_clients[i];
|
|
||||||
|
|
||||||
|
for (const auto &client : m_clients) {
|
||||||
if (!(client.flags & CF_USED) || client.ent == to) {
|
if (!(client.flags & CF_USED) || client.ent == to) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sameTeam && client.team != toTeam) || (isAlive && !(client.flags & CF_ALIVE)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & WEAPON_C4))) {
|
if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & CF_ALIVE)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & WEAPON_C4))) {
|
||||||
continue; // filter players with parameters
|
continue; // filter players with parameters
|
||||||
}
|
}
|
||||||
float distance = (client.ent->v.origin - to->v.origin).length ();
|
float distance = (client.ent->v.origin - to->v.origin).length ();
|
||||||
|
|
@ -644,8 +410,9 @@ bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (engine.isNullEntity (survive))
|
if (game.isNullEntity (survive)) {
|
||||||
return false; // nothing found
|
return false; // nothing found
|
||||||
|
}
|
||||||
|
|
||||||
// fill the holder
|
// fill the holder
|
||||||
if (needBot) {
|
if (needBot) {
|
||||||
|
|
@ -657,26 +424,26 @@ bool findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void attachSoundsToClients (edict_t *ent, const char *sample, float volume) {
|
void BotUtils::attachSoundsToClients (edict_t *ent, const char *sample, float volume) {
|
||||||
// this function called by the sound hooking code (in emit_sound) enters the played sound into
|
// this function called by the sound hooking code (in emit_sound) enters the played sound into
|
||||||
// the array associated with the entity
|
// the array associated with the entity
|
||||||
|
|
||||||
if (engine.isNullEntity (ent) || isEmptyStr (sample)) {
|
if (game.isNullEntity (ent) || isEmptyStr (sample)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const Vector &origin = engine.getAbsPos (ent);
|
const Vector &origin = game.getAbsPos (ent);
|
||||||
|
|
||||||
if (origin.empty ()) {
|
if (origin.empty ()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int index = engine.indexOfEntity (ent) - 1;
|
int index = game.indexOfEntity (ent) - 1;
|
||||||
|
|
||||||
if (index < 0 || index >= engine.maxClients ()) {
|
if (index < 0 || index >= game.maxClients ()) {
|
||||||
float nearestDistance = 99999.0f;
|
float nearestDistance = 99999.0f;
|
||||||
|
|
||||||
// loop through all players
|
// loop through all players
|
||||||
for (int i = 0; i < engine.maxClients (); i++) {
|
for (int i = 0; i < game.maxClients (); i++) {
|
||||||
const Client &client = g_clients[i];
|
const Client &client = m_clients[i];
|
||||||
|
|
||||||
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE)) {
|
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -692,63 +459,63 @@ void attachSoundsToClients (edict_t *ent, const char *sample, float volume) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case of worst case
|
// in case of worst case
|
||||||
if (index < 0 || index >= engine.maxClients ()) {
|
if (index < 0 || index >= game.maxClients ()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Client &client = g_clients[index];
|
Client &client = m_clients[index];
|
||||||
|
|
||||||
if (strncmp ("player/bhit_flesh", sample, 17) == 0 || strncmp ("player/headshot", sample, 15) == 0) {
|
if (strncmp ("player/bhit_flesh", sample, 17) == 0 || strncmp ("player/headshot", sample, 15) == 0) {
|
||||||
// hit/fall sound?
|
// hit/fall sound?
|
||||||
client.hearingDistance = 768.0f * volume;
|
client.hearingDistance = 768.0f * volume;
|
||||||
client.timeSoundLasting = engine.timebase () + 0.5f;
|
client.timeSoundLasting = game.timebase () + 0.5f;
|
||||||
client.soundPos = origin;
|
client.sound = origin;
|
||||||
}
|
}
|
||||||
else if (strncmp ("items/gunpickup", sample, 15) == 0) {
|
else if (strncmp ("items/gunpickup", sample, 15) == 0) {
|
||||||
// weapon pickup?
|
// weapon pickup?
|
||||||
client.hearingDistance = 768.0f * volume;
|
client.hearingDistance = 768.0f * volume;
|
||||||
client.timeSoundLasting = engine.timebase () + 0.5f;
|
client.timeSoundLasting = game.timebase () + 0.5f;
|
||||||
client.soundPos = origin;
|
client.sound = origin;
|
||||||
}
|
}
|
||||||
else if (strncmp ("weapons/zoom", sample, 12) == 0) {
|
else if (strncmp ("weapons/zoom", sample, 12) == 0) {
|
||||||
// sniper zooming?
|
// sniper zooming?
|
||||||
client.hearingDistance = 512.0f * volume;
|
client.hearingDistance = 512.0f * volume;
|
||||||
client.timeSoundLasting = engine.timebase () + 0.1f;
|
client.timeSoundLasting = game.timebase () + 0.1f;
|
||||||
client.soundPos = origin;
|
client.sound = origin;
|
||||||
}
|
}
|
||||||
else if (strncmp ("items/9mmclip", sample, 13) == 0) {
|
else if (strncmp ("items/9mmclip", sample, 13) == 0) {
|
||||||
// ammo pickup?
|
// ammo pickup?
|
||||||
client.hearingDistance = 512.0f * volume;
|
client.hearingDistance = 512.0f * volume;
|
||||||
client.timeSoundLasting = engine.timebase () + 0.1f;
|
client.timeSoundLasting = game.timebase () + 0.1f;
|
||||||
client.soundPos = origin;
|
client.sound = origin;
|
||||||
}
|
}
|
||||||
else if (strncmp ("hostage/hos", sample, 11) == 0) {
|
else if (strncmp ("hostage/hos", sample, 11) == 0) {
|
||||||
// CT used hostage?
|
// CT used hostage?
|
||||||
client.hearingDistance = 1024.0f * volume;
|
client.hearingDistance = 1024.0f * volume;
|
||||||
client.timeSoundLasting = engine.timebase () + 5.0f;
|
client.timeSoundLasting = game.timebase () + 5.0f;
|
||||||
client.soundPos = origin;
|
client.sound = origin;
|
||||||
}
|
}
|
||||||
else if (strncmp ("debris/bustmetal", sample, 16) == 0 || strncmp ("debris/bustglass", sample, 16) == 0) {
|
else if (strncmp ("debris/bustmetal", sample, 16) == 0 || strncmp ("debris/bustglass", sample, 16) == 0) {
|
||||||
// broke something?
|
// broke something?
|
||||||
client.hearingDistance = 1024.0f * volume;
|
client.hearingDistance = 1024.0f * volume;
|
||||||
client.timeSoundLasting = engine.timebase () + 2.0f;
|
client.timeSoundLasting = game.timebase () + 2.0f;
|
||||||
client.soundPos = origin;
|
client.sound = origin;
|
||||||
}
|
}
|
||||||
else if (strncmp ("doors/doormove", sample, 14) == 0) {
|
else if (strncmp ("doors/doormove", sample, 14) == 0) {
|
||||||
// someone opened a door
|
// someone opened a door
|
||||||
client.hearingDistance = 1024.0f * volume;
|
client.hearingDistance = 1024.0f * volume;
|
||||||
client.timeSoundLasting = engine.timebase () + 3.0f;
|
client.timeSoundLasting = game.timebase () + 3.0f;
|
||||||
client.soundPos = origin;
|
client.sound = origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void simulateSoundUpdates (int playerIndex) {
|
void BotUtils::simulateSoundUpdates (int playerIndex) {
|
||||||
// this function tries to simulate playing of sounds to let the bots hear sounds which aren't
|
// this function tries to simulate playing of sounds to let the bots hear sounds which aren't
|
||||||
// captured through server sound hooking
|
// captured through server sound hooking
|
||||||
|
|
||||||
if (playerIndex < 0 || playerIndex >= engine.maxClients ()) {
|
if (playerIndex < 0 || playerIndex >= game.maxClients ()) {
|
||||||
return; // reliability check
|
return; // reliability check
|
||||||
}
|
}
|
||||||
Client &client = g_clients[playerIndex];
|
Client &client = m_clients[playerIndex];
|
||||||
|
|
||||||
float hearDistance = 0.0f;
|
float hearDistance = 0.0f;
|
||||||
float timeSound = 0.0f;
|
float timeSound = 0.0f;
|
||||||
|
|
@ -756,23 +523,23 @@ void simulateSoundUpdates (int playerIndex) {
|
||||||
if (client.ent->v.oldbuttons & IN_ATTACK) // pressed attack button?
|
if (client.ent->v.oldbuttons & IN_ATTACK) // pressed attack button?
|
||||||
{
|
{
|
||||||
hearDistance = 2048.0f;
|
hearDistance = 2048.0f;
|
||||||
timeSound = engine.timebase () + 0.3f;
|
timeSound = game.timebase () + 0.3f;
|
||||||
}
|
}
|
||||||
else if (client.ent->v.oldbuttons & IN_USE) // pressed used button?
|
else if (client.ent->v.oldbuttons & IN_USE) // pressed used button?
|
||||||
{
|
{
|
||||||
hearDistance = 512.0f;
|
hearDistance = 512.0f;
|
||||||
timeSound = engine.timebase () + 0.5f;
|
timeSound = game.timebase () + 0.5f;
|
||||||
}
|
}
|
||||||
else if (client.ent->v.oldbuttons & IN_RELOAD) // pressed reload button?
|
else if (client.ent->v.oldbuttons & IN_RELOAD) // pressed reload button?
|
||||||
{
|
{
|
||||||
hearDistance = 512.0f;
|
hearDistance = 512.0f;
|
||||||
timeSound = engine.timebase () + 0.5f;
|
timeSound = game.timebase () + 0.5f;
|
||||||
}
|
}
|
||||||
else if (client.ent->v.movetype == MOVETYPE_FLY) // uses ladder?
|
else if (client.ent->v.movetype == MOVETYPE_FLY) // uses ladder?
|
||||||
{
|
{
|
||||||
if (cr::abs (client.ent->v.velocity.z) > 50.0f) {
|
if (cr::abs (client.ent->v.velocity.z) > 50.0f) {
|
||||||
hearDistance = 1024.0f;
|
hearDistance = 1024.0f;
|
||||||
timeSound = engine.timebase () + 0.3f;
|
timeSound = game.timebase () + 0.3f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -781,7 +548,7 @@ void simulateSoundUpdates (int playerIndex) {
|
||||||
if (mp_footsteps.boolean ()) {
|
if (mp_footsteps.boolean ()) {
|
||||||
// moves fast enough?
|
// moves fast enough?
|
||||||
hearDistance = 1280.0f * (client.ent->v.velocity.length2D () / 260.0f);
|
hearDistance = 1280.0f * (client.ent->v.velocity.length2D () / 260.0f);
|
||||||
timeSound = engine.timebase () + 0.3f;
|
timeSound = game.timebase () + 0.3f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -790,23 +557,52 @@ void simulateSoundUpdates (int playerIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// some sound already associated
|
// some sound already associated
|
||||||
if (client.timeSoundLasting > engine.timebase ()) {
|
if (client.timeSoundLasting > game.timebase ()) {
|
||||||
if (client.hearingDistance <= hearDistance) {
|
if (client.hearingDistance <= hearDistance) {
|
||||||
// override it with new
|
// override it with new
|
||||||
client.hearingDistance = hearDistance;
|
client.hearingDistance = hearDistance;
|
||||||
client.timeSoundLasting = timeSound;
|
client.timeSoundLasting = timeSound;
|
||||||
client.soundPos = client.ent->v.origin;
|
client.sound = client.ent->v.origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// just remember it
|
// just remember it
|
||||||
client.hearingDistance = hearDistance;
|
client.hearingDistance = hearDistance;
|
||||||
client.timeSoundLasting = timeSound;
|
client.timeSoundLasting = timeSound;
|
||||||
client.soundPos = client.ent->v.origin;
|
client.sound = client.ent->v.origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int buildNumber (void) {
|
void BotUtils::updateClients (void) {
|
||||||
|
// record some stats of all players on the server
|
||||||
|
for (int i = 0; i < game.maxClients (); i++) {
|
||||||
|
edict_t *player = game.entityOfIndex (i + 1);
|
||||||
|
Client &client = m_clients[i];
|
||||||
|
|
||||||
|
if (!game.isNullEntity (player) && (player->v.flags & FL_CLIENT)) {
|
||||||
|
client.ent = player;
|
||||||
|
client.flags |= CF_USED;
|
||||||
|
|
||||||
|
if (util.isAlive (player)) {
|
||||||
|
client.flags |= CF_ALIVE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
client.flags &= ~CF_ALIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.flags & CF_ALIVE) {
|
||||||
|
client.origin = player->v.origin;
|
||||||
|
simulateSoundUpdates (i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
client.flags &= ~(CF_USED | CF_ALIVE);
|
||||||
|
client.ent = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BotUtils::buildNumber (void) {
|
||||||
// this function generates build number from the compiler date macros
|
// this function generates build number from the compiler date macros
|
||||||
|
|
||||||
static int buildNumber = 0;
|
static int buildNumber = 0;
|
||||||
|
|
@ -848,7 +644,7 @@ int buildNumber (void) {
|
||||||
return buildNumber;
|
return buildNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getWeaponData (bool needString, const char *weaponAlias, int weaponIndex) {
|
int BotUtils::getWeaponAlias (bool needString, const char *weaponAlias, int weaponIndex) {
|
||||||
// this function returning weapon id from the weapon alias and vice versa.
|
// this function returning weapon id from the weapon alias and vice versa.
|
||||||
|
|
||||||
// structure definition for weapon tab
|
// structure definition for weapon tab
|
||||||
|
|
@ -895,18 +691,18 @@ int getWeaponData (bool needString, const char *weaponAlias, int weaponIndex) {
|
||||||
|
|
||||||
// if we need to return the string, find by weapon id
|
// if we need to return the string, find by weapon id
|
||||||
if (needString && weaponIndex != -1) {
|
if (needString && weaponIndex != -1) {
|
||||||
for (size_t i = 0; i < cr::arrsize (weaponTab); i++) {
|
for (auto &tab : weaponTab) {
|
||||||
if (weaponTab[i].weaponIndex == weaponIndex) { // is weapon id found?
|
if (tab.weaponIndex == weaponIndex) { // is weapon id found?
|
||||||
return MAKE_STRING (weaponTab[i].alias);
|
return MAKE_STRING (tab.alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MAKE_STRING ("(none)"); // return none
|
return MAKE_STRING ("(none)"); // return none
|
||||||
}
|
}
|
||||||
|
|
||||||
// else search weapon by name and return weapon id
|
// else search weapon by name and return weapon id
|
||||||
for (size_t i = 0; i < cr::arrsize (weaponTab); i++) {
|
for (auto &tab : weaponTab) {
|
||||||
if (strncmp (weaponTab[i].alias, weaponAlias, strlen (weaponTab[i].alias)) == 0) {
|
if (strncmp (tab.alias, weaponAlias, strlen (tab.alias)) == 0) {
|
||||||
return weaponTab[i].weaponIndex;
|
return tab.weaponIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1; // no weapon was found return -1
|
return -1; // no weapon was found return -1
|
||||||
|
|
|
||||||
1106
source/waypoint.cpp
1106
source/waypoint.cpp
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue