diff options
Diffstat (limited to 'src')
312 files changed, 28481 insertions, 30044 deletions
diff --git a/src/base/math.hpp b/src/base/math.h index 302935d7..f3ba2ea8 100644 --- a/src/base/math.hpp +++ b/src/base/math.h @@ -49,20 +49,10 @@ public: operator float() const { return value/(float)(1<<10); } }; -class tune_param -{ - int value; -public: - void set(int v) { value = v; } - int get() const { return value; } - tune_param &operator = (int v) { value = (int)(v*100.0f); return *this; } - tune_param &operator = (float v) { value = (int)(v*100.0f); return *this; } - operator float() const { return value/100.0f; } -}; - const float pi = 3.1415926535897932384626433f; template <typename T> inline T min(T a, T b) { return a<b?a:b; } template <typename T> inline T max(T a, T b) { return a>b?a:b; } +template <typename T> inline T absolute(T a) { return a<T(0)?-a:a; } #endif // BASE_MATH_H diff --git a/src/base/system.c b/src/base/system.c index f570fdf3..47893aa3 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -42,7 +42,9 @@ #include <direct.h> #include <errno.h> - #define EWOULDBLOCK WSAEWOULDBLOCK + #ifndef EWOULDBLOCK + #define EWOULDBLOCK WSAEWOULDBLOCK + #endif #else #error NOT IMPLEMENTED #endif @@ -86,11 +88,11 @@ void dbg_msg(const char *sys, const char *fmt, ...) char str[1024*4]; char *msg; int i, len; - + str_format(str, sizeof(str), "[%08x][%s]: ", (int)time(0), sys); len = strlen(str); msg = (char *)str + len; - + va_start(args, fmt); #if defined(CONF_FAMILY_WINDOWS) _vsnprintf(msg, sizeof(str)-len, fmt, args); @@ -98,7 +100,7 @@ void dbg_msg(const char *sys, const char *fmt, ...) vsnprintf(msg, sizeof(str)-len, fmt, args); #endif va_end(args); - + for(i = 0; i < num_loggers; i++) loggers[i](str); } @@ -118,7 +120,7 @@ static void logger_debugger(const char *line) } -IOHANDLE logfile = 0; +static IOHANDLE logfile = 0; static void logger_file(const char *line) { io_write(logfile, line, strlen(line)); @@ -137,7 +139,6 @@ void dbg_logger_file(const char *filename) dbg_msg("dbg/logger", "failed to open '%s' for logging", filename); } - /* */ int memory_alloced = 0; @@ -674,7 +675,7 @@ int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size mem_zero(&sa, sizeof(sa)); netaddr_to_sockaddr(addr, &sa); d = sendto((int)sock, (const char*)data, size, 0, &sa, sizeof(sa)); - if(d < 0) + /*if(d < 0) { char addrstr[256]; net_addr_str(addr, addrstr, sizeof(addrstr)); @@ -684,7 +685,7 @@ int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size dbg_msg("net", "\tsize = %d %x", size, size); dbg_msg("net", "\taddr = %s", addrstr); - } + }*/ network_stats.sent_bytes += size; network_stats.sent_packets++; return d; @@ -1102,6 +1103,10 @@ int str_comp(const char *a, const char *b) return strcmp(a, b); } +int str_comp_num(const char *a, const char *b, const int num) +{ + return strncmp(a, b, num); +} const char *str_find_nocase(const char *haystack, const char *needle) { @@ -1220,6 +1225,9 @@ char str_uppercase(char c) return c; } +int str_toint(const char *str) { return atoi(str); } +float str_tofloat(const char *str) { return atof(str); } + static int str_utf8_isstart(char c) diff --git a/src/base/system.h b/src/base/system.h index 3c9974d8..0850a08b 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -46,14 +46,15 @@ void dbg_break(); /* Function: dbg_msg - Prints a debug message. + + Prints a debug message. Parameters: sys - A string that describes what system the message belongs to fmt - A printf styled format string. Remarks: - Does nothing in relase version of the library. + Does nothing in release version of the library. See Also: <dbg_assert> @@ -351,7 +352,7 @@ void *thread_create(void (*threadfunc)(void *), void *user); void thread_wait(void *thread); /* - Function: thread_destoy + Function: thread_destroy Destroys a thread. Parameters: @@ -782,6 +783,25 @@ int str_comp_nocase(const char *a, const char *b); int str_comp(const char *a, const char *b); /* + Function: str_comp_nocase + Compares up to num characters of two strings case sensitive. + + Parameters: + a - String to compare. + b - String to compare. + num - Maximum characters to compare + + Returns: + <0 - String a is lesser then string b + 0 - String a is equal to string b + >0 - String a is greater then string b + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +int str_comp_num(const char *a, const char *b, const int num); + +/* Function: str_find_nocase Finds a string inside another string case insensitive. @@ -942,8 +962,29 @@ void mem_debug_dump(); void swap_endian(void *data, unsigned elem_size, unsigned num); -typedef void (*DBG_LOGGER)(const char *line); +/* Group: Debug levels */ +//by format +enum { + DBG_FMT_RAW = 1, //raw output + DBG_FMT_TIME = 2, //show time + DBG_FMT_SYS = 3, //show sys + DBG_FMT_FULL = 4 //show both +}; + +enum { + DBG_LEVEL_IMPORTANT = 0, //important always showed messages + DBG_LEVEL_ERROR = 1, //error messages + DBG_LEVEL_WARNING = 2, //warning messages + DBG_LEVEL_MSG = 3, //extra debug messages + DBG_LEVEL_INFO = 4 //info messages +}; + +#define DBG_LEVEL_LOW DBG_LEVEL_IMPORTANT +#define DBG_LEVEL_HIGH DBG_LEVEL_INFO + +typedef void (*DBG_LOGGER)(const char *line); void dbg_logger(DBG_LOGGER logger); + void dbg_logger_stdout(); void dbg_logger_debugger(); void dbg_logger_file(const char *filename); @@ -968,6 +1009,8 @@ typedef struct void net_stats(NETSTATS *stats); +int str_toint(const char *str); +float str_tofloat(const char *str); int str_isspace(char c); char str_uppercase(char c); unsigned str_quickhash(const char *str); diff --git a/src/base/tl/algorithm.hpp b/src/base/tl/algorithm.h index 9d78810b..32c2da73 100644 --- a/src/base/tl/algorithm.hpp +++ b/src/base/tl/algorithm.h @@ -1,7 +1,7 @@ #ifndef TL_FILE_ALGORITHM_HPP #define TL_FILE_ALGORITHM_HPP -#include "range.hpp" +#include "range.h" /* @@ -59,7 +59,7 @@ R find_linear(R range, T value) concept_empty::check(range); concept_forwarditeration::check(range); for(; !range.empty(); range.pop_front()) - if(value < range.front()) + if(value == range.front()) break; return range; } diff --git a/src/base/tl/allocator.hpp b/src/base/tl/allocator.h index 3baa1c19..3baa1c19 100644 --- a/src/base/tl/allocator.hpp +++ b/src/base/tl/allocator.h diff --git a/src/base/tl/array.hpp b/src/base/tl/array.h index 7b8698d4..580f4682 100644 --- a/src/base/tl/array.hpp +++ b/src/base/tl/array.h @@ -1,8 +1,8 @@ #ifndef TL_FILE_ARRAY_HPP #define TL_FILE_ARRAY_HPP -#include "range.hpp" -#include "allocator.hpp" +#include "range.h" +#include "allocator.h" /* @@ -165,7 +165,7 @@ public: int add(const T& item) { incsize(); - num_elements = size()+1; + set_size(size()+1); list[num_elements-1] = item; return num_elements-1; } @@ -189,7 +189,7 @@ public: int index = (int)(&r.front()-list); incsize(); - num_elements = size()+1; + set_size(size()+1); for(int i = num_elements-1; i > index; i--) list[i] = list[i-1]; @@ -283,7 +283,7 @@ public: */ int memusage() { - return sizeof(*this) + sizeof(T)*list_size; + return sizeof(array) + sizeof(T)*size; } /* diff --git a/src/base/tl/base.hpp b/src/base/tl/base.h index c202de79..c202de79 100644 --- a/src/base/tl/base.hpp +++ b/src/base/tl/base.h diff --git a/src/base/tl/math.hpp b/src/base/tl/math.hpp deleted file mode 100644 index b323dea8..00000000 --- a/src/base/tl/math.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef BASE_MATH_H -#define BASE_MATH_H - -#include <stdlib.h> - -template <typename T> -inline T clamp(T val, T min, T max) -{ - if(val < min) - return min; - if(val > max) - return max; - return val; -} - -inline float sign(float f) -{ - return f<0.0f?-1.0f:1.0f; -} - -inline int round(float f) -{ - if(f > 0) - return (int)(f+0.5f); - return (int)(f-0.5f); -} - -template<typename T, typename TB> -inline T mix(const T a, const T b, TB amount) -{ - return a + (b-a)*amount; -} - -inline float frandom() { return rand()/(float)(RAND_MAX); } - -const float pi = 3.1415926535897932384626433f; - -template <typename T> inline T min(T a, T b) { return a<b?a:b; } -template <typename T> inline T max(T a, T b) { return a>b?a:b; } - -template <typename T> inline T min(T a, T b, T c) { return min(min(a,b),c); } -template <typename T> inline T max(T a, T b, T c) { return max(max(a,b),c); } - -#endif // BASE_MATH_H diff --git a/src/base/tl/matrix.hpp b/src/base/tl/matrix.hpp deleted file mode 100644 index b723eaa7..00000000 --- a/src/base/tl/matrix.hpp +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef TL_FILE_MATRIX_HPP -#define TL_FILE_MATRIX_HPP - -/* - - Looks like OpenGL - - Column Major - - / m[0][0] m[1][0] m[2][0] m[3][0] \ / v[0] \ - | | | | - | m[0][1] m[1][1] m[2][1] m[3][1] | | v[1] | - | | | | - M(v) = | m[0][2] m[1][2] m[2][2] m[3][2] | X | v[2] | - | | | | - \ m[0][3] m[1][3] m[2][3] m[3][3] / \ v[3] / - - v[0] = x - v[1] = y - v[2] = z - v[3] = w or 1 - - +y - | - | - |_____ +x - / - / - +z - - right = +x - up = +y - forward = -z - -*/ - -template<class T> -class matrix4_base -{ -public: - // [col][row] - T m[4][4]; - - // - inline matrix4_base() - {} - - inline vector3_base<T> get_column3(const int i) const { return vector3_base<T>(m[i][0], m[i][1], m[i][2]); } - inline vector3_base<T> get_column4(const int i) const { return vector4_base<T>(m[i][0], m[i][1], m[i][2], m[i][3]); } - inline vector3_base<T> get_row3(const int i) const { return vector3_base<T>(m[0][i], m[1][i], m[2][i]); } - inline vector4_base<T> get_row4(const int i) const { return vector4_base<T>(m[0][i], m[1][i], m[2][i], m[3][i]); } - - inline vector3_base<T> get_right() const { return get_row3(0); } - inline vector3_base<T> get_up() const { return get_row3(1); } - inline vector3_base<T> get_forward() const { return -get_row3(2); } - inline vector3_base<T> get_translation() const { return get_column3(3); } - - // - void unit() - { - m[0][1] = m[0][2] = m[0][3] = 0; - m[1][0] = m[1][2] = m[1][3] = 0; - m[2][0] = m[2][1] = m[2][3] = 0; - m[3][0] = m[3][1] = m[3][2] = 0; - - m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1; - } - - // - vector3_base<T> operator *(const vector3_base<T> &v) const - { - vector3_base<T> r(0,0,0); - - r.x += v.x*m[0][0] + v.y*m[1][0] + v.z*m[2][0] + m[3][0]; - r.y += v.x*m[0][1] + v.y*m[1][1] + v.z*m[2][1] + m[3][1]; - r.z += v.x*m[0][2] + v.y*m[1][2] + v.z*m[2][2] + m[3][2]; - return r; - } - - // - vector4_base<T> operator *(const vector4_base<T> &v) const - { - vector4_base<T> r(0,0,0,0); - - r.x += v.x*m[0][0] + v.y*m[1][0] + v.z*m[2][0] + v.w*m[3][0]; - r.y += v.x*m[0][1] + v.y*m[1][1] + v.z*m[2][1] + v.w*m[3][1]; - r.z += v.x*m[0][2] + v.y*m[1][2] + v.z*m[2][2] + v.w*m[3][2]; - r.w += v.x*m[0][3] + v.y*m[1][3] + v.z*m[2][3] + v.w*m[3][3]; - return r; - } - // - matrix4_base operator *(const matrix4_base &other) const - { - matrix4_base r; - - for(int i = 0; i < 4; i++) - for(int j = 0; j < 4; j++) - { - r.m[i][j] = 0; - for(int a = 0; a < 4; a++) - r.m[i][j] += m[a][j] * other.m[i][a]; - } - return r; - } - - - // - // THIS PART IS KINDA UGLY BECAUSE MAT4 IS NOT IMMUTABLE - // - - inline void set_row(const vector3_base<T>& v, const int row) - { - m[0][row] = v.x; - m[1][row] = v.y; - m[2][row] = v.z; - } - - inline void set_column(const vector3_base<T>& v, const int col) - { - m[col][0] = v.x; - m[col][1] = v.y; - m[col][2] = v.z; - } - - inline void set_translation(const vector3_base<T>& v) { set_column(v,3); } - - // - void rot_x(T angle) - { - T sina = (T)sin(angle); - T cosa = (T)cos(angle); - - unit(); - m[1][1] = cosa; m[2][1] =-sina; - m[1][2] = sina; m[2][2] = cosa; - } - - // - void rot_y(T angle) - { - T sina = (T)sin(-angle); - T cosa = (T)cos(-angle); - - unit(); - m[0][0] = cosa; m[2][0] =-sina; - m[0][2] = sina; m[2][2] = cosa; - } - - // - void rot_z(T angle) - { - T sina = (T)sin(angle); - T cosa = (T)cos(angle); - - unit(); - m[0][0] = cosa; m[1][0] =-sina; - m[0][1] = sina; m[1][1] = cosa; - } -}; - -typedef matrix4_base<float> mat4; - -#endif diff --git a/src/base/tl/quat.hpp b/src/base/tl/quat.hpp deleted file mode 100644 index 2f381e11..00000000 --- a/src/base/tl/quat.hpp +++ /dev/null @@ -1,211 +0,0 @@ - - -template<class T> -class quaternion_base -{ -public: - T k[4]; - - /*void Unit() - { - k[0] = 0; - k[1] = 0; - k[2] = 0; - k[3] = 1; - }*/ - - inline quaternion_base(){} - - inline quaternion_base(T x, T y, T z, T w) - { - k[0] = x; - k[1] = y; - k[2] = z; - k[3] = w; - } - - inline quaternion_base(vector3_base<T> axis, T angle) - { - T sin_angle = sin(angle * (T)0.5); - T cos_angle = cos(angle * (T)0.5); - k[0] = axis.x * sin_angle; - k[1] = axis.y * sin_angle; - k[2] = axis.z * sin_angle; - k[3] = cos_angle; - } - - - T magnitude() - { - return sqrt(k[0] * k[0] + k[1] * k[1] + k[2] * k[2] + k[3] * k[3]); - } - - matrix4_base<T> create_matrix() const - { - matrix4_base<T> mat; - - T xx = k[0] * k[0]; - T xy = k[0] * k[1]; - T xz = k[0] * k[2]; - T xw = k[0] * k[3]; - T yy = k[1] * k[1]; - T yz = k[1] * k[2]; - T yw = k[1] * k[3]; - T zz = k[2] * k[2]; - T zw = k[2] * k[3]; - - mat.k[0][0] = 1 - 2 * (yy + zz); - mat.k[0][1] = 2 * (xy + zw); - mat.k[0][2] = 2 * (xz - yw); - mat.k[0][3] = 0; - - mat.k[1][0] = 2 * (xy - zw); - mat.k[1][1] = 1 - 2 * (xx + zz); - mat.k[1][2] = 2 * (yz + xw); - mat.k[1][3] = 0; - - mat.k[2][0] = 2 * (xz + yw); - mat.k[2][1] = 2 * (yz - xw); - mat.k[2][2] = 1 - 2 * (xx + yy); - mat.k[2][3] = 0; - - mat.k[3][0] = 0; - mat.k[3][1] = 0; - mat.k[3][2] = 0; - mat.k[3][3] = 1; - } - - /* - void CreateDOOM(T x, T y, T z) - { - k[0] = x; - k[1] = y; - k[2] = z; - T Term = 1 - (x * x) - (y * y) - (z * z); - if (Term < 0) - k[3] = 0; - else - k[3] = -sqrt(Term); - - Normalize(); - } - - T DotProd(const TQuaternion<T>& Quat) const - { - return (k[0] * Quat.k[0] + k[1] * Quat.k[1] + k[2] * Quat.k[2] + k[3] * Quat.k[3]); - } - - void Interpolate(const TQuaternion<T>& Quat, TQuaternion& Dest, T Scale) - { - T Separation = k[0] * Quat.k[0] + k[1] * Quat.k[1] + k[2] * Quat.k[2] + k[3] * Quat.k[3]; - T Factor1,Factor2; - - if (Separation > 1) - Separation = 1; - if (Separation < -1) - Separation = -1; - Separation = acos(Separation); - if (Separation == 0 || Separation == pi) - { - Factor1 = 1; - Factor2 = 0; - } - else - { - Factor1 = sin((1 - Scale)*Separation) / sin(Separation); - Factor2 = sin(Scale * Separation) / sin(Separation); - } - - Dest.k[0] = k[0] * Factor1 + Quat.k[0] * Factor2; - Dest.k[1] = k[1] * Factor1 + Quat.k[1] * Factor2; - Dest.k[2] = k[2] * Factor1 + Quat.k[2] * Factor2; - Dest.k[3] = k[3] * Factor1 + Quat.k[3] * Factor2; - } - - void Slerp(const TQuaternion<T>& Quat, TQuaternion& Dest, T Scale) const - { - T Sq1,Sq2; - T Dot = DotProd(Quat); - - TQuaternion Temp; - - if (Dot < 0.0f) - { - Dot = -Dot; - Temp.k[0] = -Quat.k[0]; - Temp.k[1] = -Quat.k[1]; - Temp.k[2] = -Quat.k[2]; - Temp.k[3] = -Quat.k[3]; - } - else - { - Temp = Quat; - } - - if ((1.0 + Dot) > 0.00001) - { - if ((1.0 - Dot) > 0.00001) - { - T om = (T)acos(Dot); - T rsinom = (T)(1.0f / sin(om)); - Sq1 = (T)sin(((T)1.0 - Scale) * om) * rsinom; - Sq2 = (T)sin(Scale * om) * rsinom; - } - else - { - Sq1 = (T)(1.0 - Scale); - Sq2 = Scale; - } - Dest.k[0] = Sq1 * k[0] + Sq2 * Temp[0]; - Dest.k[1] = Sq1 * k[1] + Sq2 * Temp[1]; - Dest.k[2] = Sq1 * k[2] + Sq2 * Temp[2]; - Dest.k[3] = Sq1 * k[3] + Sq2 * Temp[3]; - } - else - { - Sq1 = (T)sin(((T)1.0 - Scale) * (T)0.5 * pi); - Sq2 = (T)sin(Scale * (T)0.5 * pi); - - Dest.k[0] = Sq1 * k[0] + Sq2 * Temp[1]; - Dest.k[1] = Sq1 * k[1] + Sq2 * Temp[0]; - Dest.k[2] = Sq1 * k[2] + Sq2 * Temp[3]; - Dest.k[3] = Sq1 * k[3] + Sq2 * Temp[2]; - } - }*/ - - // perators - T& operator [] (int i) { return k[i]; } - - // quaternion multiply - quaternion_base operator *(const quaternion_base& other) const - { - // (w1 dot w2 - v1 dot v2, w1 dot v2 + w2 dot v1 + v1 cross v2) - quaternion_base r; - r.k[0] = k[3] * other.k[0] + k[0] * other.k[3] + k[1] * other.k[2] - k[2] * other.k[1]; - r.k[1] = k[3] * other.k[1] + k[1] * other.k[3] + k[2] * other.k[0] - k[0] * other.k[2]; - r.k[2] = k[3] * other.k[2] + k[2] * other.k[3] + k[0] * other.k[1] - k[1] * other.k[0]; - r.k[3] = k[3] * other.k[3] - k[0] * other.k[0] - k[1] * other.k[1] - k[2] * other.k[2]; - - return normalize(r); - } - - /* - bool operator == (const quaternion_base<T>& t) const { return ((k[0] == t.k[0]) && (k[1] == t.k[1]) && (k[2] == t.k[2]) && (k[3] == t.k[3])); } - bool operator != (const quaternion_base<T>& t) const { return ((k[0] != t.k[0]) || (k[1] != t.k[1]) || (k[2] != t.k[2]) || (k[3] != t.k[3])); } - - void operator = (const quaternion_base<T>& other) { k[0] = other.k[0]; k[1] = other.k[1]; k[2] = other.k[2]; k[3] = other.k[3]; } - */ - - /*void operator *= (const TQuaternion<T>& t) { TQuaternion Temp = Multiply(t); *this = Temp; } - TQuaternion operator * (const TQuaternion<T>& t) const { return Multiply(t); } - */ -}; - -template<typename T> -inline quaternion_base<T> normalize(const quaternion_base<T> &v) -{ - T factor = 1.0f/v.magnitude(); - return quaternion_base<T>(v.k[0]*factor, v.k[1]*factor,v. k[2]*factor,v. k[3]*factor); -} - - diff --git a/src/base/tl/range.hpp b/src/base/tl/range.h index 559daffa..1c63e73c 100644 --- a/src/base/tl/range.hpp +++ b/src/base/tl/range.h @@ -1,7 +1,7 @@ #ifndef TL_FILE_RANGE_HPP #define TL_FILE_RANGE_HPP -#include "base.hpp" +#include "base.h" /* Group: Range concepts diff --git a/src/base/tl/sorted_array.hpp b/src/base/tl/sorted_array.h index 30c1df24..95f06157 100644 --- a/src/base/tl/sorted_array.hpp +++ b/src/base/tl/sorted_array.h @@ -1,8 +1,8 @@ #ifndef TL_FILE_SORTED_ARRAY_HPP #define TL_FILE_SORTED_ARRAY_HPP -#include "algorithm.hpp" -#include "array.hpp" +#include "algorithm.h" +#include "array.h" template <class T, class ALLOCATOR = allocator_default<T> > class sorted_array : public array<T, ALLOCATOR> diff --git a/src/base/tl/stream.hpp b/src/base/tl/stream.hpp deleted file mode 100644 index c307b968..00000000 --- a/src/base/tl/stream.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef TL_FILE_STREAM_HPP -#define TL_FILE_STREAM_HPP - -class input_stream -{ -public: - virtual ~input_stream() {} - virtual size_t read(void *data, size_t size) = 0; - virtual size_t size() = 0; -}; - -class output_stream -{ -public: - virtual ~output_stream() {} - virtual size_t write(const void *data, size_t size) = 0; -}; - - -// input wrapping -// RAII style -class file_backend -{ -private: - file_backend(const file_backend &other) { /* no copy allowed */ } -protected: - IOHANDLE file_handle; - - explicit file_backend(const char *filename, int flags) - { - file_handle = io_open(filename, flags); - } - - ~file_backend() - { - if(file_handle) - io_close(file_handle); - } -public: - bool is_open() const { return file_handle != 0; } -}; - -class file_reader : public input_stream, public file_backend -{ -public: - explicit file_reader(const char *filename) - : file_backend(filename, IOFLAG_READ) - {} - - virtual size_t read(void *data, size_t size) { return io_read(file_handle, data, size); } - virtual size_t size() { return io_length(file_handle); } -}; - - -class file_writer : public output_stream, public file_backend -{ -public: - explicit file_writer(const char *filename) - : file_backend(filename, IOFLAG_WRITE) - {} - - virtual size_t write(const void *data, size_t size) { return io_write(file_handle, data, size); } -}; - -#endif diff --git a/src/base/tl/string.hpp b/src/base/tl/string.h index 2b164091..155ca2a4 100644 --- a/src/base/tl/string.hpp +++ b/src/base/tl/string.h @@ -1,8 +1,8 @@ #ifndef TL_FILE_STRING_HPP #define TL_FILE_STRING_HPP -#include "base.hpp" -#include "allocator.hpp" +#include "base.h" +#include "allocator.h" template<class ALLOCATOR > class string_base : private ALLOCATOR diff --git a/src/base/tl/vector.hpp b/src/base/tl/vector.hpp deleted file mode 100644 index 1c1bb804..00000000 --- a/src/base/tl/vector.hpp +++ /dev/null @@ -1,198 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef TL_FILE_VECTOR_HPP -#define TL_FILE_VECTOR_HPP - -#include <math.h> - -// ------------------------------------ - -template<typename T> -class vector2_base -{ -public: - union { T x,u; }; - union { T y,v; }; - - vector2_base() {} - vector2_base(float nx, float ny) - { - x = nx; - y = ny; - } - - vector2_base operator -() const { return vector2_base(-x, -y); } - vector2_base operator -(const vector2_base &v) const { return vector2_base(x-v.x, y-v.y); } - vector2_base operator +(const vector2_base &v) const { return vector2_base(x+v.x, y+v.y); } - vector2_base operator *(const T v) const { return vector2_base(x*v, y*v); } - - const vector2_base &operator =(const vector2_base &v) { x = v.x; y = v.y; return *this; } - - const vector2_base &operator +=(const vector2_base &v) { x += v.x; y += v.y; return *this; } - const vector2_base &operator -=(const vector2_base &v) { x -= v.x; y -= v.y; return *this; } - const vector2_base &operator *=(const T v) { x *= v; y *= v; return *this; } - - bool operator ==(const vector2_base &v) const { return x == v.x && y == v.y; } //TODO: do this with an eps instead - - operator const T* () { return &x; } -}; - - -template<typename T> -inline T length(const vector2_base<T> &a) -{ - return sqrtf(a.x*a.x + a.y*a.y); -} - -template<typename T> -inline T distance(const vector2_base<T> a, const vector2_base<T> &b) -{ - return length(a-b); -} - -template<typename T> -inline T dot(const vector2_base<T> a, const vector2_base<T> &b) -{ - return a.x*b.x + a.y*b.y; -} - -template<typename T> -inline vector2_base<T> normalize(const vector2_base<T> &v) -{ - T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y)); - return vector2_base<T>(v.x*l, v.y*l); -} - -typedef vector2_base<float> vec2; -typedef vector2_base<bool> bvec2; -typedef vector2_base<int> ivec2; - -template<typename T> -inline vector2_base<T> closest_point_on_line(vector2_base<T> line_point0, vector2_base<T> line_point1, vector2_base<T> target_point) -{ - vector2_base<T> c = target_point - line_point0; - vector2_base<T> v = (line_point1 - line_point0); - v = normalize(v); - T d = length(line_point0-line_point1); - T t = dot(v, c)/d; - return mix(line_point0, line_point1, clamp(t, (T)0, (T)1)); - /* - if (t < 0) t = 0; - if (t > 1.0f) return 1.0f; - return t;*/ -} - -// ------------------------------------ -template<typename T> -class vector3_base -{ -public: - union { T x,r,h; }; - union { T y,g,s; }; - union { T z,b,v,l; }; - - vector3_base() {} - vector3_base(float nx, float ny, float nz) - { - x = nx; - y = ny; - z = nz; - } - - const vector3_base &operator =(const vector3_base &v) { x = v.x; y = v.y; z = v.z; return *this; } - - vector3_base operator -(const vector3_base &v) const { return vector3_base(x-v.x, y-v.y, z-v.z); } - vector3_base operator -() const { return vector3_base(-x, -y, -z); } - vector3_base operator +(const vector3_base &v) const { return vector3_base(x+v.x, y+v.y, z+v.z); } - vector3_base operator *(const T v) const { return vector3_base(x*v, y*v, z*v); } - vector3_base operator *(const vector3_base &v) const { return vector3_base(x*v.x, y*v.y, z*v.z); } - vector3_base operator /(const T v) const { return vector3_base(x/v, y/v, z/v); } - - const vector3_base &operator +=(const vector3_base &v) { x += v.x; y += v.y; z += v.z; return *this; } - const vector3_base &operator -=(const vector3_base &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } - const vector3_base &operator *=(const T v) { x *= v; y *= v; z *= v; return *this; } - - bool operator ==(const vector3_base &v) const { return x == v.x && y == v.y && z == v.z; } //TODO: do this with an eps instead - - operator const T* () { return &x; } -}; - -template<typename T> -inline T length(const vector3_base<T> &a) -{ - return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z); -} - -template<typename T> -inline T distance(const vector3_base<T> &a, const vector3_base<T> &b) -{ - return length(a-b); -} - -template<typename T> -inline T dot(const vector3_base<T> &a, const vector3_base<T> &b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z; -} - -template<typename T> -inline vector3_base<T> normalize(const vector3_base<T> &v) -{ - T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y + v.z*v.z)); - return vector3_base<T>(v.x*l, v.y*l, v.z*l); -} - -template<typename T> -inline vector3_base<T> cross(const vector3_base<T> &a, const vector3_base<T> &b) -{ - return vector3_base<T>( - a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x); -} - -typedef vector3_base<float> vec3; -typedef vector3_base<bool> bvec3; -typedef vector3_base<int> ivec3; - -// ------------------------------------ - -template<typename T> -class vector4_base -{ -public: - union { T x,r; }; - union { T y,g; }; - union { T z,b; }; - union { T w,a; }; - - vector4_base() {} - vector4_base(float nx, float ny, float nz, float nw) - { - x = nx; - y = ny; - z = nz; - w = nw; - } - - vector4_base operator +(const vector4_base &v) const { return vector4_base(x+v.x, y+v.y, z+v.z, w+v.w); } - vector4_base operator -(const vector4_base &v) const { return vector4_base(x-v.x, y-v.y, z-v.z, w-v.w); } - vector4_base operator -() const { return vector4_base(-x, -y, -z, -w); } - vector4_base operator *(const vector4_base &v) const { return vector4_base(x*v.x, y*v.y, z*v.z, w*v.w); } - vector4_base operator *(const T v) const { return vector4_base(x*v, y*v, z*v, w*v); } - - const vector4_base &operator =(const vector4_base &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this; } - - const vector4_base &operator +=(const vector4_base &v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } - const vector4_base &operator -=(const vector4_base &v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } - const vector4_base &operator *=(const T v) { x *= v; y *= v; z *= v; w *= v; return *this; } - - bool operator ==(const vector4_base &v) const { return x == v.x && y == v.y && z == v.z && w == v.w; } //TODO: do this with an eps instead - - operator const T* () { return &x; } -}; - -typedef vector4_base<float> vec4; -typedef vector4_base<bool> bvec4; -typedef vector4_base<int> ivec4; - -#endif diff --git a/src/base/vmath.hpp b/src/base/vmath.h index 49dd26d1..49dd26d1 100644 --- a/src/base/vmath.hpp +++ b/src/base/vmath.h diff --git a/src/engine/client.h b/src/engine/client.h new file mode 100644 index 00000000..8e5a5577 --- /dev/null +++ b/src/engine/client.h @@ -0,0 +1,159 @@ +#ifndef ENGINE_CLIENT_H +#define ENGINE_CLIENT_H +#include "kernel.h" + +#include "message.h" + +class IClient : public IInterface +{ + MACRO_INTERFACE("client", 0) +protected: + // quick access to state of the client + int m_State; + + // quick access to time variables + int m_PrevGameTick; + int m_CurGameTick; + float m_GameIntraTick; + float m_GameTickTime; + + int m_PredTick; + float m_PredIntraTick; + + float m_LocalTime; + float m_FrameTime; + + int m_GameTickSpeed; +public: + + class CSnapItem + { + public: + int m_Type; + int m_Id; + int m_DataSize; + }; + + /* Constants: Client States + STATE_OFFLINE - The client is offline. + STATE_CONNECTING - The client is trying to connect to a server. + STATE_LOADING - The client has connected to a server and is loading resources. + STATE_ONLINE - The client is connected to a server and running the game. + STATE_DEMOPLAYBACK - The client is playing a demo + STATE_QUITING - The client is quiting. + */ + + enum + { + STATE_OFFLINE=0, + STATE_CONNECTING, + STATE_LOADING, + STATE_ONLINE, + STATE_DEMOPLAYBACK, + STATE_QUITING, + }; + + // + inline int State() const { return m_State; } + + // tick time access + inline int PrevGameTick() const { return m_PrevGameTick; } + inline int GameTick() const { return m_CurGameTick; } + inline int PredGameTick() const { return m_PredTick; } + inline float IntraGameTick() const { return m_GameIntraTick; } + inline float PredIntraGameTick() const { return m_PredIntraTick; } + inline float GameTickTime() const { return m_GameTickTime; } + inline int GameTickSpeed() const { return m_GameTickSpeed; } + + // other time access + inline float FrameTime() const { return m_FrameTime; } + inline float LocalTime() const { return m_LocalTime; } + + // actions + virtual void Connect(const char *pAddress) = 0; + virtual void Disconnect() = 0; + virtual void Quit() = 0; + virtual const char *DemoPlayer_Play(const char *pFilename) = 0; + + // networking + virtual void EnterGame() = 0; + + // + virtual int MapDownloadAmount() = 0; + virtual int MapDownloadTotalsize() = 0; + + // input + virtual int *GetInput(int Tick) = 0; + + // remote console + virtual void RconAuth(const char *pUsername, const char *pPassword) = 0; + virtual bool RconAuthed() = 0; + virtual void Rcon(const char *pLine) = 0; + + // server info + virtual void GetServerInfo(class CServerInfo *pServerInfo) = 0; + + // snapshot interface + + enum + { + SNAP_CURRENT=0, + SNAP_PREV=1 + }; + + // TODO: Refactor: should redo this a bit i think, too many virtual calls + virtual int SnapNumItems(int SnapId) = 0; + virtual void *SnapFindItem(int SnapId, int Type, int Id) = 0; + virtual void *SnapGetItem(int SnapId, int Index, CSnapItem *pItem) = 0; + virtual void SnapInvalidateItem(int SnapId, int Index) = 0; + + virtual void SnapSetStaticsize(int ItemType, int Size) = 0; + + virtual int SendMsg(CMsgPacker *pMsg, int Flags) = 0; + + template<class T> + int SendPackMsg(T *pMsg, int Flags) + { + CMsgPacker Packer(pMsg->MsgID()); + if(pMsg->Pack(&Packer)) + return -1; + return SendMsg(&Packer, Flags); + } + + // + virtual const char *UserDirectory() = 0; + + // + virtual const char *ErrorString() = 0; + virtual const char *LatestVersion() = 0; + virtual bool ConnectionProblems() = 0; +}; + +class IGameClient : public IInterface +{ + MACRO_INTERFACE("gameclient", 0) +protected: +public: + virtual void OnConsoleInit() = 0; + + virtual void OnRconLine(const char *pLine) = 0; + virtual void OnInit() = 0; + virtual void OnNewSnapshot() = 0; + virtual void OnEnterGame() = 0; + virtual void OnShutdown() = 0; + virtual void OnRender() = 0; + virtual void OnStateChange(int NewState, int OldState) = 0; + virtual void OnConnected() = 0; + virtual void OnMessage(int MsgId, CUnpacker *pUnpacker) = 0; + virtual void OnPredict() = 0; + + virtual int OnSnapInput(int *pData) = 0; + + virtual const char *GetItemName(int Type) = 0; + virtual const char *Version() = 0; + virtual const char *NetVersion() = 0; + +}; + +extern IGameClient *CreateGameClient(); +#endif diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp new file mode 100644 index 00000000..dbcaa1ed --- /dev/null +++ b/src/engine/client/client.cpp @@ -0,0 +1,2062 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info + +#include <stdlib.h> // qsort +#include <stdarg.h> +#include <math.h> + +#include <base/system.h> +#include <engine/shared/engine.h> + +#include <engine/shared/protocol.h> +#include <engine/shared/snapshot.h> +#include <engine/shared/compression.h> +#include <engine/shared/network.h> +#include <engine/shared/config.h> +#include <engine/shared/packer.h> +#include <engine/shared/memheap.h> +#include <engine/shared/datafile.h> +#include <engine/shared/ringbuffer.h> +#include <engine/shared/protocol.h> + +#include <engine/shared/demorec.h> + +#include <mastersrv/mastersrv.h> +#include <versionsrv/versionsrv.h> + +#include "client.h" + + +void CGraph::Init(float Min, float Max) +{ + m_Min = Min; + m_Max = Max; + m_Index = 0; +} + +void CGraph::ScaleMax() +{ + int i = 0; + m_Max = 0; + for(i = 0; i < MAX_VALUES; i++) + { + if(m_aValues[i] > m_Max) + m_Max = m_aValues[i]; + } +} + +void CGraph::ScaleMin() +{ + int i = 0; + m_Min = m_Max; + for(i = 0; i < MAX_VALUES; i++) + { + if(m_aValues[i] < m_Min) + m_Min = m_aValues[i]; + } +} + +void CGraph::Add(float v, float r, float g, float b) +{ + m_Index = (m_Index+1)&(MAX_VALUES-1); + m_aValues[m_Index] = v; + m_aColors[m_Index][0] = r; + m_aColors[m_Index][1] = g; + m_aColors[m_Index][2] = b; +} + +void CGraph::Render(IGraphics *pGraphics, int Font, float x, float y, float w, float h, const char *pDescription) +{ + //m_pGraphics->BlendNormal(); + + + pGraphics->TextureSet(-1); + + pGraphics->QuadsBegin(); + pGraphics->SetColor(0, 0, 0, 0.75f); + IGraphics::CQuadItem QuadItem(x, y, w, h); + pGraphics->QuadsDrawTL(&QuadItem, 1); + pGraphics->QuadsEnd(); + + pGraphics->LinesBegin(); + pGraphics->SetColor(0.95f, 0.95f, 0.95f, 1.00f); + IGraphics::CLineItem LineItem(x, y+h/2, x+w, y+h/2); + pGraphics->LinesDraw(&LineItem, 1); + pGraphics->SetColor(0.5f, 0.5f, 0.5f, 0.75f); + IGraphics::CLineItem Array[2] = { + IGraphics::CLineItem(x, y+(h*3)/4, x+w, y+(h*3)/4), + IGraphics::CLineItem(x, y+h/4, x+w, y+h/4)}; + pGraphics->LinesDraw(Array, 2); + for(int i = 1; i < MAX_VALUES; i++) + { + float a0 = (i-1)/(float)MAX_VALUES; + float a1 = i/(float)MAX_VALUES; + int i0 = (m_Index+i-1)&(MAX_VALUES-1); + int i1 = (m_Index+i)&(MAX_VALUES-1); + + float v0 = (m_aValues[i0]-m_Min) / (m_Max-m_Min); + float v1 = (m_aValues[i1]-m_Min) / (m_Max-m_Min); + + IGraphics::CColorVertex Array[2] = { + IGraphics::CColorVertex(0, m_aColors[i0][0], m_aColors[i0][1], m_aColors[i0][2], 0.75f), + IGraphics::CColorVertex(1, m_aColors[i1][0], m_aColors[i1][1], m_aColors[i1][2], 0.75f)}; + pGraphics->SetColorVertex(Array, 2); + IGraphics::CLineItem LineItem(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); + pGraphics->LinesDraw(&LineItem, 1); + + } + pGraphics->LinesEnd(); + + pGraphics->TextureSet(Font); + pGraphics->QuadsText(x+2, y+h-16, 16, 1,1,1,1, pDescription); + + char aBuf[32]; + str_format(aBuf, sizeof(aBuf), "%.2f", m_Max); + pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+2, 16, 1,1,1,1, aBuf); + + str_format(aBuf, sizeof(aBuf), "%.2f", m_Min); + pGraphics->QuadsText(x+w-8*str_length(aBuf)-8, y+h-16, 16, 1,1,1,1, aBuf); +} + + +void CSmoothTime::Init(int64 Target) +{ + m_Snap = time_get(); + m_Current = Target; + m_Target = Target; + m_aAdjustSpeed[0] = 0.3f; + m_aAdjustSpeed[1] = 0.3f; + m_Graph.Init(0.0f, 0.5f); +} + +void CSmoothTime::SetAdjustSpeed(int Direction, float Value) +{ + m_aAdjustSpeed[Direction] = Value; +} + +int64 CSmoothTime::Get(int64 Now) +{ + int64 c = m_Current + (Now - m_Snap); + int64 t = m_Target + (Now - m_Snap); + + // it's faster to adjust upward instead of downward + // we might need to adjust these abit + + float AdjustSpeed = m_aAdjustSpeed[0]; + if(t > c) + AdjustSpeed = m_aAdjustSpeed[1]; + + float a = ((Now-m_Snap)/(float)time_freq()) * AdjustSpeed; + if(a > 1.0f) + a = 1.0f; + + int64 r = c + (int64)((t-c)*a); + + m_Graph.Add(a+0.5f,1,1,1); + + return r; +} + +void CSmoothTime::UpdateInt(int64 Target) +{ + int64 Now = time_get(); + m_Current = Get(Now); + m_Snap = Now; + m_Target = Target; +} + +void CSmoothTime::Update(CGraph *pGraph, int64 Target, int TimeLeft, int AdjustDirection) +{ + int UpdateTimer = 1; + + if(TimeLeft < 0) + { + int IsSpike = 0; + if(TimeLeft < -50) + { + IsSpike = 1; + + m_SpikeCounter += 5; + if(m_SpikeCounter > 50) + m_SpikeCounter = 50; + } + + if(IsSpike && m_SpikeCounter < 15) + { + // ignore this ping spike + UpdateTimer = 0; + pGraph->Add(TimeLeft, 1,1,0); + } + else + { + pGraph->Add(TimeLeft, 1,0,0); + if(m_aAdjustSpeed[AdjustDirection] < 30.0f) + m_aAdjustSpeed[AdjustDirection] *= 2.0f; + } + } + else + { + if(m_SpikeCounter) + m_SpikeCounter--; + + pGraph->Add(TimeLeft, 0,1,0); + + m_aAdjustSpeed[AdjustDirection] *= 0.95f; + if(m_aAdjustSpeed[AdjustDirection] < 2.0f) + m_aAdjustSpeed[AdjustDirection] = 2.0f; + } + + if(UpdateTimer) + UpdateInt(Target); +} + + +CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotDelta) +{ + m_pEditor = 0; + m_pInput = 0; + m_pGraphics = 0; + m_pSound = 0; + m_pGameClient = 0; + m_pMap = 0; + m_pConsole = 0; + + m_FrameTime = 0.0001f; + m_FrameTimeLow = 1.0f; + m_FrameTimeHigh = 0.0f; + m_Frames = 0; + + m_GameTickSpeed = SERVER_TICK_SPEED; + + m_WindowMustRefocus = 0; + m_SnapCrcErrors = 0; + + m_AckGameTick = -1; + m_CurrentRecvTick = 0; + m_RconAuthed = 0; + + // version-checking + m_aVersionStr[0] = '0'; + m_aVersionStr[1] = 0; + + // pinging + m_PingStartTime = 0; + + // + m_aCurrentMap[0] = 0; + m_CurrentMapCrc = 0; + + // + m_aCmdConnect[0] = 0; + + // map download + m_aMapdownloadFilename[0] = 0; + m_aMapdownloadName[0] = 0; + m_MapdownloadFile = 0; + m_MapdownloadChunk = 0; + m_MapdownloadCrc = 0; + m_MapdownloadAmount = -1; + m_MapdownloadTotalsize = -1; + + m_CurrentServerInfoRequestTime = -1; + + m_CurrentInput = 0; + + m_State = IClient::STATE_OFFLINE; + m_aServerAddressStr[0] = 0; + + mem_zero(m_aSnapshots, sizeof(m_aSnapshots)); + m_RecivedSnapshots = 0; + + m_VersionInfo.m_State = 0; +} + +// ----- send functions ----- +int CClient::SendMsg(CMsgPacker *pMsg, int Flags) +{ + return SendMsgEx(pMsg, Flags, false); +} + +int CClient::SendMsgEx(CMsgPacker *pMsg, int Flags, bool System) +{ + CNetChunk Packet; + + if(State() == IClient::STATE_OFFLINE) + return 0; + + mem_zero(&Packet, sizeof(CNetChunk)); + + Packet.m_ClientID = 0; + Packet.m_pData = pMsg->Data(); + Packet.m_DataSize = pMsg->Size(); + + // HACK: modify the message id in the packet and store the system flag + if(*((unsigned char*)Packet.m_pData) == 1 && System && Packet.m_DataSize == 1) + dbg_break(); + + *((unsigned char*)Packet.m_pData) <<= 1; + if(System) + *((unsigned char*)Packet.m_pData) |= 1; + + if(Flags&MSGFLAG_VITAL) + Packet.m_Flags |= NETSENDFLAG_VITAL; + if(Flags&MSGFLAG_FLUSH) + Packet.m_Flags |= NETSENDFLAG_FLUSH; + + if(Flags&MSGFLAG_RECORD) + { + if(m_DemoRecorder.IsRecording()) + m_DemoRecorder.RecordMessage(Packet.m_pData, Packet.m_DataSize); + } + + if(!(Flags&MSGFLAG_NOSEND)) + m_NetClient.Send(&Packet); + return 0; +} + +void CClient::SendInfo() +{ + CMsgPacker Msg(NETMSG_INFO); + Msg.AddString(GameClient()->NetVersion(), 128); + Msg.AddString(g_Config.m_PlayerName, 128); + Msg.AddString(g_Config.m_ClanName, 128); + Msg.AddString(g_Config.m_Password, 128); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); +} + + +void CClient::SendEnterGame() +{ + CMsgPacker Msg(NETMSG_ENTERGAME); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); +} + +void CClient::SendReady() +{ + CMsgPacker Msg(NETMSG_READY); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); +} + +bool CClient::RconAuthed() +{ + return m_RconAuthed; +} + +void CClient::RconAuth(const char *pName, const char *pPassword) +{ + if(RconAuthed()) + return; + + CMsgPacker Msg(NETMSG_RCON_AUTH); + Msg.AddString(pName, 32); + Msg.AddString(pPassword, 32); + SendMsgEx(&Msg, MSGFLAG_VITAL); +} + +void CClient::Rcon(const char *pCmd) +{ + CMsgPacker Msg(NETMSG_RCON_CMD); + Msg.AddString(pCmd, 256); + SendMsgEx(&Msg, MSGFLAG_VITAL); +} + +bool CClient::ConnectionProblems() +{ + return m_NetClient.GotProblems() != 0; +} + +void CClient::DirectInput(int *pInput, int Size) +{ + int i; + CMsgPacker Msg(NETMSG_INPUT); + Msg.AddInt(m_AckGameTick); + Msg.AddInt(m_PredTick); + Msg.AddInt(Size); + + for(i = 0; i < Size/4; i++) + Msg.AddInt(pInput[i]); + + SendMsgEx(&Msg, 0); +} + + +void CClient::SendInput() +{ + int64 Now = time_get(); + + if(m_PredTick <= 0) + return; + + // fetch input + int Size = GameClient()->OnSnapInput(m_aInputs[m_CurrentInput].m_aData); + + if(!Size) + return; + + // pack input + CMsgPacker Msg(NETMSG_INPUT); + Msg.AddInt(m_AckGameTick); + Msg.AddInt(m_PredTick); + Msg.AddInt(Size); + + m_aInputs[m_CurrentInput].m_Tick = m_PredTick; + m_aInputs[m_CurrentInput].m_PredictedTime = m_PredictedTime.Get(Now); + m_aInputs[m_CurrentInput].m_Time = Now; + + // pack it + for(int i = 0; i < Size/4; i++) + Msg.AddInt(m_aInputs[m_CurrentInput].m_aData[i]); + + m_CurrentInput++; + m_CurrentInput%=200; + + SendMsgEx(&Msg, MSGFLAG_FLUSH); +} + +const char *CClient::LatestVersion() +{ + return m_aVersionStr; +} + +// TODO: OPT: do this alot smarter! +int *CClient::GetInput(int Tick) +{ + int Best = -1; + for(int i = 0; i < 200; i++) + { + if(m_aInputs[i].m_Tick <= Tick && (Best == -1 || m_aInputs[Best].m_Tick < m_aInputs[i].m_Tick)) + Best = i; + } + + if(Best != -1) + return (int *)m_aInputs[Best].m_aData; + return 0; +} + +// ------ state handling ----- +void CClient::SetState(int s) +{ + int Old = m_State; + if(g_Config.m_Debug) + dbg_msg("client", "state change. last=%d current=%d", m_State, s); + m_State = s; + if(Old != s) + GameClient()->OnStateChange(m_State, Old); +} + +// called when the map is loaded and we should init for a new round +void CClient::OnEnterGame() +{ + // reset input + int i; + for(i = 0; i < 200; i++) + m_aInputs[i].m_Tick = -1; + m_CurrentInput = 0; + + // reset snapshots + m_aSnapshots[SNAP_CURRENT] = 0; + m_aSnapshots[SNAP_PREV] = 0; + m_SnapshotStorage.PurgeAll(); + m_RecivedSnapshots = 0; + m_SnapshotParts = 0; + m_PredTick = 0; + m_CurrentRecvTick = 0; + m_CurGameTick = 0; + m_PrevGameTick = 0; +} + +void CClient::EnterGame() +{ + if(State() == IClient::STATE_DEMOPLAYBACK) + return; + + // now we will wait for two snapshots + // to finish the connection + SendEnterGame(); + OnEnterGame(); +} + +void CClient::Connect(const char *pAddress) +{ + char aBuf[512]; + const char *pPortStr = 0; + int Port = 8303; + + Disconnect(); + + str_copy(m_aServerAddressStr, pAddress, sizeof(m_aServerAddressStr)); + + dbg_msg("client", "connecting to '%s'", m_aServerAddressStr); + + ServerInfoRequest(); + str_copy(aBuf, m_aServerAddressStr, sizeof(aBuf)); + + for(int k = 0; aBuf[k]; k++) + { + if(aBuf[k] == ':') + { + pPortStr = &(aBuf[k+1]); + aBuf[k] = 0; + break; + } + } + + if(pPortStr) + Port = str_toint(pPortStr); + + // TODO: IPv6 support + if(net_host_lookup(aBuf, &m_ServerAddress, NETTYPE_IPV4) != 0) + dbg_msg("client", "could not find the address of %s, connecting to localhost", aBuf); + + m_RconAuthed = 0; + m_ServerAddress.port = Port; + m_NetClient.Connect(&m_ServerAddress); + SetState(IClient::STATE_CONNECTING); + + if(m_DemoRecorder.IsRecording()) + m_DemoRecorder.Stop(); + + m_InputtimeMarginGraph.Init(-150.0f, 150.0f); + m_GametimeMarginGraph.Init(-150.0f, 150.0f); +} + +void CClient::DisconnectWithReason(const char *pReason) +{ + // stop demo playback and recorder + m_DemoPlayer.Stop(); + m_DemoRecorder.Stop(); + + // + m_RconAuthed = 0; + m_NetClient.Disconnect(pReason); + SetState(IClient::STATE_OFFLINE); + m_pMap->Unload(); + + // disable all downloads + m_MapdownloadChunk = 0; + if(m_MapdownloadFile) + io_close(m_MapdownloadFile); + m_MapdownloadFile = 0; + m_MapdownloadCrc = 0; + m_MapdownloadTotalsize = -1; + m_MapdownloadAmount = 0; + + // clear the current server info + mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo)); + mem_zero(&m_ServerAddress, sizeof(m_ServerAddress)); + + // clear snapshots + m_aSnapshots[SNAP_CURRENT] = 0; + m_aSnapshots[SNAP_PREV] = 0; + m_RecivedSnapshots = 0; +} + +void CClient::Disconnect() +{ + DisconnectWithReason(0); +} + + +void CClient::GetServerInfo(CServerInfo *pServerInfo) +{ + mem_copy(pServerInfo, &m_CurrentServerInfo, sizeof(m_CurrentServerInfo)); +} + +void CClient::ServerInfoRequest() +{ + mem_zero(&m_CurrentServerInfo, sizeof(m_CurrentServerInfo)); + m_CurrentServerInfoRequestTime = 0; +} + +int CClient::LoadData() +{ + m_DebugFont = Graphics()->LoadTexture("debug_font.png", CImageInfo::FORMAT_AUTO, IGraphics::TEXLOAD_NORESAMPLE); + return 1; +} + +// --- + +void *CClient::SnapGetItem(int SnapId, int Index, CSnapItem *pItem) +{ + CSnapshotItem *i; + dbg_assert(SnapId >= 0 && SnapId < NUM_SNAPSHOT_TYPES, "invalid SnapId"); + i = m_aSnapshots[SnapId]->m_pAltSnap->GetItem(Index); + pItem->m_DataSize = m_aSnapshots[SnapId]->m_pAltSnap->GetItemSize(Index); + pItem->m_Type = i->Type(); + pItem->m_Id = i->ID(); + return (void *)i->Data(); +} + +void CClient::SnapInvalidateItem(int SnapId, int Index) +{ + CSnapshotItem *i; + dbg_assert(SnapId >= 0 && SnapId < NUM_SNAPSHOT_TYPES, "invalid SnapId"); + i = m_aSnapshots[SnapId]->m_pAltSnap->GetItem(Index); + if(i) + { + if((char *)i < (char *)m_aSnapshots[SnapId]->m_pAltSnap || (char *)i > (char *)m_aSnapshots[SnapId]->m_pAltSnap + m_aSnapshots[SnapId]->m_SnapSize) + dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); + if((char *)i >= (char *)m_aSnapshots[SnapId]->m_pSnap && (char *)i < (char *)m_aSnapshots[SnapId]->m_pSnap + m_aSnapshots[SnapId]->m_SnapSize) + dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); + i->m_TypeAndID = -1; + } +} + +void *CClient::SnapFindItem(int SnapId, int Type, int Id) +{ + // TODO: linear search. should be fixed. + int i; + + if(!m_aSnapshots[SnapId]) + return 0x0; + + for(i = 0; i < m_aSnapshots[SnapId]->m_pSnap->NumItems(); i++) + { + CSnapshotItem *pItem = m_aSnapshots[SnapId]->m_pAltSnap->GetItem(i); + if(pItem->Type() == Type && pItem->ID() == Id) + return (void *)pItem->Data(); + } + return 0x0; +} + +int CClient::SnapNumItems(int SnapId) +{ + dbg_assert(SnapId >= 0 && SnapId < NUM_SNAPSHOT_TYPES, "invalid SnapId"); + if(!m_aSnapshots[SnapId]) + return 0; + return m_aSnapshots[SnapId]->m_pSnap->NumItems(); +} + +void CClient::SnapSetStaticsize(int ItemType, int Size) +{ + m_SnapshotDelta.SetStaticsize(ItemType, Size); +} + + +void CClient::DebugRender() +{ + static NETSTATS Prev, Current; + static int64 LastSnap = 0; + static float FrameTimeAvg = 0; + int64 Now = time_get(); + char aBuffer[512]; + + if(!g_Config.m_Debug) + return; + + //m_pGraphics->BlendNormal(); + Graphics()->TextureSet(m_DebugFont); + Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight()); + + if(time_get()-LastSnap > time_freq()) + { + LastSnap = time_get(); + Prev = Current; + net_stats(&Current); + } + + /* + eth = 14 + ip = 20 + udp = 8 + total = 42 + */ + FrameTimeAvg = FrameTimeAvg*0.9f + m_FrameTime*0.1f; + str_format(aBuffer, sizeof(aBuffer), "ticks: %8d %8d mem %dk %d gfxmem: %dk fps: %3d", + m_CurGameTick, m_PredTick, + mem_stats()->allocated/1024, + mem_stats()->total_allocations, + Graphics()->MemoryUsage()/1024, + (int)(1.0f/FrameTimeAvg)); + Graphics()->QuadsText(2, 2, 16, 1,1,1,1, aBuffer); + + + { + int SendPackets = (Current.sent_packets-Prev.sent_packets); + int SendBytes = (Current.sent_bytes-Prev.sent_bytes); + int SendTotal = SendBytes + SendPackets*42; + int RecvPackets = (Current.recv_packets-Prev.recv_packets); + int RecvBytes = (Current.recv_bytes-Prev.recv_bytes); + int RecvTotal = RecvBytes + RecvPackets*42; + + if(!SendPackets) SendPackets++; + if(!RecvPackets) RecvPackets++; + str_format(aBuffer, sizeof(aBuffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d", + SendPackets, SendBytes, SendPackets*42, SendTotal, (SendTotal*8)/1024, SendBytes/SendPackets, + RecvPackets, RecvBytes, RecvPackets*42, RecvTotal, (RecvTotal*8)/1024, RecvBytes/RecvPackets); + Graphics()->QuadsText(2, 14, 16, 1,1,1,1, aBuffer); + } + + // render rates + { + int y = 0; + int i; + for(i = 0; i < 256; i++) + { + if(m_SnapshotDelta.GetDataRate(i)) + { + str_format(aBuffer, sizeof(aBuffer), "%4d %20s: %8d %8d %8d", i, GameClient()->GetItemName(i), m_SnapshotDelta.GetDataRate(i)/8, m_SnapshotDelta.GetDataUpdates(i), + (m_SnapshotDelta.GetDataRate(i)/m_SnapshotDelta.GetDataUpdates(i))/8); + Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, aBuffer); + y++; + } + } + } + + str_format(aBuffer, sizeof(aBuffer), "pred: %d ms", + (int)((m_PredictedTime.Get(Now)-m_GameTime.Get(Now))*1000/(float)time_freq())); + Graphics()->QuadsText(2, 70, 16, 1,1,1,1, aBuffer); + + // render graphs + if(g_Config.m_DbgGraphs) + { + //Graphics()->MapScreen(0,0,400.0f,300.0f); + float w = Graphics()->ScreenWidth()/4.0f; + float h = Graphics()->ScreenHeight()/6.0f; + float sp = Graphics()->ScreenWidth()/100.0f; + float x = Graphics()->ScreenWidth()-w-sp; + + m_FpsGraph.ScaleMax(); + m_FpsGraph.ScaleMin(); + m_FpsGraph.Render(Graphics(), m_DebugFont, x, sp*5, w, h, "FPS"); + m_InputtimeMarginGraph.Render(Graphics(), m_DebugFont, x, sp*5+h+sp, w, h, "Prediction Margin"); + m_GametimeMarginGraph.Render(Graphics(), m_DebugFont, x, sp*5+h+sp+h+sp, w, h, "Gametime Margin"); + } +} + +void CClient::Quit() +{ + SetState(IClient::STATE_QUITING); +} + +const char *CClient::ErrorString() +{ + return m_NetClient.ErrorString(); +} + +void CClient::Render() +{ + if(g_Config.m_GfxClear) + Graphics()->Clear(1,1,0); + + GameClient()->OnRender(); + DebugRender(); +} + +const char *CClient::LoadMap(const char *pName, const char *pFilename, unsigned WantedCrc) +{ + static char aErrorMsg[128]; + + SetState(IClient::STATE_LOADING); + + if(!m_pMap->Load(pFilename)) + { + str_format(aErrorMsg, sizeof(aErrorMsg), "map '%s' not found", pFilename); + return aErrorMsg; + } + + // get the crc of the map + if(m_pMap->Crc() != WantedCrc) + { + m_pMap->Unload(); + str_format(aErrorMsg, sizeof(aErrorMsg), "map differs from the server. %08x != %08x", m_pMap->Crc(), WantedCrc); + return aErrorMsg; + } + + // stop demo recording if we loaded a new map + m_DemoRecorder.Stop(); + + dbg_msg("client", "loaded map '%s'", pFilename); + m_RecivedSnapshots = 0; + + str_copy(m_aCurrentMap, pName, sizeof(m_aCurrentMap)); + m_CurrentMapCrc = m_pMap->Crc(); + + return 0x0; +} + + + +const char *CClient::LoadMapSearch(const char *pMapName, int WantedCrc) +{ + const char *pError = 0; + char aBuf[512]; + dbg_msg("client", "loading map, map=%s wanted crc=%08x", pMapName, WantedCrc); + SetState(IClient::STATE_LOADING); + + // try the normal maps folder + str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName); + pError = LoadMap(pMapName, aBuf, WantedCrc); + if(!pError) + return pError; + + // try the downloaded maps + str_format(aBuf, sizeof(aBuf), "downloadedmaps/%s_%08x.map", pMapName, WantedCrc); + pError = LoadMap(pMapName, aBuf, WantedCrc); + return pError; +} + +int CClient::PlayerScoreComp(const void *a, const void *b) +{ + CServerInfo::CPlayer *p0 = (CServerInfo::CPlayer *)a; + CServerInfo::CPlayer *p1 = (CServerInfo::CPlayer *)b; + if(p0->m_Score == p1->m_Score) + return 0; + if(p0->m_Score < p1->m_Score) + return 1; + return -1; +} + +void CClient::ProcessPacket(CNetChunk *pPacket) +{ + if(pPacket->m_ClientID == -1) + { + // connectionlesss + if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && + mem_comp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) + { + unsigned char *pVersionData = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION); + int VersionMatch = !mem_comp(pVersionData, VERSION_DATA, sizeof(VERSION_DATA)); + + dbg_msg("client/version", "version does %s (%d.%d.%d)", + VersionMatch ? "match" : "NOT match", + pVersionData[1], pVersionData[2], pVersionData[3]); + + // assume version is out of date when version-data doesn't match + if (!VersionMatch) + { + str_format(m_aVersionStr, sizeof(m_aVersionStr), "%d.%d.%d", pVersionData[1], pVersionData[2], pVersionData[3]); + } + } + + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) && + mem_comp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) + { + int Size = pPacket->m_DataSize-sizeof(SERVERBROWSE_LIST); + int Num = Size/sizeof(MASTERSRV_ADDR); + MASTERSRV_ADDR *pAddrs = (MASTERSRV_ADDR *)((char*)pPacket->m_pData+sizeof(SERVERBROWSE_LIST)); + int i; + + for(i = 0; i < Num; i++) + { + NETADDR Addr; + + // convert address + mem_zero(&Addr, sizeof(Addr)); + Addr.type = NETTYPE_IPV4; + Addr.ip[0] = pAddrs[i].m_aIp[0]; + Addr.ip[1] = pAddrs[i].m_aIp[1]; + Addr.ip[2] = pAddrs[i].m_aIp[2]; + Addr.ip[3] = pAddrs[i].m_aIp[3]; + Addr.port = (pAddrs[i].m_aPort[1]<<8) | pAddrs[i].m_aPort[0]; + + m_ServerBrowser.Set(Addr, IServerBrowser::SET_MASTER_ADD, -1, 0x0); + } + } + + { + int PacketType = 0; + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && mem_comp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) + PacketType = 2; + + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_OLD_INFO) && mem_comp(pPacket->m_pData, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0) + PacketType = 1; + + if(PacketType) + { + // we got ze info + CUnpacker Up; + CServerInfo Info = {0}; + int Token = -1; + + Up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO)); + if(PacketType >= 2) + Token = str_toint(Up.GetString()); + str_copy(Info.m_aVersion, Up.GetString(), sizeof(Info.m_aVersion)); + str_copy(Info.m_aName, Up.GetString(), sizeof(Info.m_aName)); + str_copy(Info.m_aMap, Up.GetString(), sizeof(Info.m_aMap)); + str_copy(Info.m_aGameType, Up.GetString(), sizeof(Info.m_aGameType)); + Info.m_Flags = str_toint(Up.GetString()); + Info.m_Progression = str_toint(Up.GetString()); + Info.m_NumPlayers = str_toint(Up.GetString()); + Info.m_MaxPlayers = str_toint(Up.GetString()); + + // don't add invalid info to the server browser list + if(Info.m_NumPlayers > MAX_CLIENTS || Info.m_MaxPlayers > MAX_CLIENTS) + return; + + str_format(Info.m_aAddress, sizeof(Info.m_aAddress), "%d.%d.%d.%d:%d", + pPacket->m_Address.ip[0], pPacket->m_Address.ip[1], pPacket->m_Address.ip[2], + pPacket->m_Address.ip[3], pPacket->m_Address.port); + + for(int i = 0; i < Info.m_NumPlayers; i++) + { + str_copy(Info.m_aPlayers[i].m_aName, Up.GetString(), sizeof(Info.m_aPlayers[i].m_aName)); + Info.m_aPlayers[i].m_Score = str_toint(Up.GetString()); + } + + if(!Up.Error()) + { + // sort players + qsort(Info.m_aPlayers, Info.m_NumPlayers, sizeof(*Info.m_aPlayers), PlayerScoreComp); + + if(net_addr_comp(&m_ServerAddress, &pPacket->m_Address) == 0) + { + mem_copy(&m_CurrentServerInfo, &Info, sizeof(m_CurrentServerInfo)); + m_CurrentServerInfo.m_NetAddr = m_ServerAddress; + m_CurrentServerInfoRequestTime = -1; + } + else + { + if(PacketType == 2) + m_ServerBrowser.Set(pPacket->m_Address, IServerBrowser::SET_TOKEN, Token, &Info); + else + m_ServerBrowser.Set(pPacket->m_Address, IServerBrowser::SET_OLD_INTERNET, -1, &Info); + } + } + } + } + } + else + { + CUnpacker Unpacker; + Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize); + + // unpack msgid and system flag + int Msg = Unpacker.GetInt(); + int Sys = Msg&1; + Msg >>= 1; + + if(Unpacker.Error()) + return; + + if(Sys) + { + // system message + if(Msg == NETMSG_MAP_CHANGE) + { + const char *pMap = Unpacker.GetString(); + int MapCrc = Unpacker.GetInt(); + const char *pError = 0; + + if(Unpacker.Error()) + return; + + for(int i = 0; pMap[i]; i++) // protect the player from nasty map names + { + if(pMap[i] == '/' || pMap[i] == '\\') + pError = "strange character in map name"; + } + + if(pError) + DisconnectWithReason(pError); + else + { + pError = LoadMapSearch(pMap, MapCrc); + + if(!pError) + { + dbg_msg("client/network", "loading done"); + SendReady(); + GameClient()->OnConnected(); + } + else + { + str_format(m_aMapdownloadFilename, sizeof(m_aMapdownloadFilename), "downloadedmaps/%s_%08x.map", pMap, MapCrc); + + dbg_msg("client/network", "starting to download map to '%s'", m_aMapdownloadFilename); + + m_MapdownloadChunk = 0; + str_copy(m_aMapdownloadName, pMap, sizeof(m_aMapdownloadName)); + m_MapdownloadFile = Storage()->OpenFile(m_aMapdownloadFilename, IOFLAG_WRITE); + m_MapdownloadCrc = MapCrc; + m_MapdownloadTotalsize = -1; + m_MapdownloadAmount = 0; + + CMsgPacker Msg(NETMSG_REQUEST_MAP_DATA); + Msg.AddInt(m_MapdownloadChunk); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); + + if(g_Config.m_Debug) + dbg_msg("client/network", "requested chunk %d", m_MapdownloadChunk); + } + } + } + else if(Msg == NETMSG_MAP_DATA) + { + int Last = Unpacker.GetInt(); + int TotalSize = Unpacker.GetInt(); + int Size = Unpacker.GetInt(); + const unsigned char *pData = Unpacker.GetRaw(Size); + + // check fior errors + if(Unpacker.Error() || Size <= 0 || TotalSize <= 0 || !m_MapdownloadFile) + return; + + io_write(m_MapdownloadFile, pData, Size); + + m_MapdownloadTotalsize = TotalSize; + m_MapdownloadAmount += Size; + + if(Last) + { + const char *pError; + dbg_msg("client/network", "download complete, loading map"); + + io_close(m_MapdownloadFile); + m_MapdownloadFile = 0; + m_MapdownloadAmount = 0; + m_MapdownloadTotalsize = -1; + + // load map + pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, m_MapdownloadCrc); + if(!pError) + { + dbg_msg("client/network", "loading done"); + SendReady(); + GameClient()->OnConnected(); + } + else + DisconnectWithReason(pError); + } + else + { + // request new chunk + m_MapdownloadChunk++; + + CMsgPacker Msg(NETMSG_REQUEST_MAP_DATA); + Msg.AddInt(m_MapdownloadChunk); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH); + + if(g_Config.m_Debug) + dbg_msg("client/network", "requested chunk %d", m_MapdownloadChunk); + } + } + else if(Msg == NETMSG_PING) + { + CMsgPacker Msg(NETMSG_PING_REPLY); + SendMsgEx(&Msg, 0); + } + else if(Msg == NETMSG_RCON_AUTH_STATUS) + { + int Result = Unpacker.GetInt(); + if(Unpacker.Error() == 0) + m_RconAuthed = Result; + } + else if(Msg == NETMSG_RCON_LINE) + { + const char *pLine = Unpacker.GetString(); + if(Unpacker.Error() == 0) + { + //dbg_msg("remote", "%s", line); + GameClient()->OnRconLine(pLine); + } + } + else if(Msg == NETMSG_PING_REPLY) + dbg_msg("client/network", "latency %.2f", (time_get() - m_PingStartTime)*1000 / (float)time_freq()); + else if(Msg == NETMSG_INPUTTIMING) + { + int InputPredTick = Unpacker.GetInt(); + int TimeLeft = Unpacker.GetInt(); + + // adjust our prediction time + int64 Target = 0; + for(int k = 0; k < 200; k++) + { + if(m_aInputs[k].m_Tick == InputPredTick) + { + Target = m_aInputs[k].m_PredictedTime + (time_get() - m_aInputs[k].m_Time); + Target = Target - (int64)(((TimeLeft-PREDICTION_MARGIN)/1000.0f)*time_freq()); + //st_update(&predicted_time, ); + break; + } + } + + if(Target) + m_PredictedTime.Update(&m_InputtimeMarginGraph, Target, TimeLeft, 1); + } + else if(Msg == NETMSG_SNAP || Msg == NETMSG_SNAPSINGLE || Msg == NETMSG_SNAPEMPTY) + { + //dbg_msg("client/network", "got snapshot"); + int NumParts = 1; + int Part = 0; + int GameTick = Unpacker.GetInt(); + int DeltaTick = GameTick-Unpacker.GetInt(); + int PartSize = 0; + int Crc = 0; + int CompleteSize = 0; + const char *pData = 0; + + // we are not allowed to process snapshot yet + if(State() < IClient::STATE_LOADING) + return; + + if(Msg == NETMSG_SNAP) + { + NumParts = Unpacker.GetInt(); + Part = Unpacker.GetInt(); + } + + if(Msg != NETMSG_SNAPEMPTY) + { + Crc = Unpacker.GetInt(); + PartSize = Unpacker.GetInt(); + } + + pData = (const char *)Unpacker.GetRaw(PartSize); + + if(Unpacker.Error()) + return; + + if(GameTick >= m_CurrentRecvTick) + { + if(GameTick != m_CurrentRecvTick) + { + m_SnapshotParts = 0; + m_CurrentRecvTick = GameTick; + } + + // TODO: clean this up abit + mem_copy((char*)m_aSnapshotIncommingData + Part*MAX_SNAPSHOT_PACKSIZE, pData, PartSize); + m_SnapshotParts |= 1<<Part; + + if(m_SnapshotParts == (unsigned)((1<<NumParts)-1)) + { + static CSnapshot Emptysnap; + CSnapshot *pDeltaShot = &Emptysnap; + int PurgeTick; + void *pDeltaData; + int DeltaSize; + unsigned char aTmpBuffer2[CSnapshot::MAX_SIZE]; + unsigned char aTmpBuffer3[CSnapshot::MAX_SIZE]; + int SnapSize; + + CompleteSize = (NumParts-1) * MAX_SNAPSHOT_PACKSIZE + PartSize; + + // reset snapshoting + m_SnapshotParts = 0; + + // find snapshot that we should use as delta + Emptysnap.Clear(); + + // find delta + if(DeltaTick >= 0) + { + int DeltashotSize = m_SnapshotStorage.Get(DeltaTick, 0, &pDeltaShot, 0); + + if(DeltashotSize < 0) + { + // couldn't find the delta snapshots that the server used + // to compress this snapshot. force the server to resync + if(g_Config.m_Debug) + dbg_msg("client", "error, couldn't find the delta snapshot"); + + // ack snapshot + // TODO: combine this with the input message + m_AckGameTick = -1; + return; + } + } + + // decompress snapshot + pDeltaData = m_SnapshotDelta.EmptyDelta(); + DeltaSize = sizeof(int)*3; + + if(CompleteSize) + { + int IntSize = CVariableInt::Decompress(m_aSnapshotIncommingData, CompleteSize, aTmpBuffer2); + + if(IntSize < 0) // failure during decompression, bail + return; + + pDeltaData = aTmpBuffer2; + DeltaSize = IntSize; + } + + // unpack delta + PurgeTick = DeltaTick; + SnapSize = m_SnapshotDelta.UnpackDelta(pDeltaShot, (CSnapshot*)aTmpBuffer3, pDeltaData, DeltaSize); + if(SnapSize < 0) + { + dbg_msg("client", "delta unpack failed!"); + return; + } + + if(Msg != NETMSG_SNAPEMPTY && ((CSnapshot*)aTmpBuffer3)->Crc() != Crc) + { + if(g_Config.m_Debug) + { + dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d", + m_SnapCrcErrors, GameTick, Crc, ((CSnapshot*)aTmpBuffer3)->Crc(), CompleteSize, DeltaTick); + } + + m_SnapCrcErrors++; + if(m_SnapCrcErrors > 10) + { + // to many errors, send reset + m_AckGameTick = -1; + SendInput(); + m_SnapCrcErrors = 0; + } + return; + } + else + { + if(m_SnapCrcErrors) + m_SnapCrcErrors--; + } + + // purge old snapshots + PurgeTick = DeltaTick; + if(m_aSnapshots[SNAP_PREV] && m_aSnapshots[SNAP_PREV]->m_Tick < PurgeTick) + PurgeTick = m_aSnapshots[SNAP_PREV]->m_Tick; + if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_CURRENT]->m_Tick < PurgeTick) + PurgeTick = m_aSnapshots[SNAP_PREV]->m_Tick; + m_SnapshotStorage.PurgeUntil(PurgeTick); + + // add new + m_SnapshotStorage.Add(GameTick, time_get(), SnapSize, (CSnapshot*)aTmpBuffer3, 1); + + // add snapshot to demo + if(m_DemoRecorder.IsRecording()) + { + + // write tick marker + /* + DEMOREC_TICKMARKER marker; + marker.tick = game_tick; + swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int)); + demorec_record_write("TICK", sizeof(marker), &marker); + demorec_record_write("SNAP", snapsize, tmpbuffer3); + */ + + // write snapshot + m_DemoRecorder.RecordSnapshot(GameTick, aTmpBuffer3, SnapSize); + } + + // apply snapshot, cycle pointers + m_RecivedSnapshots++; + + m_CurrentRecvTick = GameTick; + + // we got two snapshots until we see us self as connected + if(m_RecivedSnapshots == 2) + { + // start at 200ms and work from there + m_PredictedTime.Init(GameTick*time_freq()/50); + m_PredictedTime.SetAdjustSpeed(1, 1000.0f); + m_GameTime.Init((GameTick-1)*time_freq()/50); + m_aSnapshots[SNAP_PREV] = m_SnapshotStorage.m_pFirst; + m_aSnapshots[SNAP_CURRENT] = m_SnapshotStorage.m_pLast; + m_LocalStartTime = time_get(); + SetState(IClient::STATE_ONLINE); + } + + // adjust game time + { + int64 Now = m_GameTime.Get(time_get()); + int64 TickStart = GameTick*time_freq()/50; + int64 TimeLeft = (TickStart-Now)*1000 / time_freq(); + //st_update(&game_time, (game_tick-1)*time_freq()/50); + m_GameTime.Update(&m_GametimeMarginGraph, (GameTick-1)*time_freq()/50, TimeLeft, 0); + } + + // ack snapshot + m_AckGameTick = GameTick; + } + } + } + } + else + { + // game message + if(m_DemoRecorder.IsRecording()) + m_DemoRecorder.RecordMessage(pPacket->m_pData, pPacket->m_DataSize); + // demorec_record_write("MESG", pPacket->data_size, ); + + GameClient()->OnMessage(Msg, &Unpacker); + } + } +} + +void CClient::PumpNetwork() +{ + m_NetClient.Update(); + + if(State() != IClient::STATE_DEMOPLAYBACK) + { + // check for errors + if(State() != IClient::STATE_OFFLINE && m_NetClient.State() == NETSTATE_OFFLINE) + { + SetState(IClient::STATE_OFFLINE); + Disconnect(); + dbg_msg("client", "offline error='%s'", m_NetClient.ErrorString()); + } + + // + if(State() == IClient::STATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE) + { + // we switched to online + dbg_msg("client", "connected, sending info"); + SetState(IClient::STATE_LOADING); + SendInfo(); + } + } + + // process packets + CNetChunk Packet; + while(m_NetClient.Recv(&Packet)) + ProcessPacket(&Packet); +} + +void CClient::OnDemoPlayerSnapshot(void *pData, int Size) +{ + // update ticks, they could have changed + const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info(); + CSnapshotStorage::CHolder *pTemp; + m_CurGameTick = pInfo->m_Info.m_CurrentTick; + m_PrevGameTick = pInfo->m_PreviousTick; + + // handle snapshots + pTemp = m_aSnapshots[SNAP_PREV]; + m_aSnapshots[SNAP_PREV] = m_aSnapshots[SNAP_CURRENT]; + m_aSnapshots[SNAP_CURRENT] = pTemp; + + mem_copy(m_aSnapshots[SNAP_CURRENT]->m_pSnap, pData, Size); + mem_copy(m_aSnapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size); + + GameClient()->OnNewSnapshot(); +} + +void CClient::OnDemoPlayerMessage(void *pData, int Size) +{ + CUnpacker Unpacker; + Unpacker.Reset(pData, Size); + + // unpack msgid and system flag + int Msg = Unpacker.GetInt(); + int Sys = Msg&1; + Msg >>= 1; + + if(Unpacker.Error()) + return; + + if(!Sys) + GameClient()->OnMessage(Msg, &Unpacker); +} +/* +const IDemoPlayer::CInfo *client_demoplayer_getinfo() +{ + static DEMOPLAYBACK_INFO ret; + const DEMOREC_PLAYBACKINFO *info = m_DemoPlayer.Info(); + ret.first_tick = info->first_tick; + ret.last_tick = info->last_tick; + ret.current_tick = info->current_tick; + ret.paused = info->paused; + ret.speed = info->speed; + return &ret; +}*/ + +/* +void DemoPlayer()->SetPos(float percent) +{ + demorec_playback_set(percent); +} + +void DemoPlayer()->SetSpeed(float speed) +{ + demorec_playback_setspeed(speed); +} + +void DemoPlayer()->SetPause(int paused) +{ + if(paused) + demorec_playback_pause(); + else + demorec_playback_unpause(); +}*/ + +void CClient::Update() +{ + if(State() == IClient::STATE_DEMOPLAYBACK) + { + m_DemoPlayer.Update(); + if(m_DemoPlayer.IsPlaying()) + { + // update timers + const CDemoPlayer::CPlaybackInfo *pInfo = m_DemoPlayer.Info(); + m_CurGameTick = pInfo->m_Info.m_CurrentTick; + m_PrevGameTick = pInfo->m_PreviousTick; + m_GameIntraTick = pInfo->m_IntraTick; + m_GameTickTime = pInfo->m_TickTime; + } + else + { + // disconnect on error + Disconnect(); + } + } + else if(State() != IClient::STATE_OFFLINE && m_RecivedSnapshots >= 3) + { + // switch snapshot + int Repredict = 0; + int64 Freq = time_freq(); + int64 Now = m_GameTime.Get(time_get()); + int64 PredNow = m_PredictedTime.Get(time_get()); + + while(1) + { + CSnapshotStorage::CHolder *pCur = m_aSnapshots[SNAP_CURRENT]; + int64 TickStart = (pCur->m_Tick)*time_freq()/50; + + if(TickStart < Now) + { + CSnapshotStorage::CHolder *pNext = m_aSnapshots[SNAP_CURRENT]->m_pNext; + if(pNext) + { + m_aSnapshots[SNAP_PREV] = m_aSnapshots[SNAP_CURRENT]; + m_aSnapshots[SNAP_CURRENT] = pNext; + + // set ticks + m_CurGameTick = m_aSnapshots[SNAP_CURRENT]->m_Tick; + m_PrevGameTick = m_aSnapshots[SNAP_PREV]->m_Tick; + + if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_PREV]) + { + GameClient()->OnNewSnapshot(); + Repredict = 1; + } + } + else + break; + } + else + break; + } + + if(m_aSnapshots[SNAP_CURRENT] && m_aSnapshots[SNAP_PREV]) + { + int64 CurtickStart = (m_aSnapshots[SNAP_CURRENT]->m_Tick)*time_freq()/50; + int64 PrevtickStart = (m_aSnapshots[SNAP_PREV]->m_Tick)*time_freq()/50; + //tg_add(&predicted_time_graph, pred_now, 0); + int PrevPredTick = (int)(PredNow*50/time_freq()); + int NewPredTick = PrevPredTick+1; + static float LastPredintra = 0; + + m_GameIntraTick = (Now - PrevtickStart) / (float)(CurtickStart-PrevtickStart); + m_GameTickTime = (Now - PrevtickStart) / (float)Freq; //(float)SERVER_TICK_SPEED); + + CurtickStart = NewPredTick*time_freq()/50; + PrevtickStart = PrevPredTick*time_freq()/50; + m_PredIntraTick = (PredNow - PrevtickStart) / (float)(CurtickStart-PrevtickStart); + + if(NewPredTick < m_aSnapshots[SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || NewPredTick > m_aSnapshots[SNAP_PREV]->m_Tick+SERVER_TICK_SPEED) + { + dbg_msg("client", "prediction time reset!"); + m_PredictedTime.Init(m_aSnapshots[SNAP_CURRENT]->m_Tick*time_freq()/50); + } + + if(NewPredTick > m_PredTick) + { + LastPredintra = m_PredIntraTick; + m_PredTick = NewPredTick; + Repredict = 1; + + // send input + SendInput(); + } + + LastPredintra = m_PredIntraTick; + } + + // only do sane predictions + if(Repredict) + { + if(m_PredTick > m_CurGameTick && m_PredTick < m_CurGameTick+50) + GameClient()->OnPredict(); + } + + // fetch server info if we don't have it + if(State() >= IClient::STATE_LOADING && + m_CurrentServerInfoRequestTime >= 0 && + time_get() > m_CurrentServerInfoRequestTime) + { + m_ServerBrowser.Request(m_ServerAddress); + m_CurrentServerInfoRequestTime = time_get()+time_freq()*2; + } + } + + // STRESS TEST: join the server again + if(g_Config.m_DbgStress) + { + static int64 ActionTaken = 0; + int64 Now = time_get(); + if(State() == IClient::STATE_OFFLINE) + { + if(Now > ActionTaken+time_freq()*2) + { + dbg_msg("stress", "reconnecting!"); + Connect(g_Config.m_DbgStressServer); + ActionTaken = Now; + } + } + else + { + /*if(now > action_taken+time_freq()*(10+config.dbg_stress)) + { + dbg_msg("stress", "disconnecting!"); + Disconnect(); + action_taken = now; + }*/ + } + } + + // pump the network + PumpNetwork(); + + // update the maser server registry + MasterServer()->Update(); + + // update the server browser + m_ServerBrowser.Update(); +} + +const char *CClient::UserDirectory() +{ + static char saPath[1024] = {0}; + fs_storage_path("Teeworlds", saPath, sizeof(saPath)); + return saPath; +} + +void CClient::VersionUpdate() +{ + if(m_VersionInfo.m_State == 0) + { + m_Engine.HostLookup(&m_VersionInfo.m_VersionServeraddr, g_Config.m_ClVersionServer); + m_VersionInfo.m_State++; + } + else if(m_VersionInfo.m_State == 1) + { + if(m_VersionInfo.m_VersionServeraddr.m_Job.Status() == CJob::STATE_DONE) + { + CNetChunk Packet; + + mem_zero(&Packet, sizeof(Packet)); + + m_VersionInfo.m_VersionServeraddr.m_Addr.port = VERSIONSRV_PORT; + + Packet.m_ClientID = -1; + Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr; + Packet.m_pData = VERSIONSRV_GETVERSION; + Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION); + Packet.m_Flags = NETSENDFLAG_CONNLESS; + + m_NetClient.Send(&Packet); + m_VersionInfo.m_State++; + } + } +} + +void CClient::InitEngine(const char *pAppname) +{ + m_Engine.Init(pAppname); +} + +void CClient::RegisterInterfaces() +{ + Kernel()->RegisterInterface(static_cast<IDemoPlayer*>(&m_DemoPlayer)); + Kernel()->RegisterInterface(static_cast<IServerBrowser*>(&m_ServerBrowser)); +} + +void CClient::InitInterfaces() +{ + // fetch interfaces + m_pEditor = Kernel()->RequestInterface<IEditor>(); + m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>(); + m_pSound = Kernel()->RequestInterface<IEngineSound>(); + m_pGameClient = Kernel()->RequestInterface<IGameClient>(); + m_pInput = Kernel()->RequestInterface<IEngineInput>(); + m_pMap = Kernel()->RequestInterface<IEngineMap>(); + m_pMasterServer = Kernel()->RequestInterface<IEngineMasterServer>(); + m_pStorage = Kernel()->RequestInterface<IStorage>(); + + // + m_ServerBrowser.SetBaseInfo(&m_NetClient, m_pGameClient->NetVersion()); +} + +void CClient::Run() +{ + int64 ReportTime = time_get(); + int64 ReportInterval = time_freq()*1; + + m_LocalStartTime = time_get(); + m_SnapshotParts = 0; + + // init graphics + if(m_pGraphics->Init() != 0) + return; + + // init font rendering + Kernel()->RequestInterface<IEngineTextRender>()->Init(); + + // init the input + Input()->Init(); + + // start refreshing addresses while we load + MasterServer()->RefreshAddresses(); + + // init the editor + m_pEditor->Init(); + + // init sound, allowed to fail + Sound()->Init(); + + // load data + if(!LoadData()) + return; + + GameClient()->OnInit(); + dbg_msg("client", "version %s", GameClient()->NetVersion()); + + // open socket + { + NETADDR BindAddr; + mem_zero(&BindAddr, sizeof(BindAddr)); + m_NetClient.Open(BindAddr, 0); + } + + // connect to the server if wanted + /* + if(config.cl_connect[0] != 0) + Connect(config.cl_connect); + config.cl_connect[0] = 0; + */ + + // + m_FpsGraph.Init(0.0f, 200.0f); + + // never start with the editor + g_Config.m_ClEditor = 0; + + Input()->MouseModeRelative(); + + while (1) + { + int64 FrameStartTime = time_get(); + m_Frames++; + + // + VersionUpdate(); + + // handle pending connects + if(m_aCmdConnect[0]) + { + str_copy(g_Config.m_UiServerAddress, m_aCmdConnect, sizeof(g_Config.m_UiServerAddress)); + Connect(m_aCmdConnect); + m_aCmdConnect[0] = 0; + } + + // update input + Input()->Update(); + + // update sound + Sound()->Update(); + + // release focus + if(!m_pGraphics->WindowActive()) + { + if(m_WindowMustRefocus == 0) + Input()->MouseModeAbsolute(); + m_WindowMustRefocus = 1; + } + else if (g_Config.m_DbgFocus && Input()->KeyPressed(KEY_ESCAPE)) + { + Input()->MouseModeAbsolute(); + m_WindowMustRefocus = 1; + } + + // refocus + if(m_WindowMustRefocus && m_pGraphics->WindowActive()) + { + if(m_WindowMustRefocus < 3) + { + Input()->MouseModeAbsolute(); + m_WindowMustRefocus++; + } + + if(Input()->KeyPressed(KEY_MOUSE_1)) + { + Input()->MouseModeRelative(); + m_WindowMustRefocus = 0; + } + } + + // panic quit button + if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyPressed('q')) + break; + + if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('d')) + g_Config.m_Debug ^= 1; + + if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('g')) + g_Config.m_DbgGraphs ^= 1; + + if(Input()->KeyPressed(KEY_LCTRL) && Input()->KeyPressed(KEY_LSHIFT) && Input()->KeyDown('e')) + { + g_Config.m_ClEditor = g_Config.m_ClEditor^1; + Input()->MouseModeRelative(); + } + + /* + if(!gfx_window_open()) + break; + */ + + // render + if(g_Config.m_ClEditor) + { + Update(); + m_pEditor->UpdateAndRender(); + m_pGraphics->Swap(); + } + else + { + Update(); + + if(g_Config.m_DbgStress) + { + if((m_Frames%10) == 0) + { + Render(); + m_pGraphics->Swap(); + } + } + else + { + Render(); + m_pGraphics->Swap(); + } + } + + // check conditions + if(State() == IClient::STATE_QUITING) + break; + + // beNice + if(g_Config.m_DbgStress) + thread_sleep(5); + else if(g_Config.m_ClCpuThrottle || !m_pGraphics->WindowActive()) + thread_sleep(1); + + if(g_Config.m_DbgHitch) + { + thread_sleep(g_Config.m_DbgHitch); + g_Config.m_DbgHitch = 0; + } + + if(ReportTime < time_get()) + { + if(0 && g_Config.m_Debug) + { + dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", + m_Frames/(float)(ReportInterval/time_freq()), + 1.0f/m_FrameTimeHigh, + 1.0f/m_FrameTimeLow, + m_NetClient.State()); + } + m_FrameTimeLow = 1; + m_FrameTimeHigh = 0; + m_Frames = 0; + ReportTime += ReportInterval; + } + + // update frametime + m_FrameTime = (time_get()-FrameStartTime)/(float)time_freq(); + if(m_FrameTime < m_FrameTimeLow) + m_FrameTimeLow = m_FrameTime; + if(m_FrameTime > m_FrameTimeHigh) + m_FrameTimeHigh = m_FrameTime; + + m_LocalTime = (time_get()-m_LocalStartTime)/(float)time_freq(); + + m_FpsGraph.Add(1.0f/m_FrameTime, 1,1,1); + } + + GameClient()->OnShutdown(); + Disconnect(); + + m_pGraphics->Shutdown(); + m_pSound->Shutdown(); +} + + +void CClient::Con_Connect(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + str_copy(pSelf->m_aCmdConnect, pResult->GetString(0), sizeof(pSelf->m_aCmdConnect)); +} + +void CClient::Con_Disconnect(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->Disconnect(); +} + +void CClient::Con_Quit(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->Quit(); +} + +void CClient::Con_Minimize(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->Graphics()->Minimize(); +} + +void CClient::Con_Ping(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + + CMsgPacker Msg(NETMSG_PING); + pSelf->SendMsgEx(&Msg, 0); + pSelf->m_PingStartTime = time_get(); +} + +void CClient::Con_Screenshot(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->Graphics()->TakeScreenshot(); +} + +void CClient::Con_Rcon(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->Rcon(pResult->GetString(0)); +} + +void CClient::Con_RconAuth(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->RconAuth("", pResult->GetString(0)); +} + +void CClient::Con_AddFavorite(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + NETADDR Addr; + if(net_addr_from_str(&Addr, pResult->GetString(0)) == 0) + pSelf->m_ServerBrowser.AddFavorite(Addr); +} + +const char *CClient::DemoPlayer_Play(const char *pFilename) +{ + int Crc; + const char *pError; + Disconnect(); + m_NetClient.ResetErrorString(); + + // try to start playback + m_DemoPlayer.SetListner(this); + + if(m_DemoPlayer.Load(Storage(), pFilename)) + return "error loading demo"; + + // load map + Crc = (m_DemoPlayer.Info()->m_Header.m_aCrc[0]<<24)| + (m_DemoPlayer.Info()->m_Header.m_aCrc[1]<<16)| + (m_DemoPlayer.Info()->m_Header.m_aCrc[2]<<8)| + (m_DemoPlayer.Info()->m_Header.m_aCrc[3]); + pError = LoadMapSearch(m_DemoPlayer.Info()->m_Header.m_aMap, Crc); + if(pError) + { + DisconnectWithReason(pError); + return pError; + } + + GameClient()->OnConnected(); + + // setup buffers + mem_zero(m_aDemorecSnapshotData, sizeof(m_aDemorecSnapshotData)); + + m_aSnapshots[SNAP_CURRENT] = &m_aDemorecSnapshotHolders[SNAP_CURRENT]; + m_aSnapshots[SNAP_PREV] = &m_aDemorecSnapshotHolders[SNAP_PREV]; + + m_aSnapshots[SNAP_CURRENT]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][0]; + m_aSnapshots[SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_CURRENT][1]; + m_aSnapshots[SNAP_CURRENT]->m_SnapSize = 0; + m_aSnapshots[SNAP_CURRENT]->m_Tick = -1; + + m_aSnapshots[SNAP_PREV]->m_pSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][0]; + m_aSnapshots[SNAP_PREV]->m_pAltSnap = (CSnapshot *)m_aDemorecSnapshotData[SNAP_PREV][1]; + m_aSnapshots[SNAP_PREV]->m_SnapSize = 0; + m_aSnapshots[SNAP_PREV]->m_Tick = -1; + + // enter demo playback state + SetState(IClient::STATE_DEMOPLAYBACK); + + m_DemoPlayer.Play(); + GameClient()->OnEnterGame(); + + return 0; +} + +void CClient::Con_Play(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->DemoPlayer_Play(pResult->GetString(0)); +} + +void CClient::Con_Record(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + if(pSelf->State() != IClient::STATE_ONLINE) + dbg_msg("demorec/record", "client is not online"); + else + { + char aFilename[512]; + str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0)); + pSelf->m_DemoRecorder.Start(pSelf->Storage(), aFilename, pSelf->GameClient()->NetVersion(), pSelf->m_aCurrentMap, pSelf->m_CurrentMapCrc, "client"); + } +} + +void CClient::Con_StopRecord(IConsole::IResult *pResult, void *pUserData) +{ + CClient *pSelf = (CClient *)pUserData; + pSelf->m_DemoRecorder.Stop(); +} + +void CClient::Con_ServerDummy(IConsole::IResult *pResult, void *pUserData) +{ + dbg_msg("client", "this command is not available on the client"); +} + +void CClient::RegisterCommands() +{ + m_pConsole = Kernel()->RequestInterface<IConsole>(); + // register server dummy commands for tab completion + m_pConsole->Register("kick", "i", CFGFLAG_SERVER, Con_ServerDummy, this, "Kick player with specified id"); + m_pConsole->Register("ban", "s?i", CFGFLAG_SERVER, Con_ServerDummy, this, "Ban player with ip/id for x minutes"); + m_pConsole->Register("unban", "s", CFGFLAG_SERVER, Con_ServerDummy, this, "Unban ip"); + m_pConsole->Register("bans", "", CFGFLAG_SERVER, Con_ServerDummy, this, "Show banlist"); + m_pConsole->Register("status", "", CFGFLAG_SERVER, Con_ServerDummy, this, "List players"); + m_pConsole->Register("shutdown", "", CFGFLAG_SERVER, Con_ServerDummy, this, "Shut down"); + m_pConsole->Register("record", "s", CFGFLAG_SERVER, Con_ServerDummy, this, "Record to a file"); + m_pConsole->Register("stoprecord", "", CFGFLAG_SERVER, Con_ServerDummy, this, "Stop recording"); + + m_pConsole->Register("quit", "", CFGFLAG_CLIENT, Con_Quit, this, "Quit Teeworlds"); + m_pConsole->Register("exit", "", CFGFLAG_CLIENT, Con_Quit, this, "Quit Teeworlds"); + m_pConsole->Register("minimize", "", CFGFLAG_CLIENT, Con_Minimize, this, "Minimize Teeworlds"); + m_pConsole->Register("connect", "s", CFGFLAG_CLIENT, Con_Connect, this, "Connect to the specified host/ip"); + m_pConsole->Register("disconnect", "", CFGFLAG_CLIENT, Con_Disconnect, this, "Disconnect from the server"); + m_pConsole->Register("ping", "", CFGFLAG_CLIENT, Con_Ping, this, "Ping the current server"); + m_pConsole->Register("screenshot", "", CFGFLAG_CLIENT, Con_Screenshot, this, "Take a screenshot"); + m_pConsole->Register("rcon", "r", CFGFLAG_CLIENT, Con_Rcon, this, "Send specified command to rcon"); + m_pConsole->Register("rcon_auth", "s", CFGFLAG_CLIENT, Con_RconAuth, this, "Authenticate to rcon"); + m_pConsole->Register("play", "r", CFGFLAG_CLIENT, Con_Play, this, "Play the file specified"); + m_pConsole->Register("record", "s", CFGFLAG_CLIENT, Con_Record, this, "Record to the file"); + m_pConsole->Register("stoprecord", "", CFGFLAG_CLIENT, Con_StopRecord, this, "Stop recording"); + + m_pConsole->Register("add_favorite", "s", CFGFLAG_CLIENT, Con_AddFavorite, this, "Add a server as a favorite"); +} + +static CClient m_Client; + +/* + Server Time + Client Mirror Time + Client Predicted Time + + Snapshot Latency + Downstream latency + + Prediction Latency + Upstream latency +*/ + +#if defined(CONF_PLATFORM_MACOSX) +int SDL_main(int argc, const char **argv) // ignore_convention +#else +int main(int argc, const char **argv) // ignore_convention +#endif +{ + // init the engine + dbg_msg("client", "starting..."); + m_Client.InitEngine("Teeworlds"); + + IKernel *pKernel = IKernel::Create(); + pKernel->RegisterInterface(&m_Client); + m_Client.RegisterInterfaces(); + + // create the components + IConsole *pConsole = CreateConsole(); + IStorage *pStorage = CreateStorage("Teeworlds", argv[0]); // ignore_convention + IConfig *pConfig = CreateConfig(); + IEngineGraphics *pEngineGraphics = CreateEngineGraphics(); + IEngineSound *pEngineSound = CreateEngineSound(); + IEngineInput *pEngineInput = CreateEngineInput(); + IEngineTextRender *pEngineTextRender = CreateEngineTextRender(); + IEngineMap *pEngineMap = CreateEngineMap(); + IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer(); + + { + bool RegisterFail = false; + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IConsole*>(pConsole)); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IConfig*>(pConfig)); + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineGraphics*>(pEngineGraphics)); // register graphics as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IGraphics*>(pEngineGraphics)); + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineSound*>(pEngineSound)); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<ISound*>(pEngineSound)); + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineInput*>(pEngineInput)); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IInput*>(pEngineInput)); + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineTextRender*>(pEngineTextRender)); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<ITextRender*>(pEngineTextRender)); + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMap*>(pEngineMap)); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMap*>(pEngineMap)); + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMasterServer*>(pEngineMasterServer)); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMasterServer*>(pEngineMasterServer)); + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateEditor()); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateGameClient()); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage); + + if(RegisterFail) + return -1; + } + + pConfig->Init(); + pEngineMasterServer->Init(m_Client.Engine()); + pEngineMasterServer->Load(); + + // register all console commands + m_Client.RegisterCommands(); + + pKernel->RequestInterface<IGameClient>()->OnConsoleInit(); + + // init client's interfaces + m_Client.InitInterfaces(); + + // execute autoexec file + pConsole->ExecuteFile("autoexec.cfg"); + + // parse the command line arguments + if(argc > 1) // ignore_convention + pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention + + // execute config file + pConsole->ExecuteFile("settings.cfg"); + + // run the client + m_Client.Run(); + + // write down the config and quit + pConfig->Save(); + + return 0; +} diff --git a/src/engine/client/client.h b/src/engine/client/client.h index 64ef6d9b..c7174f85 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -1,17 +1,291 @@ +#ifndef ENGINE_CLIENT_CLIENT_H +#define ENGINE_CLIENT_CLIENT_H -class IEngine +#include <engine/console.h> +#include <engine/editor.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/client.h> +#include <engine/config.h> +#include <engine/serverbrowser.h> +#include <engine/sound.h> +#include <engine/input.h> +#include <engine/keys.h> +#include <engine/map.h> +#include <engine/masterserver.h> +#include <engine/storage.h> + +#include <engine/shared/engine.h> +#include <engine/shared/protocol.h> +#include <engine/shared/demorec.h> +#include <engine/shared/network.h> + +#include "srvbrowse.h" + +class CGraph { public: - virtual ~IEngine() {} - virtual class IGraphics *Graphics() = 0; + enum + { + // restrictions: Must be power of two + MAX_VALUES=128, + }; + + float m_Min, m_Max; + float m_aValues[MAX_VALUES]; + float m_aColors[MAX_VALUES][3]; + int m_Index; + + void Init(float Min, float Max); + + void ScaleMax(); + void ScaleMin(); + + void Add(float v, float r, float g, float b); + void Render(IGraphics *pGraphics, int Font, float x, float y, float w, float h, const char *pDescription); }; -class IGameClient +class CSmoothTime { + int64 m_Snap; + int64 m_Current; + int64 m_Target; + + int64 m_RLast; + int64 m_TLast; + CGraph m_Graph; + + int m_SpikeCounter; + + float m_aAdjustSpeed[2]; // 0 = down, 1 = up public: - virtual ~IGameClient() {} + void Init(int64 Target); + void SetAdjustSpeed(int Direction, float Value); + + int64 Get(int64 Now); + + void UpdateInt(int64 Target); + void Update(CGraph *pGraph, int64 Target, int TimeLeft, int AdjustDirection); }; -extern IGameClient *CreateGameClient(IEngine *pEngine); + +class CClient : public IClient, public CDemoPlayer::IListner +{ + // needed interfaces + IEditor *m_pEditor; + IEngineInput *m_pInput; + IEngineGraphics *m_pGraphics; + IEngineSound *m_pSound; + IGameClient *m_pGameClient; + IEngineMap *m_pMap; + IConsole *m_pConsole; + IStorage *m_pStorage; + IEngineMasterServer *m_pMasterServer; + + enum + { + NUM_SNAPSHOT_TYPES=2, + PREDICTION_MARGIN=1000/50/2, // magic network prediction value + }; + + CNetClient m_NetClient; + CDemoPlayer m_DemoPlayer; + CDemoRecorder m_DemoRecorder; + CEngine m_Engine; + CServerBrowser m_ServerBrowser; + + char m_aServerAddressStr[256]; + + unsigned m_SnapshotParts; + int64 m_LocalStartTime; + + int m_DebugFont; + float m_FrameTimeLow; + float m_FrameTimeHigh; + int m_Frames; + NETADDR m_ServerAddress; + int m_WindowMustRefocus; + int m_SnapCrcErrors; + + int m_AckGameTick; + int m_CurrentRecvTick; + int m_RconAuthed; + + // version-checking + char m_aVersionStr[10]; + + // pinging + int64 m_PingStartTime; + + // + char m_aCurrentMap[256]; + int m_CurrentMapCrc; + + // + char m_aCmdConnect[256]; + + // map download + char m_aMapdownloadFilename[256]; + char m_aMapdownloadName[256]; + IOHANDLE m_MapdownloadFile; + int m_MapdownloadChunk; + int m_MapdownloadCrc; + int m_MapdownloadAmount; + int m_MapdownloadTotalsize; + + // time + CSmoothTime m_GameTime; + CSmoothTime m_PredictedTime; + + // input + struct // TODO: handle input better + { + int m_aData[MAX_INPUT_SIZE]; // the input data + int m_Tick; // the tick that the input is for + int64 m_PredictedTime; // prediction latency when we sent this input + int64 m_Time; + } m_aInputs[200]; + + int m_CurrentInput; + + // graphs + CGraph m_InputtimeMarginGraph; + CGraph m_GametimeMarginGraph; + CGraph m_FpsGraph; + + // the game snapshots are modifiable by the game + CSnapshotStorage m_SnapshotStorage; + CSnapshotStorage::CHolder *m_aSnapshots[NUM_SNAPSHOT_TYPES]; + + int m_RecivedSnapshots; + char m_aSnapshotIncommingData[CSnapshot::MAX_SIZE]; + + CSnapshotStorage::CHolder m_aDemorecSnapshotHolders[NUM_SNAPSHOT_TYPES]; + char *m_aDemorecSnapshotData[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE]; + + CSnapshotDelta m_SnapshotDelta; + + // + CServerInfo m_CurrentServerInfo; + int64 m_CurrentServerInfoRequestTime; // >= 0 should request, == -1 got info + + // version info + struct + { + int m_State; + CHostLookup m_VersionServeraddr; + } m_VersionInfo; +public: + IEngineGraphics *Graphics() { return m_pGraphics; } + IEngineInput *Input() { return m_pInput; } + IEngineSound *Sound() { return m_pSound; } + IGameClient *GameClient() { return m_pGameClient; } + IEngineMasterServer *MasterServer() { return m_pMasterServer; } + IStorage *Storage() { return m_pStorage; } + + CClient(); + + // ----- send functions ----- + virtual int SendMsg(CMsgPacker *pMsg, int Flags); + + int SendMsgEx(CMsgPacker *pMsg, int Flags, bool System=true); + void SendInfo(); + void SendEnterGame(); + void SendReady(); + + virtual bool RconAuthed(); + void RconAuth(const char *pName, const char *pPassword); + virtual void Rcon(const char *pCmd); + + virtual bool ConnectionProblems(); + + void DirectInput(int *pInput, int Size); + void SendInput(); + + // TODO: OPT: do this alot smarter! + virtual int *GetInput(int Tick); + + const char *LatestVersion(); + void VersionUpdate(); + + // ------ state handling ----- + void SetState(int s); + + // called when the map is loaded and we should init for a new round + void OnEnterGame(); + virtual void EnterGame(); + + virtual void Connect(const char *pAddress); + void DisconnectWithReason(const char *pReason); + virtual void Disconnect(); + + + virtual void GetServerInfo(CServerInfo *pServerInfo); + void ServerInfoRequest(); + + int LoadData(); + + // --- + + void *SnapGetItem(int SnapId, int Index, CSnapItem *pItem); + void SnapInvalidateItem(int SnapId, int Index); + void *SnapFindItem(int SnapId, int Type, int Id); + int SnapNumItems(int SnapId); + void SnapSetStaticsize(int ItemType, int Size); + + void Render(); + void DebugRender(); + + virtual void Quit(); + + virtual const char *ErrorString(); + + const char *LoadMap(const char *pName, const char *pFilename, unsigned WantedCrc); + const char *LoadMapSearch(const char *pMapName, int WantedCrc); + + static int PlayerScoreComp(const void *a, const void *b); + + void ProcessPacket(CNetChunk *pPacket); + + virtual int MapDownloadAmount() { return m_MapdownloadAmount; } + virtual int MapDownloadTotalsize() { return m_MapdownloadTotalsize; } + + void PumpNetwork(); + + virtual void OnDemoPlayerSnapshot(void *pData, int Size); + virtual void OnDemoPlayerMessage(void *pData, int Size); + + void Update(); + + virtual const char *UserDirectory(); + + void InitEngine(const char *pAppname); + void RegisterInterfaces(); + void InitInterfaces(); + + void Run(); + + + static void Con_Connect(IConsole::IResult *pResult, void *pUserData); + static void Con_Disconnect(IConsole::IResult *pResult, void *pUserData); + static void Con_Quit(IConsole::IResult *pResult, void *pUserData); + static void Con_Minimize(IConsole::IResult *pResult, void *pUserData); + static void Con_Ping(IConsole::IResult *pResult, void *pUserData); + static void Con_Screenshot(IConsole::IResult *pResult, void *pUserData); + static void Con_Rcon(IConsole::IResult *pResult, void *pUserData); + static void Con_RconAuth(IConsole::IResult *pResult, void *pUserData); + static void Con_AddFavorite(IConsole::IResult *pResult, void *pUserData); + static void Con_Play(IConsole::IResult *pResult, void *pUserData); + static void Con_Record(IConsole::IResult *pResult, void *pUserData); + static void Con_StopRecord(IConsole::IResult *pResult, void *pUserData); + static void Con_ServerDummy(IConsole::IResult *pResult, void *pUserData); + + void RegisterCommands(); + + const char *DemoPlayer_Play(const char *pFilename); + + virtual class CEngine *Engine() { return &m_Engine; } +}; +#endif diff --git a/src/engine/client/ec_client.cpp b/src/engine/client/ec_client.cpp deleted file mode 100644 index 7ba0a2bb..00000000 --- a/src/engine/client/ec_client.cpp +++ /dev/null @@ -1,2087 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <string.h> -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <math.h> - -#include <base/system.h> -#include <engine/e_engine.h> -#include <engine/e_client_interface.h> - -#include <engine/e_protocol.h> -#include <engine/e_snapshot.h> -#include <engine/e_compression.h> -#include <engine/e_network.h> -#include <engine/e_config.h> -#include <engine/e_packer.h> -#include <engine/e_memheap.h> -#include <engine/e_datafile.h> -#include <engine/e_console.h> -#include <engine/e_ringbuffer.h> - -#include <engine/e_huffman.h> - -#include <engine/e_demorec.h> - -#include <mastersrv/mastersrv.h> -#include <versionsrv/versionsrv.h> - -#include "editor.h" -#include "graphics.h" -#include "client.h" - -static IEditor *m_pEditor = 0; -static IEngineGraphics *m_pGraphics = 0; -IEngineGraphics *Graphics() { return m_pGraphics; } - -static IGameClient *m_pGameClient = 0; - - -class CClient : public IEngine -{ -public: - virtual class IGraphics *Graphics() - { - return m_pGraphics; - } -}; - -static CClient m_Client; - -const int prediction_margin = 1000/50/2; /* magic network prediction value */ - -/* - Server Time - Client Mirror Time - Client Predicted Time - - Snapshot Latency - Downstream latency - - Prediction Latency - Upstream latency -*/ - -/* network client, must be accessible from other parts like the server browser */ -CNetClient m_NetClient; - -/* TODO: ugly, fix me */ -extern void client_serverbrowse_set(NETADDR *addr, int request, int token, SERVER_INFO *info); -extern void client_serverbrowse_save(); - -static unsigned snapshot_parts; -static int64 local_start_time; - -static int debug_font; -static float frametime = 0.0001f; -static float frametime_low = 1.0f; -static float frametime_high = 0.0f; -static int frames = 0; -static NETADDR server_address; -static int window_must_refocus = 0; -static int snapcrcerrors = 0; - -static int ack_game_tick = -1; -static int current_recv_tick = 0; -static int rcon_authed = 0; - -/* version-checking */ -static char versionstr[10] = "0"; - -/* pinging */ -static int64 ping_start_time = 0; - -/* */ -static char current_map[256] = {0}; -static int current_map_crc = 0; - -/* */ -static char cmd_connect[256] = {0}; - -/* map download */ -static char mapdownload_filename[256] = {0}; -static char mapdownload_name[256] = {0}; -static IOHANDLE mapdownload_file = 0; -static int mapdownload_chunk = 0; -static int mapdownload_crc = 0; -static int mapdownload_amount = -1; -static int mapdownload_totalsize = -1; - -/* */ -static SERVER_INFO current_server_info = {0}; -static int64 current_server_info_requesttime = -1; /* >= 0 should request, == -1 got info */ - -/* current time */ -static int current_tick = 0; -static float intratick = 0; -static float ticktime = 0; -static int prev_tick = 0; - -/* */ -/*static int predictiontime_pingspikecounter = 0; -static int gametime_pingspikecounter = 0;*/ - -/* predicted time */ -static int current_predtick = 0; -static float predintratick = 0; -static int last_input_timeleft = 0; - -static struct /* TODO: handle input better */ -{ - int data[MAX_INPUT_SIZE]; /* the input data */ - int tick; /* the tick that the input is for */ - int64 predicted_time; /* prediction latency when we sent this input */ - int64 time; -} inputs[200]; -static int current_input = 0; - -enum -{ - GRAPH_MAX=128 -}; - -typedef struct -{ - float min, max; - float values[GRAPH_MAX]; - float colors[GRAPH_MAX][3]; - int index; -} GRAPH; - -static void graph_init(GRAPH *g, float min, float max) -{ - g->min = min; - g->max = max; - g->index = 0; -} - -static void graph_scale_max(GRAPH *g) -{ - int i = 0; - g->max = 0; - for(i = 0; i < GRAPH_MAX; i++) - { - if(g->values[i] > g->max) - g->max = g->values[i]; - } -} - -static void graph_scale_min(GRAPH *g) -{ - int i = 0; - g->min = g->max; - for(i = 0; i < GRAPH_MAX; i++) - { - if(g->values[i] < g->min) - g->min = g->values[i]; - } -} - -static void graph_add(GRAPH *graph, float v, float r, float g, float b) -{ - graph->index = (graph->index+1)&(GRAPH_MAX-1); - graph->values[graph->index] = v; - graph->colors[graph->index][0] = r; - graph->colors[graph->index][1] = g; - graph->colors[graph->index][2] = b; -} - -static void graph_render(GRAPH *g, float x, float y, float w, float h, const char *description) -{ - char buf[32]; - int i; - - //m_pGraphics->BlendNormal(); - - - Graphics()->TextureSet(-1); - - m_pGraphics->QuadsBegin(); - Graphics()->SetColor(0, 0, 0, 0.75f); - Graphics()->QuadsDrawTL(x, y, w, h); - m_pGraphics->QuadsEnd(); - - Graphics()->LinesBegin(); - Graphics()->SetColor(0.95f, 0.95f, 0.95f, 1.00f); - Graphics()->LinesDraw(x, y+h/2, x+w, y+h/2); - Graphics()->SetColor(0.5f, 0.5f, 0.5f, 0.75f); - Graphics()->LinesDraw(x, y+(h*3)/4, x+w, y+(h*3)/4); - Graphics()->LinesDraw(x, y+h/4, x+w, y+h/4); - for(i = 1; i < GRAPH_MAX; i++) - { - float a0 = (i-1)/(float)GRAPH_MAX; - float a1 = i/(float)GRAPH_MAX; - int i0 = (g->index+i-1)&(GRAPH_MAX-1); - int i1 = (g->index+i)&(GRAPH_MAX-1); - - float v0 = (g->values[i0]-g->min) / (g->max-g->min); - float v1 = (g->values[i1]-g->min) / (g->max-g->min); - - Graphics()->SetColorVertex(0, g->colors[i0][0], g->colors[i0][1], g->colors[i0][2], 0.75f); - Graphics()->SetColorVertex(1, g->colors[i1][0], g->colors[i1][1], g->colors[i1][2], 0.75f); - Graphics()->LinesDraw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); - - } - Graphics()->LinesEnd(); - - - Graphics()->TextureSet(debug_font); - Graphics()->QuadsText(x+2, y+h-16, 16, 1,1,1,1, description); - - str_format(buf, sizeof(buf), "%.2f", g->max); - Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf); - - str_format(buf, sizeof(buf), "%.2f", g->min); - Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf); - -} - -typedef struct -{ - int64 snap; - int64 current; - int64 target; - - int64 rlast; - int64 tlast; - GRAPH graph; - - int spikecounter; - - float adjustspeed[2]; /* 0 = down, 1 = up */ -} SMOOTHTIME; - -static void st_init(SMOOTHTIME *st, int64 target) -{ - st->snap = time_get(); - st->current = target; - st->target = target; - st->adjustspeed[0] = 0.3f; - st->adjustspeed[1] = 0.3f; - graph_init(&st->graph, 0.0f, 0.5f); -} - -static int64 st_get(SMOOTHTIME *st, int64 now) -{ - float adjust_speed, a; - int64 c = st->current + (now - st->snap); - int64 t = st->target + (now - st->snap); - int64 r; - - /* it's faster to adjust upward instead of downward */ - /* we might need to adjust these abit */ - - adjust_speed = st->adjustspeed[0]; - if(t > c) - adjust_speed = st->adjustspeed[1]; - - a = ((now-st->snap)/(float)time_freq()) * adjust_speed; - if(a > 1.0f) - a = 1.0f; - - r = c + (int64)((t-c)*a); - - graph_add(&st->graph, a+0.5f,1,1,1); - - return r; -} - -static void st_update_int(SMOOTHTIME *st, int64 target) -{ - int64 now = time_get(); - st->current = st_get(st, now); - st->snap = now; - st->target = target; -} - -static void st_update(SMOOTHTIME *st, GRAPH *graph, int64 target, int time_left, int adjust_direction) -{ - int update_timer = 1; - - if(time_left < 0) - { - int is_spike = 0; - if(time_left < -50) - { - is_spike = 1; - - st->spikecounter += 5; - if(st->spikecounter > 50) - st->spikecounter = 50; - } - - if(is_spike && st->spikecounter < 15) - { - /* ignore this ping spike */ - update_timer = 0; - graph_add(graph, time_left, 1,1,0); - } - else - { - graph_add(graph, time_left, 1,0,0); - if(st->adjustspeed[adjust_direction] < 30.0f) - st->adjustspeed[adjust_direction] *= 2.0f; - } - } - else - { - if(st->spikecounter) - st->spikecounter--; - - graph_add(graph, time_left, 0,1,0); - - st->adjustspeed[adjust_direction] *= 0.95f; - if(st->adjustspeed[adjust_direction] < 2.0f) - st->adjustspeed[adjust_direction] = 2.0f; - } - - last_input_timeleft = time_left; - - if(update_timer) - st_update_int(st, target); -} - -static SMOOTHTIME game_time; -static SMOOTHTIME predicted_time; - -/* graphs */ -static GRAPH inputtime_margin_graph; -static GRAPH gametime_margin_graph; -static GRAPH fps_graph; - -/* -- snapshot handling --- */ -enum -{ - NUM_SNAPSHOT_TYPES=2 -}; - -/* the game snapshots are modifiable by the game */ -CSnapshotStorage snapshot_storage; -static CSnapshotStorage::CHolder *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0}; - -static int recived_snapshots = 0; -static char snapshot_incomming_data[CSnapshot::MAX_SIZE]; - -static CSnapshotStorage::CHolder demorec_snapshotholders[NUM_SNAPSHOT_TYPES]; -static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE]; - -/* --- */ - -void *snap_get_item(int snapid, int index, SNAP_ITEM *item) -{ - CSnapshotItem *i; - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshots[snapid]->m_pAltSnap->GetItem(index); - item->datasize = snapshots[snapid]->m_pAltSnap->GetItemSize(index); - item->type = i->Type(); - item->id = i->ID(); - return (void *)i->Data(); -} - -void snap_invalidate_item(int snapid, int index) -{ - CSnapshotItem *i; - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshots[snapid]->m_pAltSnap->GetItem(index); - if(i) - { - if((char *)i < (char *)snapshots[snapid]->m_pAltSnap || (char *)i > (char *)snapshots[snapid]->m_pAltSnap + snapshots[snapid]->m_SnapSize) - dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); - if((char *)i >= (char *)snapshots[snapid]->m_pSnap && (char *)i < (char *)snapshots[snapid]->m_pSnap + snapshots[snapid]->m_SnapSize) - dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); - i->m_TypeAndID = -1; - } -} - -void *snap_find_item(int snapid, int type, int id) -{ - /* TODO: linear search. should be fixed. */ - int i; - - if(!snapshots[snapid]) - return 0x0; - - for(i = 0; i < snapshots[snapid]->m_pSnap->m_NumItems; i++) - { - CSnapshotItem *itm = snapshots[snapid]->m_pAltSnap->GetItem(i); - if(itm->Type() == type && itm->ID() == id) - return (void *)itm->Data(); - } - return 0x0; -} - -int snap_num_items(int snapid) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - if(!snapshots[snapid]) - return 0; - return snapshots[snapid]->m_pSnap->m_NumItems; -} - -/* ------ time functions ------ */ -float client_intratick() { return intratick; } -float client_predintratick() { return predintratick; } -float client_ticktime() { return ticktime; } -int client_tick() { return current_tick; } -int client_prevtick() { return prev_tick; } -int client_predtick() { return current_predtick; } -int client_tickspeed() { return SERVER_TICK_SPEED; } -float client_frametime() { return frametime; } -float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); } - -/* ----- send functions ----- */ -int client_send_msg() -{ - const MSG_INFO *pInfo = msg_get_info(); - CNetChunk Packet; - - if(!pInfo) - return -1; - - if(client_state() == CLIENTSTATE_OFFLINE) - return 0; - - mem_zero(&Packet, sizeof(CNetChunk)); - - Packet.m_ClientID = 0; - Packet.m_pData = pInfo->data; - Packet.m_DataSize = pInfo->size; - - if(pInfo->flags&MSGFLAG_VITAL) - Packet.m_Flags |= NETSENDFLAG_VITAL; - if(pInfo->flags&MSGFLAG_FLUSH) - Packet.m_Flags |= NETSENDFLAG_FLUSH; - - if(pInfo->flags&MSGFLAG_RECORD) - { - if(demorec_isrecording()) - demorec_record_message(Packet.m_pData, Packet.m_DataSize); - } - - if(!(pInfo->flags&MSGFLAG_NOSEND)) - m_NetClient.Send(&Packet); - return 0; -} - -static void client_send_info() -{ - msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_string(modc_net_version(), 128); - msg_pack_string(config.player_name, 128); - msg_pack_string(config.clan_name, 128); - msg_pack_string(config.password, 128); - msg_pack_end(); - client_send_msg(); -} - - -static void client_send_entergame() -{ - msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_end(); - client_send_msg(); -} - -static void client_send_ready() -{ - msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_end(); - client_send_msg(); -} - -int client_rcon_authed() -{ - return rcon_authed; -} - -void client_rcon_auth(const char *name, const char *password) -{ - msg_pack_start_system(NETMSG_RCON_AUTH, MSGFLAG_VITAL); - msg_pack_string(name, 32); - msg_pack_string(password, 32); - msg_pack_end(); - client_send_msg(); -} - -void client_rcon(const char *cmd) -{ - msg_pack_start_system(NETMSG_RCON_CMD, MSGFLAG_VITAL); - msg_pack_string(cmd, 256); - msg_pack_end(); - client_send_msg(); -} - -int client_connection_problems() -{ - return m_NetClient.GotProblems(); -} - -void client_direct_input(int *input, int size) -{ - int i; - msg_pack_start_system(NETMSG_INPUT, 0); - msg_pack_int(ack_game_tick); - msg_pack_int(current_predtick); - msg_pack_int(size); - - for(i = 0; i < size/4; i++) - msg_pack_int(input[i]); - - msg_pack_end(); - client_send_msg(); -} - - -static void client_send_input() -{ - int64 now = time_get(); - int i, size; - - if(current_predtick <= 0) - return; - - /* fetch input */ - size = modc_snap_input(inputs[current_input].data); - - if(!size) - return; - - /* pack input */ - msg_pack_start_system(NETMSG_INPUT, MSGFLAG_FLUSH); - msg_pack_int(ack_game_tick); - msg_pack_int(current_predtick); - msg_pack_int(size); - - inputs[current_input].tick = current_predtick; - inputs[current_input].predicted_time = st_get(&predicted_time, now); - inputs[current_input].time = now; - - /* pack it */ - for(i = 0; i < size/4; i++) - msg_pack_int(inputs[current_input].data[i]); - - current_input++; - current_input%=200; - - msg_pack_end(); - client_send_msg(); -} - -const char *client_latestversion() -{ - return versionstr; -} - -/* TODO: OPT: do this alot smarter! */ -int *client_get_input(int tick) -{ - int i; - int best = -1; - for(i = 0; i < 200; i++) - { - if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick)) - best = i; - } - - if(best != -1) - return (int *)inputs[best].data; - return 0; -} - -/* ------ state handling ----- */ -static int state = CLIENTSTATE_OFFLINE; -int client_state() { return state; } -static void client_set_state(int s) -{ - int old = state; - if(config.debug) - dbg_msg("client", "state change. last=%d current=%d", state, s); - state = s; - if(old != s) - modc_statechange(state, old); -} - -/* called when the map is loaded and we should init for a new round */ -static void client_on_enter_game() -{ - /* reset input */ - int i; - for(i = 0; i < 200; i++) - inputs[i].tick = -1; - current_input = 0; - - /* reset snapshots */ - snapshots[SNAP_CURRENT] = 0; - snapshots[SNAP_PREV] = 0; - snapshot_storage.PurgeAll(); - recived_snapshots = 0; - snapshot_parts = 0; - current_predtick = 0; - current_recv_tick = 0; -} - -void client_entergame() -{ - if(state == CLIENTSTATE_DEMOPLAYBACK) - return; - - /* now we will wait for two snapshots */ - /* to finish the connection */ - client_send_entergame(); - client_on_enter_game(); -} - -void client_connect(const char *server_address_str) -{ - char buf[512]; - const char *port_str = 0; - int k; - int port = 8303; - - client_disconnect(); - - dbg_msg("client", "connecting to '%s'", server_address_str); - - //client_serverinfo_request(); - str_copy(buf, server_address_str, sizeof(buf)); - - for(k = 0; buf[k]; k++) - { - if(buf[k] == ':') - { - port_str = &(buf[k+1]); - buf[k] = 0; - break; - } - } - - if(port_str) - port = atoi(port_str); - - /* TODO: IPv6 support */ - if(net_host_lookup(buf, &server_address, NETTYPE_IPV4) != 0) - dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); - - rcon_authed = 0; - server_address.port = port; - m_NetClient.Connect(&server_address); - client_set_state(CLIENTSTATE_CONNECTING); - - graph_init(&inputtime_margin_graph, -150.0f, 150.0f); - graph_init(&gametime_margin_graph, -150.0f, 150.0f); -} - -void client_disconnect_with_reason(const char *reason) -{ - /* stop demo playback */ - demorec_playback_stop(); - - /* */ - rcon_authed = 0; - m_NetClient.Disconnect(reason); - client_set_state(CLIENTSTATE_OFFLINE); - map_unload(); - - /* disable all downloads */ - mapdownload_chunk = 0; - if(mapdownload_file) - io_close(mapdownload_file); - mapdownload_file = 0; - mapdownload_crc = 0; - mapdownload_totalsize = -1; - mapdownload_amount = 0; - - /* clear the current server info */ - mem_zero(¤t_server_info, sizeof(current_server_info)); - mem_zero(&server_address, sizeof(server_address)); - - /* clear snapshots */ - snapshots[SNAP_CURRENT] = 0; - snapshots[SNAP_PREV] = 0; - recived_snapshots = 0; -} - -void client_disconnect() -{ - client_disconnect_with_reason(0); -} - - -void client_serverinfo(SERVER_INFO *serverinfo) -{ - mem_copy(serverinfo, ¤t_server_info, sizeof(current_server_info)); -} - -void client_serverinfo_request() -{ - mem_zero(¤t_server_info, sizeof(current_server_info)); - current_server_info_requesttime = 0; -} - -static int client_load_data() -{ - debug_font = Graphics()->LoadTexture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE); - return 1; -} - -extern int snapshot_data_rate[0xffff]; -extern int snapshot_data_updates[0xffff]; - -const char *modc_getitemname(int type); - -static void client_debug_render() -{ - static NETSTATS prev, current; - static int64 last_snap = 0; - static float frametime_avg = 0; - int64 now = time_get(); - char buffer[512]; - - if(!config.debug) - return; - - //m_pGraphics->BlendNormal(); - Graphics()->TextureSet(debug_font); - Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight()); - - if(time_get()-last_snap > time_freq()) - { - last_snap = time_get(); - prev = current; - net_stats(¤t); - } - - /* - eth = 14 - ip = 20 - udp = 8 - total = 42 - */ - frametime_avg = frametime_avg*0.9f + frametime*0.1f; - str_format(buffer, sizeof(buffer), "ticks: %8d %8d mem %dk %d gfxmem: N/A fps: %3d", - current_tick, current_predtick, - mem_stats()->allocated/1024, - mem_stats()->total_allocations, - /*gfx_memory_usage()/1024, */ // TODO: Refactor: Reenable this - (int)(1.0f/frametime_avg)); - Graphics()->QuadsText(2, 2, 16, 1,1,1,1, buffer); - - - { - int send_packets = (current.sent_packets-prev.sent_packets); - int send_bytes = (current.sent_bytes-prev.sent_bytes); - int send_total = send_bytes + send_packets*42; - int recv_packets = (current.recv_packets-prev.recv_packets); - int recv_bytes = (current.recv_bytes-prev.recv_bytes); - int recv_total = recv_bytes + recv_packets*42; - - if(!send_packets) send_packets++; - if(!recv_packets) recv_packets++; - str_format(buffer, sizeof(buffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d", - send_packets, send_bytes, send_packets*42, send_total, (send_total*8)/1024, send_bytes/send_packets, - recv_packets, recv_bytes, recv_packets*42, recv_total, (recv_total*8)/1024, recv_bytes/recv_packets); - Graphics()->QuadsText(2, 14, 16, 1,1,1,1, buffer); - } - - /* render rates */ - { - int y = 0; - int i; - for(i = 0; i < 256; i++) - { - if(snapshot_data_rate[i]) - { - str_format(buffer, sizeof(buffer), "%4d %20s: %8d %8d %8d", i, modc_getitemname(i), snapshot_data_rate[i]/8, snapshot_data_updates[i], - (snapshot_data_rate[i]/snapshot_data_updates[i])/8); - Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, buffer); - y++; - } - } - } - - str_format(buffer, sizeof(buffer), "pred: %d ms %3.2f", - (int)((st_get(&predicted_time, now)-st_get(&game_time, now))*1000/(float)time_freq()), - predicted_time.adjustspeed[1]); - Graphics()->QuadsText(2, 70, 16, 1,1,1,1, buffer); - - /* render graphs */ - if(config.dbg_graphs) - { - //Graphics()->MapScreen(0,0,400.0f,300.0f); - float w = Graphics()->ScreenWidth()/4.0f; - float h = Graphics()->ScreenHeight()/6.0f; - float sp = Graphics()->ScreenWidth()/100.0f; - float x = Graphics()->ScreenWidth()-w-sp; - - graph_scale_max(&fps_graph); - graph_scale_min(&fps_graph); - graph_render(&fps_graph, x, sp*5, w, h, "FPS"); - graph_render(&inputtime_margin_graph, x, sp*5+h+sp, w, h, "Prediction Margin"); - graph_render(&gametime_margin_graph, x, sp*5+h+sp+h+sp, w, h, "Gametime Margin"); - } -} - -void client_quit() -{ - client_set_state(CLIENTSTATE_QUITING); -} - -const char *client_error_string() -{ - return m_NetClient.ErrorString(); -} - -static void client_render() -{ - if(config.gfx_clear) - Graphics()->Clear(1,1,0); - - modc_render(); - client_debug_render(); -} - -static const char *client_load_map(const char *name, const char *filename, int wanted_crc) -{ - static char errormsg[128]; - DATAFILE *df; - int crc; - - client_set_state(CLIENTSTATE_LOADING); - - df = datafile_load(filename); - if(!df) - { - str_format(errormsg, sizeof(errormsg), "map '%s' not found", filename); - return errormsg; - } - - /* get the crc of the map */ - crc = datafile_crc(filename); - if(crc != wanted_crc) - { - datafile_unload(df); - str_format(errormsg, sizeof(errormsg), "map differs from the server. %08x != %08x", crc, wanted_crc); - return errormsg; - } - - // stop demo recording if we loaded a new map - demorec_record_stop(); - - dbg_msg("client", "loaded map '%s'", filename); - recived_snapshots = 0; - map_set(df); - - str_copy(current_map, name, sizeof(current_map)); - current_map_crc = crc; - - return NULL; -} - -static const char *client_load_map_search(const char *mapname, int wanted_crc) -{ - const char *error = 0; - char buf[512]; - dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc); - client_set_state(CLIENTSTATE_LOADING); - - /* try the normal maps folder */ - str_format(buf, sizeof(buf), "maps/%s.map", mapname); - error = client_load_map(mapname, buf, wanted_crc); - if(!error) - return error; - - /* try the downloaded maps */ - str_format(buf, sizeof(buf), "downloadedmaps/%s_%8x.map", mapname, wanted_crc); - error = client_load_map(mapname, buf, wanted_crc); - return error; -} - -static int player_score_comp(const void *a, const void *b) -{ - SERVER_INFO_PLAYER *p0 = (SERVER_INFO_PLAYER *)a; - SERVER_INFO_PLAYER *p1 = (SERVER_INFO_PLAYER *)b; - if(p0->score == p1->score) - return 0; - if(p0->score < p1->score) - return 1; - return -1; -} - -static void client_process_packet(CNetChunk *pPacket) -{ - if(pPacket->m_ClientID == -1) - { - /* connectionlesss */ - if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && - memcmp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) - { - unsigned char *versiondata = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION); - int version_match = !memcmp(versiondata, VERSION_DATA, sizeof(VERSION_DATA)); - - dbg_msg("client/version", "version does %s (%d.%d.%d)", - version_match ? "match" : "NOT match", - versiondata[1], versiondata[2], versiondata[3]); - - /* assume version is out of date when version-data doesn't match */ - if (!version_match) - { - sprintf(versionstr, "%d.%d.%d", versiondata[1], versiondata[2], versiondata[3]); - } - } - - if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) && - memcmp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) - { - int size = pPacket->m_DataSize-sizeof(SERVERBROWSE_LIST); - int num = size/sizeof(MASTERSRV_ADDR); - MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)pPacket->m_pData+sizeof(SERVERBROWSE_LIST)); - int i; - - for(i = 0; i < num; i++) - { - NETADDR addr; - - /* convert address */ - mem_zero(&addr, sizeof(addr)); - addr.type = NETTYPE_IPV4; - addr.ip[0] = addrs[i].ip[0]; - addr.ip[1] = addrs[i].ip[1]; - addr.ip[2] = addrs[i].ip[2]; - addr.ip[3] = addrs[i].ip[3]; - addr.port = (addrs[i].port[1]<<8) | addrs[i].port[0]; - - client_serverbrowse_set(&addr, BROWSESET_MASTER_ADD, -1, NULL); - } - } - - { - int packet_type = 0; - if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) - packet_type = 2; - - if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_OLD_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0) - packet_type = 1; - - if(packet_type) - { - /* we got ze info */ - CUnpacker up; - SERVER_INFO info = {0}; - int i; - int token = -1; - - up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO)); - if(packet_type >= 2) - token = atol(up.GetString()); - str_copy(info.version, up.GetString(), sizeof(info.version)); - str_copy(info.name, up.GetString(), sizeof(info.name)); - str_copy(info.map, up.GetString(), sizeof(info.map)); - str_copy(info.gametype, up.GetString(), sizeof(info.gametype)); - info.flags = atol(up.GetString()); - info.progression = atol(up.GetString()); - info.num_players = atol(up.GetString()); - info.max_players = atol(up.GetString()); - str_format(info.address, sizeof(info.address), "%d.%d.%d.%d:%d", - pPacket->m_Address.ip[0], pPacket->m_Address.ip[1], pPacket->m_Address.ip[2], - pPacket->m_Address.ip[3], pPacket->m_Address.port); - - for(i = 0; i < info.num_players; i++) - { - str_copy(info.players[i].name, up.GetString(), sizeof(info.players[i].name)); - info.players[i].score = atol(up.GetString()); - } - - if(!up.Error()) - { - /* sort players */ - qsort(info.players, info.num_players, sizeof(*info.players), player_score_comp); - - if(net_addr_comp(&server_address, &pPacket->m_Address) == 0) - { - mem_copy(¤t_server_info, &info, sizeof(current_server_info)); - current_server_info.netaddr = server_address; - current_server_info_requesttime = -1; - } - else - { - if(packet_type == 2) - client_serverbrowse_set(&pPacket->m_Address, BROWSESET_TOKEN, token, &info); - else - client_serverbrowse_set(&pPacket->m_Address, BROWSESET_OLD_INTERNET, -1, &info); - } - } - } - } - } - else - { - int sys; - int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys); - - if(sys) - { - /* system message */ - if(msg == NETMSG_MAP_CHANGE) - { - const char *map = msg_unpack_string(); - int map_crc = msg_unpack_int(); - const char *error = 0; - int i; - - if(msg_unpack_error()) - return; - - for(i = 0; map[i]; i++) /* protect the player from nasty map names */ - { - if(map[i] == '/' || map[i] == '\\') - error = "strange character in map name"; - } - - if(error) - client_disconnect_with_reason(error); - else - { - error = client_load_map_search(map, map_crc); - - if(!error) - { - dbg_msg("client/network", "loading done"); - client_send_ready(); - modc_connected(); - } - else - { - str_format(mapdownload_filename, sizeof(mapdownload_filename), "downloadedmaps/%s_%08x.map", map, map_crc); - - dbg_msg("client/network", "starting to download map to '%s'", mapdownload_filename); - - mapdownload_chunk = 0; - str_copy(mapdownload_name, map, sizeof(mapdownload_name)); - mapdownload_file = engine_openfile(mapdownload_filename, IOFLAG_WRITE); - mapdownload_crc = map_crc; - mapdownload_totalsize = -1; - mapdownload_amount = 0; - - msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_int(mapdownload_chunk); - msg_pack_end(); - client_send_msg(); - - if(config.debug) - dbg_msg("client/network", "requested chunk %d", mapdownload_chunk); - } - } - } - else if(msg == NETMSG_MAP_DATA) - { - int last = msg_unpack_int(); - int total_size = msg_unpack_int(); - int size = msg_unpack_int(); - const unsigned char *data = msg_unpack_raw(size); - - /* check fior errors */ - if(msg_unpack_error() || size <= 0 || total_size <= 0 || !mapdownload_file) - return; - - io_write(mapdownload_file, data, size); - - mapdownload_totalsize = total_size; - mapdownload_amount += size; - - if(last) - { - const char *error; - dbg_msg("client/network", "download complete, loading map"); - - io_close(mapdownload_file); - mapdownload_file = 0; - mapdownload_amount = 0; - mapdownload_totalsize = -1; - - /* load map */ - error = client_load_map(mapdownload_name, mapdownload_filename, mapdownload_crc); - if(!error) - { - dbg_msg("client/network", "loading done"); - client_send_ready(); - modc_connected(); - } - else - client_disconnect_with_reason(error); - } - else - { - /* request new chunk */ - mapdownload_chunk++; - msg_pack_start_system(NETMSG_REQUEST_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_int(mapdownload_chunk); - msg_pack_end(); - client_send_msg(); - - if(config.debug) - dbg_msg("client/network", "requested chunk %d", mapdownload_chunk); - } - } - else if(msg == NETMSG_PING) - { - msg_pack_start_system(NETMSG_PING_REPLY, 0); - msg_pack_end(); - client_send_msg(); - } - else if(msg == NETMSG_RCON_AUTH_STATUS) - { - int result = msg_unpack_int(); - if(msg_unpack_error() == 0) - rcon_authed = result; - } - else if(msg == NETMSG_RCON_LINE) - { - const char *line = msg_unpack_string(); - if(msg_unpack_error() == 0) - { - /*dbg_msg("remote", "%s", line);*/ - modc_rcon_line(line); - } - } - else if(msg == NETMSG_PING_REPLY) - dbg_msg("client/network", "latency %.2f", (time_get() - ping_start_time)*1000 / (float)time_freq()); - else if(msg == NETMSG_INPUTTIMING) - { - int input_predtick = msg_unpack_int(); - int time_left = msg_unpack_int(); - - /* adjust our prediction time */ - int k; - int64 target = 0; - for(k = 0; k < 200; k++) - { - if(inputs[k].tick == input_predtick) - { - target = inputs[k].predicted_time + (time_get() - inputs[k].time); - target = target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq()); - //st_update(&predicted_time, ); - break; - } - } - - if(target) - st_update(&predicted_time, &inputtime_margin_graph, target, time_left, 1); - } - else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY) - { - /*dbg_msg("client/network", "got snapshot"); */ - int num_parts = 1; - int part = 0; - int game_tick = msg_unpack_int(); - int delta_tick = game_tick-msg_unpack_int(); - int part_size = 0; - int crc = 0; - int complete_size = 0; - const char *data = 0; - - /* we are not allowed to process snapshot yet */ - if(client_state() < CLIENTSTATE_LOADING) - return; - - if(msg == NETMSG_SNAP) - { - num_parts = msg_unpack_int(); - part = msg_unpack_int(); - } - - if(msg != NETMSG_SNAPEMPTY) - { - crc = msg_unpack_int(); - part_size = msg_unpack_int(); - } - - data = (const char *)msg_unpack_raw(part_size); - - if(msg_unpack_error()) - return; - - if(game_tick >= current_recv_tick) - { - if(game_tick != current_recv_tick) - { - snapshot_parts = 0; - current_recv_tick = game_tick; - } - - /* TODO: clean this up abit */ - mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, data, part_size); - snapshot_parts |= 1<<part; - - if(snapshot_parts == (unsigned)((1<<num_parts)-1)) - { - static CSnapshot emptysnap; - CSnapshot *deltashot = &emptysnap; - int purgetick; - void *deltadata; - int deltasize; - unsigned char tmpbuffer2[CSnapshot::MAX_SIZE]; - unsigned char tmpbuffer3[CSnapshot::MAX_SIZE]; - int snapsize; - - complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size; - - /* reset snapshoting */ - snapshot_parts = 0; - - /* find snapshot that we should use as delta */ - emptysnap.m_DataSize = 0; - emptysnap.m_NumItems = 0; - - /* find delta */ - if(delta_tick >= 0) - { - int deltashot_size = snapshot_storage.Get(delta_tick, 0, &deltashot, 0); - - if(deltashot_size < 0) - { - /* couldn't find the delta snapshots that the server used */ - /* to compress this snapshot. force the server to resync */ - if(config.debug) - dbg_msg("client", "error, couldn't find the delta snapshot"); - - /* ack snapshot */ - /* TODO: combine this with the input message */ - ack_game_tick = -1; - return; - } - } - - /* decompress snapshot */ - deltadata = CSnapshot::EmptyDelta(); - deltasize = sizeof(int)*3; - - if(complete_size) - { - int intsize = intpack_decompress(snapshot_incomming_data, complete_size, tmpbuffer2); - - if(intsize < 0) /* failure during decompression, bail */ - return; - - deltadata = tmpbuffer2; - deltasize = intsize; - } - - /* unpack delta */ - purgetick = delta_tick; - snapsize = CSnapshot::UnpackDelta(deltashot, (CSnapshot*)tmpbuffer3, deltadata, deltasize); - if(snapsize < 0) - { - dbg_msg("client", "delta unpack failed!"); - return; - } - - if(msg != NETMSG_SNAPEMPTY && ((CSnapshot*)tmpbuffer3)->Crc() != crc) - { - if(config.debug) - { - dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d", - snapcrcerrors, game_tick, crc, ((CSnapshot*)tmpbuffer3)->Crc(), complete_size, delta_tick); - } - - snapcrcerrors++; - if(snapcrcerrors > 10) - { - /* to many errors, send reset */ - ack_game_tick = -1; - client_send_input(); - snapcrcerrors = 0; - } - return; - } - else - { - if(snapcrcerrors) - snapcrcerrors--; - } - - /* purge old snapshots */ - purgetick = delta_tick; - if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->m_Tick < purgetick) - purgetick = snapshots[SNAP_PREV]->m_Tick; - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->m_Tick < purgetick) - purgetick = snapshots[SNAP_PREV]->m_Tick; - snapshot_storage.PurgeUntil(purgetick); - - /* add new */ - snapshot_storage.Add(game_tick, time_get(), snapsize, (CSnapshot*)tmpbuffer3, 1); - - /* add snapshot to demo */ - if(demorec_isrecording()) - { - - /* write tick marker */ - /* - DEMOREC_TICKMARKER marker; - marker.tick = game_tick; - swap_endian(&marker, sizeof(int), sizeof(marker)/sizeof(int)); - demorec_record_write("TICK", sizeof(marker), &marker); - demorec_record_write("SNAP", snapsize, tmpbuffer3); - */ - - /* write snapshot */ - demorec_record_snapshot(game_tick, tmpbuffer3, snapsize); - } - - /* apply snapshot, cycle pointers */ - recived_snapshots++; - - current_recv_tick = game_tick; - - /* we got two snapshots until we see us self as connected */ - if(recived_snapshots == 2) - { - /* start at 200ms and work from there */ - st_init(&predicted_time, game_tick*time_freq()/50); - predicted_time.adjustspeed[1] = 1000.0f; - st_init(&game_time, (game_tick-1)*time_freq()/50); - snapshots[SNAP_PREV] = snapshot_storage.m_pFirst; - snapshots[SNAP_CURRENT] = snapshot_storage.m_pLast; - local_start_time = time_get(); - client_set_state(CLIENTSTATE_ONLINE); - } - - /* adjust game time */ - { - int64 now = st_get(&game_time, time_get()); - int64 tickstart = game_tick*time_freq()/50; - int64 time_left = (tickstart-now)*1000 / time_freq(); - /*st_update(&game_time, (game_tick-1)*time_freq()/50);*/ - st_update(&game_time, &gametime_margin_graph, (game_tick-1)*time_freq()/50, time_left, 0); - } - - /* ack snapshot */ - ack_game_tick = game_tick; - } - } - } - } - else - { - /* game message */ - if(demorec_isrecording()) - demorec_record_message(pPacket->m_pData, pPacket->m_DataSize); - /* demorec_record_write("MESG", pPacket->data_size, ); */ - - modc_message(msg); - } - } -} - -int client_mapdownload_amount() { return mapdownload_amount; } -int client_mapdownload_totalsize() { return mapdownload_totalsize; } - -static void client_pump_network() -{ - - m_NetClient.Update(); - - if(client_state() != CLIENTSTATE_DEMOPLAYBACK) - { - /* check for errors */ - if(client_state() != CLIENTSTATE_OFFLINE && m_NetClient.State() == NETSTATE_OFFLINE) - { - client_set_state(CLIENTSTATE_OFFLINE); - client_disconnect(); - dbg_msg("client", "offline error='%s'", m_NetClient.ErrorString()); - } - - /* */ - if(client_state() == CLIENTSTATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE) - { - /* we switched to online */ - dbg_msg("client", "connected, sending info"); - client_set_state(CLIENTSTATE_LOADING); - client_send_info(); - } - } - - /* process packets */ - CNetChunk Packet; - while(m_NetClient.Recv(&Packet)) - client_process_packet(&Packet); -} - -static void client_democallback_snapshot(void *pData, int Size) -{ - /* update ticks, they could have changed */ - const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); - CSnapshotStorage::CHolder *temp; - current_tick = info->current_tick; - prev_tick = info->previous_tick; - - /* handle snapshots */ - temp = snapshots[SNAP_PREV]; - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = temp; - - mem_copy(snapshots[SNAP_CURRENT]->m_pSnap, pData, Size); - mem_copy(snapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size); - - modc_newsnapshot(); - /*modc_predict();*/ -} - -static void client_democallback_message(void *data, int size) -{ - int sys = 0; - int msg = msg_unpack_start(data, size, &sys); - if(!sys) - modc_message(msg); -} - - -const DEMOPLAYBACK_INFO *client_demoplayer_getinfo() -{ - static DEMOPLAYBACK_INFO ret; - const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); - ret.first_tick = info->first_tick; - ret.last_tick = info->last_tick; - ret.current_tick = info->current_tick; - ret.paused = info->paused; - ret.speed = info->speed; - return &ret; -} - -void client_demoplayer_setpos(float percent) -{ - demorec_playback_set(percent); -} - -void client_demoplayer_setspeed(float speed) -{ - demorec_playback_setspeed(speed); -} - -void client_demoplayer_setpause(int paused) -{ - if(paused) - demorec_playback_pause(); - else - demorec_playback_unpause(); -} - -static void client_update() -{ - if(client_state() == CLIENTSTATE_DEMOPLAYBACK) - { - demorec_playback_update(); - if(demorec_isplaying()) - { - /* update timers */ - const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); - current_tick = info->current_tick; - prev_tick = info->previous_tick; - intratick = info->intratick; - ticktime = info->ticktime; - } - else - { - /* disconnect on error */ - client_disconnect(); - } - } - else if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) - { - /* switch snapshot */ - int repredict = 0; - int64 freq = time_freq(); - int64 now = st_get(&game_time, time_get()); - int64 pred_now = st_get(&predicted_time, time_get()); - - while(1) - { - CSnapshotStorage::CHolder *cur = snapshots[SNAP_CURRENT]; - int64 tickstart = (cur->m_Tick)*time_freq()/50; - - if(tickstart < now) - { - CSnapshotStorage::CHolder *next = snapshots[SNAP_CURRENT]->m_pNext; - if(next) - { - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = next; - - /* set ticks */ - current_tick = snapshots[SNAP_CURRENT]->m_Tick; - prev_tick = snapshots[SNAP_PREV]->m_Tick; - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - modc_newsnapshot(); - repredict = 1; - } - } - else - break; - } - else - break; - } - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - int64 curtick_start = (snapshots[SNAP_CURRENT]->m_Tick)*time_freq()/50; - int64 prevtick_start = (snapshots[SNAP_PREV]->m_Tick)*time_freq()/50; - /*tg_add(&predicted_time_graph, pred_now, 0); */ - int prev_pred_tick = (int)(pred_now*50/time_freq()); - int new_pred_tick = prev_pred_tick+1; - static float last_predintra = 0; - - intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); - ticktime = (now - prevtick_start) / (float)freq; /*(float)SERVER_TICK_SPEED);*/ - - curtick_start = new_pred_tick*time_freq()/50; - prevtick_start = prev_pred_tick*time_freq()/50; - predintratick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); - - if(new_pred_tick < snapshots[SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || new_pred_tick > snapshots[SNAP_PREV]->m_Tick+SERVER_TICK_SPEED) - { - dbg_msg("client", "prediction time reset!"); - st_init(&predicted_time, snapshots[SNAP_CURRENT]->m_Tick*time_freq()/50); - } - - if(new_pred_tick > current_predtick) - { - last_predintra = predintratick; - current_predtick = new_pred_tick; - repredict = 1; - - /* send input */ - client_send_input(); - } - - last_predintra = predintratick; - } - - /* only do sane predictions */ - if(repredict) - { - if(current_predtick > current_tick && current_predtick < current_tick+50) - modc_predict(); - } - - /* fetch server info if we don't have it */ - if(client_state() >= CLIENTSTATE_LOADING && - current_server_info_requesttime >= 0 && - time_get() > current_server_info_requesttime) - { - client_serverbrowse_request(&server_address); - current_server_info_requesttime = time_get()+time_freq()*2; - } - } - - /* STRESS TEST: join the server again */ - if(config.dbg_stress) - { - static int64 action_taken = 0; - int64 now = time_get(); - if(client_state() == CLIENTSTATE_OFFLINE) - { - if(now > action_taken+time_freq()*2) - { - dbg_msg("stress", "reconnecting!"); - client_connect(config.dbg_stress_server); - action_taken = now; - } - } - else - { - /*if(now > action_taken+time_freq()*(10+config.dbg_stress)) - { - dbg_msg("stress", "disconnecting!"); - client_disconnect(); - action_taken = now; - }*/ - } - } - - /* pump the network */ - client_pump_network(); - - /* update the maser server registry */ - mastersrv_update(); - - /* update the server browser */ - client_serverbrowse_update(); -} - - -static void client_versionupdate() -{ - static int state = 0; - static HOSTLOOKUP version_serveraddr; - - if(state == 0) - { - engine_hostlookup(&version_serveraddr, config.cl_version_server); - state++; - } - else if(state == 1) - { - if(jobs_status(&version_serveraddr.job) == JOBSTATUS_DONE) - { - CNetChunk Packet; - - mem_zero(&Packet, sizeof(Packet)); - - version_serveraddr.addr.port = VERSIONSRV_PORT; - - Packet.m_ClientID = -1; - Packet.m_Address = version_serveraddr.addr; - Packet.m_pData = VERSIONSRV_GETVERSION; - Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION); - Packet.m_Flags = NETSENDFLAG_CONNLESS; - - m_NetClient.Send(&Packet); - state++; - } - } -} - -static void client_run() -{ - NETADDR bindaddr; - int64 reporttime = time_get(); - int64 reportinterval = time_freq()*1; - - static PERFORMACE_INFO rootscope = {"root", 0}; - perf_start(&rootscope); - - local_start_time = time_get(); - snapshot_parts = 0; - - /* init graphics and sound */ - m_pGraphics = CreateEngineGraphics(); - if(m_pGraphics->Init() != 0) - return; - - /* start refreshing addresses while we load */ - mastersrv_refresh_addresses(); - - /* init the editor */ - m_pEditor = CreateEditor(); - m_pEditor->Init(m_pGraphics); - - /* sound is allowed to fail */ - snd_init(); - - /* load data */ - if(!client_load_data()) - return; - - /* init the mod */ - m_pGameClient = CreateGameClient(&m_Client); - modc_init(); - dbg_msg("client", "version %s", modc_net_version()); - - /* open socket */ - mem_zero(&bindaddr, sizeof(bindaddr)); - m_NetClient.Open(bindaddr, 0); - - /* connect to the server if wanted */ - /* - if(config.cl_connect[0] != 0) - client_connect(config.cl_connect); - config.cl_connect[0] = 0; - */ - - /* */ - graph_init(&fps_graph, 0.0f, 200.0f); - - /* never start with the editor */ - config.cl_editor = 0; - - inp_mouse_mode_relative(); - - while (1) - { - static PERFORMACE_INFO rootscope = {"root", 0}; - int64 frame_start_time = time_get(); - frames++; - - perf_start(&rootscope); - - /* */ - client_versionupdate(); - - /* handle pending connects */ - if(cmd_connect[0]) - { - client_connect(cmd_connect); - cmd_connect[0] = 0; - } - - /* update input */ - { - static PERFORMACE_INFO scope = {"inp_update", 0}; - perf_start(&scope); - inp_update(); - perf_end(); - } - - /* update sound */ - { - static PERFORMACE_INFO scope = {"snd_update", 0}; - perf_start(&scope); - snd_update(); - perf_end(); - } - - /* release focus */ - if(!Graphics()->WindowActive()) - { - if(window_must_refocus == 0) - inp_mouse_mode_absolute(); - window_must_refocus = 1; - } - else if (config.dbg_focus && inp_key_pressed(KEY_ESCAPE)) - { - inp_mouse_mode_absolute(); - window_must_refocus = 1; - } - - /* refocus */ - if(window_must_refocus && Graphics()->WindowActive()) - { - if(window_must_refocus < 3) - { - inp_mouse_mode_absolute(); - window_must_refocus++; - } - - if(inp_key_pressed(KEY_MOUSE_1)) - { - inp_mouse_mode_relative(); - window_must_refocus = 0; - } - } - - /* panic quit button */ - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('q')) - break; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('d')) - config.debug ^= 1; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('g')) - config.dbg_graphs ^= 1; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('e')) - { - config.cl_editor = config.cl_editor^1; - inp_mouse_mode_relative(); - } - - /* - if(!gfx_window_open()) - break; - */ - - /* render */ - if(config.cl_editor) - { - client_update(); - m_pEditor->UpdateAndRender(); - m_pGraphics->Swap(); - } - else - { - { - static PERFORMACE_INFO scope = {"client_update", 0}; - perf_start(&scope); - client_update(); - perf_end(); - } - - if(config.dbg_stress) - { - if((frames%10) == 0) - { - client_render(); - m_pGraphics->Swap(); - } - } - else - { - { - static PERFORMACE_INFO scope = {"client_render", 0}; - perf_start(&scope); - client_render(); - perf_end(); - } - - { - static PERFORMACE_INFO scope = {"gfx_swap", 0}; - perf_start(&scope); - m_pGraphics->Swap(); - perf_end(); - } - } - } - - perf_end(); - - - /* check conditions */ - if(client_state() == CLIENTSTATE_QUITING) - break; - - /* be nice */ - if(config.dbg_stress) - thread_sleep(5); - else if(config.cl_cpu_throttle || !Graphics()->WindowActive()) - thread_sleep(1); - - if(config.dbg_hitch) - { - thread_sleep(config.dbg_hitch); - config.dbg_hitch = 0; - } - - if(reporttime < time_get()) - { - if(0 && config.debug) - { - dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", - frames/(float)(reportinterval/time_freq()), - 1.0f/frametime_high, - 1.0f/frametime_low, - m_NetClient.State()); - } - frametime_low = 1; - frametime_high = 0; - frames = 0; - reporttime += reportinterval; - perf_next(); - - if(config.dbg_pref) - perf_dump(&rootscope); - } - - /* update frametime */ - frametime = (time_get()-frame_start_time)/(float)time_freq(); - if(frametime < frametime_low) - frametime_low = frametime; - if(frametime > frametime_high) - frametime_high = frametime; - - graph_add(&fps_graph, 1.0f/frametime, 1,1,1); - } - - modc_shutdown(); - client_disconnect(); - - m_pGraphics->Shutdown(); - snd_shutdown(); -} - -void gfx_swap() -{ - m_pGraphics->Swap(); -} - -static void con_connect(void *result, void *user_data) -{ - str_copy(cmd_connect, console_arg_string(result, 0), sizeof(cmd_connect)); -} - -static void con_disconnect(void *result, void *user_data) -{ - client_disconnect(); -} - -static void con_quit(void *result, void *user_data) -{ - client_quit(); -} - -static void con_ping(void *result, void *user_data) -{ - msg_pack_start_system(NETMSG_PING, 0); - msg_pack_end(); - client_send_msg(); - ping_start_time = time_get(); -} - -static void con_screenshot(void *result, void *user_data) -{ - Graphics()->TakeScreenshot(); -} - -static void con_rcon(void *result, void *user_data) -{ - client_rcon(console_arg_string(result, 0)); -} - -static void con_rcon_auth(void *result, void *user_data) -{ - client_rcon_auth("", console_arg_string(result, 0)); -} - -static void con_addfavorite(void *result, void *user_data) -{ - NETADDR addr; - if(net_addr_from_str(&addr, console_arg_string(result, 0)) == 0) - client_serverbrowse_addfavorite(addr); -} - -const char *client_demoplayer_play(const char *filename) -{ - int crc; - const char *error; - client_disconnect(); - m_NetClient.ResetErrorString(); - - /* try to start playback */ - demorec_playback_registercallbacks(client_democallback_snapshot, client_democallback_message); - - if(demorec_playback_load(filename)) - return "error loading demo"; - - /* load map */ - crc = (demorec_playback_info()->header.crc[0]<<24)| - (demorec_playback_info()->header.crc[1]<<16)| - (demorec_playback_info()->header.crc[2]<<8)| - (demorec_playback_info()->header.crc[3]); - error = client_load_map_search(demorec_playback_info()->header.map, crc); - if(error) - { - client_disconnect_with_reason(error); - return error; - } - - modc_connected(); - - /* setup buffers */ - mem_zero(demorec_snapshotdata, sizeof(demorec_snapshotdata)); - - snapshots[SNAP_CURRENT] = &demorec_snapshotholders[SNAP_CURRENT]; - snapshots[SNAP_PREV] = &demorec_snapshotholders[SNAP_PREV]; - - snapshots[SNAP_CURRENT]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][0]; - snapshots[SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][1]; - snapshots[SNAP_CURRENT]->m_SnapSize = 0; - snapshots[SNAP_CURRENT]->m_Tick = -1; - - snapshots[SNAP_PREV]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][0]; - snapshots[SNAP_PREV]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][1]; - snapshots[SNAP_PREV]->m_SnapSize = 0; - snapshots[SNAP_PREV]->m_Tick = -1; - - /* enter demo playback state */ - client_set_state(CLIENTSTATE_DEMOPLAYBACK); - - demorec_playback_play(); - modc_entergame(); - - return 0; -} - -static void con_play(void *result, void *user_data) -{ - client_demoplayer_play(console_arg_string(result, 0)); -} - -static void con_record(void *result, void *user_data) -{ - if(state != CLIENTSTATE_ONLINE) - dbg_msg("demorec/record", "client is not online"); - else - { - char filename[512]; - str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0)); - demorec_record_start(filename, modc_net_version(), current_map, current_map_crc, "client"); - } -} - -static void con_stoprecord(void *result, void *user_data) -{ - demorec_record_stop(); -} - -static void con_serverdummy(void *result, void *user_data) -{ - dbg_msg("client", "this command is not available on the client"); -} - -static void client_register_commands() -{ - MACRO_REGISTER_COMMAND("quit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds"); - MACRO_REGISTER_COMMAND("exit", "", CFGFLAG_CLIENT, con_quit, 0x0, "Quit Teeworlds"); - MACRO_REGISTER_COMMAND("connect", "s", CFGFLAG_CLIENT, con_connect, 0x0, "Connect to the specified host/ip"); - MACRO_REGISTER_COMMAND("disconnect", "", CFGFLAG_CLIENT, con_disconnect, 0x0, "Disconnect from the server"); - MACRO_REGISTER_COMMAND("ping", "", CFGFLAG_CLIENT, con_ping, 0x0, "Ping the current server"); - MACRO_REGISTER_COMMAND("screenshot", "", CFGFLAG_CLIENT, con_screenshot, 0x0, "Take a screenshot"); - MACRO_REGISTER_COMMAND("rcon", "r", CFGFLAG_CLIENT, con_rcon, 0x0, "Send specified command to rcon"); - MACRO_REGISTER_COMMAND("rcon_auth", "s", CFGFLAG_CLIENT, con_rcon_auth, 0x0, "Authenticate to rcon"); - - MACRO_REGISTER_COMMAND("play", "r", CFGFLAG_CLIENT, con_play, 0x0, "Play the file specified"); - MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_CLIENT, con_record, 0, "Record to the file"); - MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_CLIENT, con_stoprecord, 0, "Stop recording"); - - MACRO_REGISTER_COMMAND("add_favorite", "s", CFGFLAG_CLIENT, con_addfavorite, 0x0, "Add a server as a favorite"); - - /* register server dummy commands for tab completion */ - MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_serverdummy, 0, "Kick player with specified id"); - MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_serverdummy, 0, "Ban player with ip/id for x minutes"); - MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_serverdummy, 0, "Unban ip"); - MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_serverdummy, 0, "Show banlist"); - MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_serverdummy, 0, "List players"); - MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_serverdummy, 0, "Shut down"); - /*MACRO_REGISTER_COMMAND("record", "", CFGFLAG_SERVER, con_serverdummy, 0); - MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_serverdummy, 0);*/ -} - -void client_save_line(const char *line) -{ - engine_config_write_line(line); -} - -const char *client_user_directory() -{ - static char path[1024] = {0}; - fs_storage_path("Teeworlds", path, sizeof(path)); - return path; -} - -#if defined(CONF_PLATFORM_MACOSX) -int SDL_main(int argc, char **argv) -#else -int main(int argc, char **argv) -#endif -{ - /* init the engine */ - dbg_msg("client", "starting..."); - engine_init("Teeworlds"); - - /* register all console commands */ - client_register_commands(); - modc_console_init(); - - /* parse the command line arguments */ - engine_parse_arguments(argc, argv); - - /* execute config file */ - console_execute_file("settings.cfg"); - - /* run the client*/ - client_run(); - - /* write down the config and quit */ - if(engine_config_write_start() == 0) - { - config_save(); - client_serverbrowse_save(); - modc_save_config(); - engine_config_write_stop(); - } - - return 0; -} diff --git a/src/engine/client/ec_gfx.cpp b/src/engine/client/ec_gfx.cpp deleted file mode 100644 index 5632581a..00000000 --- a/src/engine/client/ec_gfx.cpp +++ /dev/null @@ -1,992 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <base/detect.h> - -#include "SDL.h" - -#ifdef CONF_FAMILY_WINDOWS - #define WIN32_LEAN_AND_MEAN - #include <windows.h> -#endif - -#ifdef CONF_PLATFORM_MACOSX - #include <OpenGL/gl.h> - #include <OpenGL/glu.h> -#else - #include <GL/gl.h> - #include <GL/glu.h> -#endif - -#include <base/system.h> -#include <engine/external/pnglite/pnglite.h> - -#include <engine/e_client_interface.h> -#include <engine/e_engine.h> -#include <engine/e_config.h> -#include <engine/e_keys.h> - -#include <string.h> -#include <stdio.h> -#include <math.h> - -/* compressed textures */ -#define GL_COMPRESSED_RGB_ARB 0x84ED -#define GL_COMPRESSED_RGBA_ARB 0x84EE -#define GL_COMPRESSED_ALPHA_ARB 0x84E9 - -#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE - - -void gfx_font_init(); - -VIDEO_MODE fakemodes[] = { - {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, - {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, - {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, - {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, - {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, - {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, - {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, - {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, - {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, - {2048,1536,8,8,8}, - - {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, - {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, - {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, - {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, - {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, - {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, - {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, - {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, - {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, - {2048,1536,5,6,5} -}; - -int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) -{ - int num_modes = sizeof(fakemodes)/sizeof(VIDEO_MODE); - SDL_Rect **modes; - - if(config.gfx_display_all_modes) - { - int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); - mem_copy(list, fakemodes, sizeof(fakemodes)); - if(maxcount < count) - count = maxcount; - return count; - } - - /* TODO: fix this code on osx or windows */ - - modes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN); - if(modes == NULL) - { - /* no modes */ - num_modes = 0; - } - else if(modes == (SDL_Rect**)-1) - { - /* all modes */ - } - else - { - int i; - num_modes = 0; - for(i = 0; modes[i]; ++i) - { - if(num_modes == maxcount) - break; - list[num_modes].width = modes[i]->w; - list[num_modes].height = modes[i]->h; - list[num_modes].red = 8; - list[num_modes].green = 8; - list[num_modes].blue = 8; - num_modes++; - } - } - - return num_modes; -} - - -#include "graphics.h" - -class CGraphics_OpenGL : public IEngineGraphics -{ -protected: - /* */ - typedef struct { float x, y, z; } CPoint; - typedef struct { float u, v; } CTexCoord; - typedef struct { float r, g, b, a; } CColor; - - typedef struct - { - CPoint m_Pos; - CTexCoord m_Tex; - CColor m_Color; - } CVertex; - - enum - { - MAX_VERTICES = 32*1024, - MAX_TEXTURES = 1024*4, - - DRAWING_QUADS=1, - DRAWING_LINES=2 - }; - - CVertex m_aVertices[MAX_VERTICES]; - int m_NumVertices; - - CColor m_aColor[4]; - CTexCoord m_aTexture[4]; - - bool m_RenderEnable; - - float m_Rotation; - int m_Drawing; - bool m_DoScreenshot; - - float m_ScreenX0; - float m_ScreenY0; - float m_ScreenX1; - float m_ScreenY1; - - int m_InvalidTexture; - - struct CTexture - { - GLuint tex; - int memsize; - int flags; - int next; - }; - - enum - { - - }; - - CTexture m_aTextures[MAX_TEXTURES]; - int m_FirstFreeTexture; - int m_TextureMemoryUsage; - - - void Flush() - { - if(m_NumVertices == 0) - return; - - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glVertexPointer(3, GL_FLOAT, - sizeof(CVertex), - (char*)m_aVertices); - glTexCoordPointer(2, GL_FLOAT, - sizeof(CVertex), - (char*)m_aVertices + sizeof(float)*3); - glColorPointer(4, GL_FLOAT, - sizeof(CVertex), - (char*)m_aVertices + sizeof(float)*5); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - if(m_RenderEnable) - { - if(m_Drawing == DRAWING_QUADS) - glDrawArrays(GL_QUADS, 0, m_NumVertices); - else if(m_Drawing == DRAWING_LINES) - glDrawArrays(GL_LINES, 0, m_NumVertices); - } - - /* Reset pointer */ - m_NumVertices = 0; - } - - void AddVertices(int count) - { - m_NumVertices += count; - if((m_NumVertices + count) >= MAX_VERTICES) - Flush(); - } - - void Rotate(CPoint *pCenter, CPoint *pPoint) - { - float x = pPoint->x - pCenter->x; - float y = pPoint->y - pCenter->y; - pPoint->x = x * cosf(m_Rotation) - y * sinf(m_Rotation) + pCenter->x; - pPoint->y = x * sinf(m_Rotation) + y * cosf(m_Rotation) + pCenter->y; - } - - - - - static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) - { - return (data[(v*w+u)*4+offset]+ - data[(v*w+u+1)*4+offset]+ - data[((v+1)*w+u)*4+offset]+ - data[((v+1)*w+u+1)*4+offset])/4; - } -public: - CGraphics_OpenGL() - { - m_NumVertices = 0; - - m_ScreenX0 = 0; - m_ScreenY0 = 0; - m_ScreenX1 = 0; - m_ScreenY1 = 0; - - m_ScreenWidth = -1; - m_ScreenHeight = -1; - - m_Rotation = 0; - m_Drawing = 0; - m_InvalidTexture = 0; - - m_TextureMemoryUsage = 0; - - m_RenderEnable = true; - m_DoScreenshot = false; - } - - - virtual void ClipEnable(int x, int y, int w, int h) - { - //if(no_gfx) return; - glScissor(x, ScreenHeight()-(y+h), w, h); - glEnable(GL_SCISSOR_TEST); - } - - virtual void ClipDisable() - { - //if(no_gfx) return; - glDisable(GL_SCISSOR_TEST); - } - - - virtual void BlendNone() - { - glDisable(GL_BLEND); - } - - virtual void BlendNormal() - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - virtual void BlendAdditive() - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } - - //int gfx_memory_usage() { return m_MemoryUsage; } - - virtual void MapScreen(float tl_x, float tl_y, float br_x, float br_y) - { - m_ScreenX0 = tl_x; - m_ScreenY0 = tl_y; - m_ScreenX1 = br_x; - m_ScreenY1 = br_y; - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); - } - - virtual void GetScreen(float *tl_x, float *tl_y, float *br_x, float *br_y) - { - *tl_x = m_ScreenX0; - *tl_y = m_ScreenY0; - *br_x = m_ScreenX1; - *br_y = m_ScreenY1; - } - - virtual void LinesBegin() - { - dbg_assert(m_Drawing == 0, "called begin twice"); - m_Drawing = DRAWING_LINES; - SetColor(1,1,1,1); - } - - virtual void LinesEnd() - { - dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin"); - Flush(); - m_Drawing = 0; - } - - virtual void LinesDraw(float x0, float y0, float x1, float y1) - { - dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin"); - - m_aVertices[m_NumVertices].m_Pos.x = x0; - m_aVertices[m_NumVertices].m_Pos.y = y0; - m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; - m_aVertices[m_NumVertices].m_Color = m_aColor[0]; - - m_aVertices[m_NumVertices + 1].m_Pos.x = x1; - m_aVertices[m_NumVertices + 1].m_Pos.y = y1; - m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; - m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; - - AddVertices(2); - } - - - - virtual int UnloadTexture(int Index) - { - if(Index == m_InvalidTexture) - return 0; - - if(Index < 0) - return 0; - - glDeleteTextures(1, &m_aTextures[Index].tex); - m_aTextures[Index].next = m_FirstFreeTexture; - m_TextureMemoryUsage -= m_aTextures[Index].memsize; - m_FirstFreeTexture = Index; - return 0; - } - - - virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) - { - int mipmap = 1; - unsigned char *texdata = (unsigned char *)pData; - unsigned char *tmpdata = 0; - int oglformat = 0; - int store_oglformat = 0; - int tex = 0; - - /* don't waste memory on texture if we are stress testing */ - if(config.dbg_stress) - return m_InvalidTexture; - - /* grab texture */ - tex = m_FirstFreeTexture; - m_FirstFreeTexture = m_aTextures[tex].next; - m_aTextures[tex].next = -1; - - /* resample if needed */ - if(!(Flags&TEXLOAD_NORESAMPLE) && config.gfx_texture_quality==0) - { - if(Width > 16 && Height > 16 && Format == IMG_RGBA) - { - unsigned char *tmpdata; - int c = 0; - int x, y; - - tmpdata = (unsigned char *)mem_alloc(Width*Height*4, 1); - - Width/=2; - Height/=2; - - for(y = 0; y < Height; y++) - for(x = 0; x < Width; x++, c++) - { - tmpdata[c*4] = sample(Width*2, Height*2, texdata, x*2,y*2, 0); - tmpdata[c*4+1] = sample(Width*2, Height*2, texdata, x*2,y*2, 1); - tmpdata[c*4+2] = sample(Width*2, Height*2, texdata, x*2,y*2, 2); - tmpdata[c*4+3] = sample(Width*2, Height*2, texdata, x*2,y*2, 3); - } - texdata = tmpdata; - } - } - - oglformat = GL_RGBA; - if(Format == IMG_RGB) - oglformat = GL_RGB; - else if(Format == IMG_ALPHA) - oglformat = GL_ALPHA; - - /* upload texture */ - if(config.gfx_texture_compression) - { - store_oglformat = GL_COMPRESSED_RGBA_ARB; - if(StoreFormat == IMG_RGB) - store_oglformat = GL_COMPRESSED_RGB_ARB; - else if(StoreFormat == IMG_ALPHA) - store_oglformat = GL_COMPRESSED_ALPHA_ARB; - } - else - { - store_oglformat = GL_RGBA; - if(StoreFormat == IMG_RGB) - store_oglformat = GL_RGB; - else if(StoreFormat == IMG_ALPHA) - store_oglformat = GL_ALPHA; - } - - glGenTextures(1, &m_aTextures[tex].tex); - glBindTexture(GL_TEXTURE_2D, m_aTextures[tex].tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - gluBuild2DMipmaps(GL_TEXTURE_2D, store_oglformat, Width, Height, oglformat, GL_UNSIGNED_BYTE, texdata); - - /* calculate memory usage */ - { - int pixel_size = 4; - if(StoreFormat == IMG_RGB) - pixel_size = 3; - else if(StoreFormat == IMG_ALPHA) - pixel_size = 1; - - m_aTextures[tex].memsize = Width*Height*pixel_size; - if(mipmap) - { - while(Width > 2 && Height > 2) - { - Width>>=1; - Height>>=1; - m_aTextures[tex].memsize += Width*Height*pixel_size; - } - } - } - - m_TextureMemoryUsage += m_aTextures[tex].memsize; - mem_free(tmpdata); - return tex; - } - - /* simple uncompressed RGBA loaders */ - virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) - { - int l = strlen(pFilename); - int id; - IMAGE_INFO img; - - if(l < 3) - return -1; - if(LoadPNG(&img, pFilename)) - { - if (StoreFormat == IMG_AUTO) - StoreFormat = img.format; - - id = LoadTextureRaw(img.width, img.height, img.format, img.data, StoreFormat, Flags); - mem_free(img.data); - return id; - } - - return m_InvalidTexture; - } - - virtual int LoadPNG(IMAGE_INFO *pImg, const char *pFilename) - { - char aCompleteFilename[512]; - unsigned char *pBuffer; - png_t png; - - /* open file for reading */ - png_init(0,0); - - engine_getpath(aCompleteFilename, sizeof(aCompleteFilename), pFilename, IOFLAG_READ); - - if(png_open_file(&png, aCompleteFilename) != PNG_NO_ERROR) - { - dbg_msg("game/png", "failed to open file. filename='%s'", aCompleteFilename); - return 0; - } - - if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) - { - dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename); - png_close_file(&png); - return 0; - } - - pBuffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); - png_get_data(&png, pBuffer); - png_close_file(&png); - - pImg->width = png.width; - pImg->height = png.height; - if(png.color_type == PNG_TRUECOLOR) - pImg->format = IMG_RGB; - else if(png.color_type == PNG_TRUECOLOR_ALPHA) - pImg->format = IMG_RGBA; - pImg->data = pBuffer; - return 1; - } - - void ScreenshotDirect(const char *filename) - { - /* fetch image data */ - int y; - int w = m_ScreenWidth; - int h = m_ScreenHeight; - unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*4, 1); - unsigned char *temp_row = pixel_data+w*h*4; - glReadPixels(0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data); - - /* flip the pixel because opengl works from bottom left corner */ - for(y = 0; y < h/2; y++) - { - mem_copy(temp_row, pixel_data+y*w*4, w*4); - mem_copy(pixel_data+y*w*4, pixel_data+(h-y-1)*w*4, w*4); - mem_copy(pixel_data+(h-y-1)*w*4, temp_row,w*4); - } - - /* find filename */ - { - char wholepath[1024]; - png_t png; - - engine_savepath(filename, wholepath, sizeof(wholepath)); - - /* save png */ - dbg_msg("client", "saved screenshot to '%s'", wholepath); - png_open_file_write(&png, wholepath); - png_set_data(&png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pixel_data); - png_close_file(&png); - } - - /* clean up */ - mem_free(pixel_data); - } - - virtual void TextureSet(int TextureID) - { - dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin"); - if(TextureID == -1) - { - glDisable(GL_TEXTURE_2D); - } - else - { - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, m_aTextures[TextureID].tex); - } - } - - virtual void Clear(float r, float g, float b) - { - glClearColor(r,g,b,0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - - virtual void QuadsBegin() - { - dbg_assert(m_Drawing == 0, "called quads_begin twice"); - m_Drawing = DRAWING_QUADS; - - QuadsSetSubset(0,0,1,1); - QuadsSetRotation(0); - SetColor(1,1,1,1); - } - - virtual void QuadsEnd() - { - dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin"); - Flush(); - m_Drawing = 0; - } - - virtual void QuadsSetRotation(float Angle) - { - dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin"); - m_Rotation = Angle; - } - - virtual void SetColorVertex(int i, float r, float g, float b, float a) - { - dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin"); - m_aColor[i].r = r; - m_aColor[i].g = g; - m_aColor[i].b = b; - m_aColor[i].a = a; - } - - virtual void SetColor(float r, float g, float b, float a) - { - dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin"); - SetColorVertex(0, r, g, b, a); - SetColorVertex(1, r, g, b, a); - SetColorVertex(2, r, g, b, a); - SetColorVertex(3, r, g, b, a); - } - - virtual void QuadsSetSubset(float tl_u, float tl_v, float br_u, float br_v) - { - dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin"); - - m_aTexture[0].u = tl_u; m_aTexture[1].u = br_u; - m_aTexture[0].v = tl_v; m_aTexture[1].v = tl_v; - - m_aTexture[3].u = tl_u; m_aTexture[2].u = br_u; - m_aTexture[3].v = br_v; m_aTexture[2].v = br_v; - } - - virtual void QuadsSetSubsetFree( - float x0, float y0, float x1, float y1, - float x2, float y2, float x3, float y3) - { - m_aTexture[0].u = x0; m_aTexture[0].v = y0; - m_aTexture[1].u = x1; m_aTexture[1].v = y1; - m_aTexture[2].u = x2; m_aTexture[2].v = y2; - m_aTexture[3].u = x3; m_aTexture[3].v = y3; - } - - virtual void QuadsDraw(float x, float y, float w, float h) - { - QuadsDrawTL(x-w/2, y-h/2,w,h); - } - - virtual void QuadsDrawTL(float x, float y, float w, float h) - { - CPoint Center; - - dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin"); - - Center.x = x + w/2; - Center.y = y + h/2; - Center.z = 0; - - m_aVertices[m_NumVertices].m_Pos.x = x; - m_aVertices[m_NumVertices].m_Pos.y = y; - m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; - m_aVertices[m_NumVertices].m_Color = m_aColor[0]; - Rotate(&Center, &m_aVertices[m_NumVertices].m_Pos); - - m_aVertices[m_NumVertices + 1].m_Pos.x = x+w; - m_aVertices[m_NumVertices + 1].m_Pos.y = y; - m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; - m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; - Rotate(&Center, &m_aVertices[m_NumVertices + 1].m_Pos); - - m_aVertices[m_NumVertices + 2].m_Pos.x = x + w; - m_aVertices[m_NumVertices + 2].m_Pos.y = y+h; - m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[2]; - m_aVertices[m_NumVertices + 2].m_Color = m_aColor[2]; - Rotate(&Center, &m_aVertices[m_NumVertices + 2].m_Pos); - - m_aVertices[m_NumVertices + 3].m_Pos.x = x; - m_aVertices[m_NumVertices + 3].m_Pos.y = y+h; - m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[3]; - m_aVertices[m_NumVertices + 3].m_Color = m_aColor[3]; - Rotate(&Center, &m_aVertices[m_NumVertices + 3].m_Pos); - - AddVertices(4); - } - - void QuadsDrawFreeform( - float x0, float y0, float x1, float y1, - float x2, float y2, float x3, float y3) - { - dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); - - m_aVertices[m_NumVertices].m_Pos.x = x0; - m_aVertices[m_NumVertices].m_Pos.y = y0; - m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; - m_aVertices[m_NumVertices].m_Color = m_aColor[0]; - - m_aVertices[m_NumVertices + 1].m_Pos.x = x1; - m_aVertices[m_NumVertices + 1].m_Pos.y = y1; - m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; - m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; - - m_aVertices[m_NumVertices + 2].m_Pos.x = x3; - m_aVertices[m_NumVertices + 2].m_Pos.y = y3; - m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[3]; - m_aVertices[m_NumVertices + 2].m_Color = m_aColor[3]; - - m_aVertices[m_NumVertices + 3].m_Pos.x = x2; - m_aVertices[m_NumVertices + 3].m_Pos.y = y2; - m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[2]; - m_aVertices[m_NumVertices + 3].m_Color = m_aColor[2]; - - AddVertices(4); - } - - virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) - { - float startx = x; - - QuadsBegin(); - SetColor(r,g,b,a); - - while(*pText) - { - char c = *pText; - pText++; - - if(c == '\n') - { - x = startx; - y += Size; - } - else - { - QuadsSetSubset( - (c%16)/16.0f, - (c/16)/16.0f, - (c%16)/16.0f+1.0f/16.0f, - (c/16)/16.0f+1.0f/16.0f); - - QuadsDrawTL(x,y,Size,Size); - x += Size/2; - } - } - - QuadsEnd(); - } - - virtual bool Init() - { - /* Set all z to -5.0f */ - for(int i = 0; i < MAX_VERTICES; i++) - m_aVertices[i].m_Pos.z = -5.0f; - - /* init textures */ - m_FirstFreeTexture = 0; - for(int i = 0; i < MAX_TEXTURES; i++) - m_aTextures[i].next = i+1; - m_aTextures[MAX_TEXTURES-1].next = -1; - - /* set some default settings */ - glEnable(GL_BLEND); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glAlphaFunc(GL_GREATER, 0); - glEnable(GL_ALPHA_TEST); - glDepthMask(0); - - /* create null texture, will get id=0 */ - static const unsigned char aNullTextureData[] = { - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, - }; - - m_InvalidTexture = LoadTextureRaw(4,4,IMG_RGBA,aNullTextureData,IMG_RGBA,TEXLOAD_NORESAMPLE); - dbg_msg("", "invalid texture id: %d %d", m_InvalidTexture, m_aTextures[m_InvalidTexture].tex); - - return true; - } -}; - -class CGraphics_SDL : public CGraphics_OpenGL -{ - SDL_Surface *m_pScreenSurface; - - int TryInit() - { - const SDL_VideoInfo *pInfo; - int Flags = SDL_OPENGL; - - m_ScreenWidth = config.gfx_screen_width; - m_ScreenHeight = config.gfx_screen_height; - - pInfo = SDL_GetVideoInfo(); - - /* set flags */ - Flags = SDL_OPENGL; - Flags |= SDL_GL_DOUBLEBUFFER; - Flags |= SDL_HWPALETTE; - if(config.dbg_resizable) - Flags |= SDL_RESIZABLE; - - if(pInfo->hw_available) - Flags |= SDL_HWSURFACE; - else - Flags |= SDL_SWSURFACE; - - if(pInfo->blit_hw) - Flags |= SDL_HWACCEL; - - if(config.gfx_fullscreen) - Flags |= SDL_FULLSCREEN; - - /* set gl attributes */ - if(config.gfx_fsaa_samples) - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.gfx_fsaa_samples); - } - else - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); - } - - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, config.gfx_vsync); - - /* set caption */ - SDL_WM_SetCaption("Teeworlds", "Teeworlds"); - - /* create window */ - m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags); - if(m_pScreenSurface == NULL) - { - dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); - return -1; - } - - return 0; - } - - - int InitWindow() - { - if(TryInit() == 0) - return 0; - - /* try disabling fsaa */ - while(config.gfx_fsaa_samples) - { - config.gfx_fsaa_samples--; - - if(config.gfx_fsaa_samples) - dbg_msg("gfx", "lowering FSAA to %d and trying again", config.gfx_fsaa_samples); - else - dbg_msg("gfx", "disabling FSAA and trying again"); - - if(TryInit() == 0) - return 0; - } - - /* try lowering the resolution */ - if(config.gfx_screen_width != 640 || config.gfx_screen_height != 480) - { - dbg_msg("gfx", "setting resolution to 640x480 and trying again"); - config.gfx_screen_width = 640; - config.gfx_screen_height = 480; - - if(TryInit() == 0) - return 0; - } - - dbg_msg("gfx", "out of ideas. failed to init graphics"); - - return -1; - } - - -public: - CGraphics_SDL() - { - m_pScreenSurface = 0; - } - - virtual bool Init() - { - { - int Systems = SDL_INIT_VIDEO; - - if(config.snd_enable) - Systems |= SDL_INIT_AUDIO; - - if(config.cl_eventthread) - Systems |= SDL_INIT_EVENTTHREAD; - - if(SDL_Init(Systems) < 0) - { - dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError()); - return -1; - } - } - - atexit(SDL_Quit); - - #ifdef CONF_FAMILY_WINDOWS - if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) - putenv("SDL_VIDEO_WINDOW_POS=8,27"); - #endif - - if(InitWindow() != 0) - return -1; - - SDL_ShowCursor(0); - - CGraphics_OpenGL::Init(); - - MapScreen(0,0,config.gfx_screen_width, config.gfx_screen_height); - - /* init input */ - inp_init(); - - /* font init */ - gfx_font_init(); - - return 0; - } - - virtual void Shutdown() - { - /* TODO: SDL, is this correct? */ - SDL_Quit(); - } - - virtual void Minimize() - { - SDL_WM_IconifyWindow(); - } - - virtual void Maximize() - { - /* TODO: SDL */ - } - - virtual int WindowActive() - { - return SDL_GetAppState()&SDL_APPINPUTFOCUS; - } - - virtual int WindowOpen() - { - return SDL_GetAppState()&SDL_APPACTIVE; - - } - - virtual void TakeScreenshot() - { - m_DoScreenshot = true; - } - - virtual void Swap() - { - if(m_DoScreenshot) - { - /* find filename */ - char filename[128]; - static int index = 1; - - for(; index < 1000; index++) - { - IOHANDLE io; - str_format(filename, sizeof(filename), "screenshots/screenshot%04d.png", index); - io = engine_openfile(filename, IOFLAG_READ); - if(io) - io_close(io); - else - break; - } - - ScreenshotDirect(filename); - m_DoScreenshot = false; - } - - { - static PERFORMACE_INFO pscope = {"glfwSwapBuffers", 0}; - perf_start(&pscope); - SDL_GL_SwapBuffers(); - perf_end(); - } - - if(config.gfx_finish) - glFinish(); - } -}; - -extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); } diff --git a/src/engine/client/ec_gfx_text.cpp b/src/engine/client/ec_gfx_text.cpp deleted file mode 100644 index d17d1bed..00000000 --- a/src/engine/client/ec_gfx_text.cpp +++ /dev/null @@ -1,669 +0,0 @@ -#include <base/system.h> -#include <string.h> -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> - -extern IEngineGraphics *Graphics(); - -#ifdef CONF_PLATFORM_MACOSX - #include <OpenGL/gl.h> - #include <OpenGL/glu.h> -#else - #include <GL/gl.h> - #include <GL/glu.h> -#endif - -static int word_length(const char *text) -{ - int s = 1; - while(1) - { - if(*text == 0) - return s-1; - if(*text == '\n' || *text == '\t' || *text == ' ') - return s; - text++; - s++; - } -} - -static float text_r=1; -static float text_g=1; -static float text_b=1; -static float text_a=1; - -static struct FONT *default_font = 0; -void gfx_text_set_default_font(struct FONT *font) -{ - default_font = font; -} - - -void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags) -{ - mem_zero(cursor, sizeof(*cursor)); - cursor->font_size = font_size; - cursor->start_x = x; - cursor->start_y = y; - cursor->x = x; - cursor->y = y; - cursor->line_count = 1; - cursor->line_width = -1; - cursor->flags = flags; - cursor->charcount = 0; -} - - -void gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width) -{ - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, x, y, size, TEXTFLAG_RENDER); - cursor.line_width = max_width; - gfx_text_ex(&cursor, text, -1); -} - -float gfx_text_width(void *font_set_v, float size, const char *text, int length) -{ - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, 0, 0, size, 0); - gfx_text_ex(&cursor, text, length); - return cursor.x; -} - -void gfx_text_color(float r, float g, float b, float a) -{ - text_r = r; - text_g = g; - text_b = b; - text_a = a; -} - -/* ft2 texture */ -#include <ft2build.h> -#include FT_FREETYPE_H - -static FT_Library ft_library; - -#define MAX_CHARACTERS 64 - - -/* GL_LUMINANCE can be good for debugging*/ -static int font_texture_format = GL_ALPHA; - - -static int font_sizes[] = {8,9,10,11,12,13,14,15,16,17,18,19,20,36}; -#define NUM_FONT_SIZES (sizeof(font_sizes)/sizeof(int)) - - -typedef struct FONTCHAR -{ - int id; - - /* these values are scaled to the font size */ - /* width * font_size == real_size */ - float width; - float height; - float offset_x; - float offset_y; - float advance_x; - - float uvs[4]; - int64 touch_time; -} FONTCHAR; - -typedef struct FONTSIZEDATA -{ - int font_size; - FT_Face *face; - - unsigned textures[2]; - int texture_width; - int texture_height; - - int num_x_chars; - int num_y_chars; - - int char_max_width; - int char_max_height; - - FONTCHAR characters[MAX_CHARACTERS*MAX_CHARACTERS]; - - int current_character; -} FONTSIZEDATA; - -typedef struct FONT -{ - char filename[128]; - FT_Face ft_face; - FONTSIZEDATA sizes[NUM_FONT_SIZES]; -} FONT; - -static int font_get_index(int pixelsize) -{ - for(unsigned i = 0; i < NUM_FONT_SIZES; i++) - { - if(font_sizes[i] >= pixelsize) - return i; - } - - return NUM_FONT_SIZES-1; -} - -FONT *gfx_font_load(const char *filename) -{ - FONT *font = (FONT *)mem_alloc(sizeof(FONT), 1); - - mem_zero(font, sizeof(*font)); - str_copy(font->filename, filename, sizeof(font->filename)); - - if(FT_New_Face(ft_library, font->filename, 0, &font->ft_face)) - { - mem_free(font); - return NULL; - } - - for(unsigned i = 0; i < NUM_FONT_SIZES; i++) - font->sizes[i].font_size = -1; - - return font; -}; - -void gfx_font_destroy(FONT *font) -{ - mem_free(font); -} - -void gfx_font_init() -{ - FT_Init_FreeType(&ft_library); -} - -static void grow(unsigned char *in, unsigned char *out, int w, int h) -{ - int y, x; - for(y = 0; y < h; y++) - for(x = 0; x < w; x++) - { - int c = in[y*w+x]; - int s_y, s_x; - - for(s_y = -1; s_y <= 1; s_y++) - for(s_x = -1; s_x <= 1; s_x++) - { - int get_x = x+s_x; - int get_y = y+s_y; - if (get_x >= 0 && get_y >= 0 && get_x < w && get_y < h) - { - int index = get_y*w+get_x; - if(in[index] > c) - c = in[index]; - } - } - - out[y*w+x] = c; - } -} - -static void font_init_texture(FONTSIZEDATA *sizedata, int charwidth, int charheight, int xchars, int ychars) -{ - static int font_memory_usage = 0; - int i; - int width = charwidth*xchars; - int height = charheight*ychars; - void *mem = mem_alloc(width*height, 1); - mem_zero(mem, width*height); - - if(sizedata->textures[0] == 0) - glGenTextures(2, sizedata->textures); - else - font_memory_usage -= sizedata->texture_width*sizedata->texture_height*2; - - sizedata->num_x_chars = xchars; - sizedata->num_y_chars = ychars; - sizedata->texture_width = width; - sizedata->texture_height = height; - sizedata->current_character = 0; - - for(i = 0; i < 2; i++) - { - glBindTexture(GL_TEXTURE_2D, sizedata->textures[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, font_texture_format, width, height, 0, font_texture_format, GL_UNSIGNED_BYTE, mem); - font_memory_usage += width*height; - } - - dbg_msg("", "font memory usage: %d", font_memory_usage); - - mem_free(mem); -} - -static void font_increase_texture_size(FONTSIZEDATA *sizedata) -{ - if(sizedata->texture_width < sizedata->texture_height) - sizedata->num_x_chars <<= 1; - else - sizedata->num_y_chars <<= 1; - font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, sizedata->num_x_chars, sizedata->num_y_chars); -} - -static void font_init_index(FONT *font, int index) -{ - int outline_thickness = 1; - FONTSIZEDATA *sizedata = &font->sizes[index]; - - sizedata->font_size = font_sizes[index]; - FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size); - - if(sizedata->font_size >= 18) - outline_thickness = 2; - - { - unsigned glyph_index; - int charcode; - int max_h = 0; - int max_w = 0; - - charcode = FT_Get_First_Char(font->ft_face, &glyph_index); - while(glyph_index != 0) - { - /* do stuff */ - FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT); - - if(font->ft_face->glyph->metrics.width > max_w) max_w = font->ft_face->glyph->metrics.width; - if(font->ft_face->glyph->metrics.height > max_h) max_h = font->ft_face->glyph->metrics.height; - charcode = FT_Get_Next_Char(font->ft_face, charcode, &glyph_index); - } - - max_w = (max_w>>6)+2+outline_thickness*2; - max_h = (max_h>>6)+2+outline_thickness*2; - - for(sizedata->char_max_width = 1; sizedata->char_max_width < max_w; sizedata->char_max_width <<= 1); - for(sizedata->char_max_height = 1; sizedata->char_max_height < max_h; sizedata->char_max_height <<= 1); - } - - //dbg_msg("font", "init size %d, texture size %d %d", font->sizes[index].font_size, w, h); - //FT_New_Face(ft_library, "data/fonts/vera.ttf", 0, &font->ft_face); - font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, 8, 8); -} - -static FONTSIZEDATA *font_get_size(FONT *font, int pixelsize) -{ - int index = font_get_index(pixelsize); - if(font->sizes[index].font_size != font_sizes[index]) - font_init_index(font, index); - return &font->sizes[index]; -} - - -static void font_upload_glyph(FONTSIZEDATA *sizedata, int texnum, int slot_id, int chr, const void *data) -{ - int x = (slot_id%sizedata->num_x_chars) * (sizedata->texture_width/sizedata->num_x_chars); - int y = (slot_id/sizedata->num_x_chars) * (sizedata->texture_height/sizedata->num_y_chars); - - glBindTexture(GL_TEXTURE_2D, sizedata->textures[texnum]); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, - sizedata->texture_width/sizedata->num_x_chars, - sizedata->texture_height/sizedata->num_y_chars, - font_texture_format, GL_UNSIGNED_BYTE, data); -} - -/* 8k of data used for rendering glyphs */ -static unsigned char glyphdata[(4096/64) * (4096/64)]; -static unsigned char glyphdata_outlined[(4096/64) * (4096/64)]; - -static int font_get_slot(FONTSIZEDATA *sizedata) -{ - int char_count = sizedata->num_x_chars*sizedata->num_y_chars; - if(sizedata->current_character < char_count) - { - int i = sizedata->current_character; - sizedata->current_character++; - return i; - } - - /* kick out the oldest */ - /* TODO: remove this linear search */ - { - int oldest = 0; - int i; - for(i = 1; i < char_count; i++) - { - if(sizedata->characters[i].touch_time < sizedata->characters[oldest].touch_time) - oldest = i; - } - - if(time_get()-sizedata->characters[oldest].touch_time < time_freq()) - { - font_increase_texture_size(sizedata); - return font_get_slot(sizedata); - } - - return oldest; - } -} - -static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr) -{ - FT_Bitmap *bitmap; - int slot_id = 0; - int slot_w = sizedata->texture_width / sizedata->num_x_chars; - int slot_h = sizedata->texture_height / sizedata->num_y_chars; - int slot_size = slot_w*slot_h; - int outline_thickness = 1; - int x = 1; - int y = 1; - int px, py; - - FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size); - - if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP)) - { - dbg_msg("font", "error loading glyph %d", chr); - return -1; - } - - bitmap = &font->ft_face->glyph->bitmap; - - /* fetch slot */ - slot_id = font_get_slot(sizedata); - if(slot_id < 0) - return -1; - - /* adjust spacing */ - if(sizedata->font_size >= 18) - outline_thickness = 2; - x += outline_thickness; - y += outline_thickness; - - /* prepare glyph data */ - mem_zero(glyphdata, slot_size); - - if(bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) - { - for(py = 0; py < bitmap->rows; py++) - for(px = 0; px < bitmap->width; px++) - glyphdata[(py+y)*slot_w+px+x] = bitmap->buffer[py*bitmap->pitch+px]; - } - else if(bitmap->pixel_mode == FT_PIXEL_MODE_MONO) - { - for(py = 0; py < bitmap->rows; py++) - for(px = 0; px < bitmap->width; px++) - { - if(bitmap->buffer[py*bitmap->pitch+px/8]&(1<<(7-(px%8)))) - glyphdata[(py+y)*slot_w+px+x] = 255; - } - } - - if(0) for(py = 0; py < slot_w; py++) - for(px = 0; px < slot_h; px++) - glyphdata[py*slot_w+px] = 255; - - /* upload the glyph */ - font_upload_glyph(sizedata, 0, slot_id, chr, glyphdata); - - if(outline_thickness == 1) - { - grow(glyphdata, glyphdata_outlined, slot_w, slot_h); - font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata_outlined); - } - else - { - grow(glyphdata, glyphdata_outlined, slot_w, slot_h); - grow(glyphdata_outlined, glyphdata, slot_w, slot_h); - font_upload_glyph(sizedata, 1, slot_id, chr, glyphdata); - } - - /* set char info */ - { - FONTCHAR *fontchr = &sizedata->characters[slot_id]; - float scale = 1.0f/sizedata->font_size; - float uscale = 1.0f/sizedata->texture_width; - float vscale = 1.0f/sizedata->texture_height; - int height = bitmap->rows + outline_thickness*2 + 2; - int width = bitmap->width + outline_thickness*2 + 2; - - fontchr->id = chr; - fontchr->height = height * scale; - fontchr->width = width * scale; - fontchr->offset_x = (font->ft_face->glyph->bitmap_left-1) * scale; - fontchr->offset_y = (sizedata->font_size - font->ft_face->glyph->bitmap_top) * scale; - fontchr->advance_x = (font->ft_face->glyph->advance.x>>6) * scale; - - fontchr->uvs[0] = (slot_id%sizedata->num_x_chars) / (float)(sizedata->num_x_chars); - fontchr->uvs[1] = (slot_id/sizedata->num_x_chars) / (float)(sizedata->num_y_chars); - fontchr->uvs[2] = fontchr->uvs[0] + width*uscale; - fontchr->uvs[3] = fontchr->uvs[1] + height*vscale; - } - - return slot_id; -} - -static FONTCHAR *font_get_char(FONT *font, FONTSIZEDATA *sizedata, int chr) -{ - FONTCHAR *fontchr = NULL; - - /* search for the character */ - /* TODO: remove this linear search */ - int i; - for(i = 0; i < sizedata->current_character; i++) - { - if(sizedata->characters[i].id == chr) - { - fontchr = &sizedata->characters[i]; - break; - } - } - - /* check if we need to render the character */ - if(!fontchr) - { - int index = font_render_glyph(font, sizedata, chr); - if(index >= 0) - fontchr = &sizedata->characters[index]; - } - - /* touch the character */ - /* TODO: don't call time_get here */ - if(fontchr) - fontchr->touch_time = time_get(); - - return fontchr; -} - -/* must only be called from the rendering function as the font must be set to the correct size */ -static void font_render_setup(FONT *font, int size) -{ - FT_Set_Pixel_Sizes(font->ft_face, 0, size); -} - -static float font_kerning(FONT *font, int left, int right) -{ - FT_Vector kerning = {0,0}; - FT_Get_Kerning(font->ft_face, left, right, FT_KERNING_DEFAULT, &kerning); - return (kerning.x>>6); -} - - -void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) -{ - FONT *font = cursor->font; - FONTSIZEDATA *sizedata = NULL; - - float screen_x0, screen_y0, screen_x1, screen_y1; - float fake_to_screen_x, fake_to_screen_y; - int actual_x, actual_y; - - int actual_size; - int i; - int got_new_line = 0; - float draw_x, draw_y; - float cursor_x, cursor_y; - const char *end; - - float size = cursor->font_size; - - /* to correct coords, convert to screen coords, round, and convert back */ - Graphics()->GetScreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); - - fake_to_screen_x = (Graphics()->ScreenWidth()/(screen_x1-screen_x0)); - fake_to_screen_y = (Graphics()->ScreenHeight()/(screen_y1-screen_y0)); - actual_x = cursor->x * fake_to_screen_x; - actual_y = cursor->y * fake_to_screen_y; - - cursor_x = actual_x / fake_to_screen_x; - cursor_y = actual_y / fake_to_screen_y; - - /* same with size */ - actual_size = size * fake_to_screen_y; - size = actual_size / fake_to_screen_y; - - /* fetch font data */ - if(!font) - font = default_font; - - if(!font) - return; - - sizedata = font_get_size(font, actual_size); - font_render_setup(font, actual_size); - - /* set length */ - if(length < 0) - length = strlen(text); - - end = text + length; - - /* if we don't want to render, we can just skip the first outline pass */ - i = 1; - if(cursor->flags&TEXTFLAG_RENDER) - i = 0; - - for(;i < 2; i++) - { - const char *current = (char *)text; - const char *end = current+length; - draw_x = cursor_x; - draw_y = cursor_y; - - if(cursor->flags&TEXTFLAG_RENDER) - { - // TODO: Make this better - glEnable(GL_TEXTURE_2D); - if (i == 0) - glBindTexture(GL_TEXTURE_2D, sizedata->textures[1]); - else - glBindTexture(GL_TEXTURE_2D, sizedata->textures[0]); - - Graphics()->QuadsBegin(); - if (i == 0) - Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f*text_a); - else - Graphics()->SetColor(text_r, text_g, text_b, text_a); - } - - while(current < end) - { - int new_line = 0; - const char *batch_end = end; - if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END)) - { - int wlen = word_length((char *)current); - TEXT_CURSOR compare = *cursor; - compare.x = draw_x; - compare.y = draw_y; - compare.flags &= ~TEXTFLAG_RENDER; - compare.line_width = -1; - gfx_text_ex(&compare, text, wlen); - - if(compare.x-draw_x > cursor->line_width) - { - /* word can't be fitted in one line, cut it */ - TEXT_CURSOR cutter = *cursor; - cutter.charcount = 0; - cutter.x = draw_x; - cutter.y = draw_y; - cutter.flags &= ~TEXTFLAG_RENDER; - cutter.flags |= TEXTFLAG_STOP_AT_END; - - gfx_text_ex(&cutter, (const char *)current, wlen); - wlen = cutter.charcount; - new_line = 1; - - if(wlen <= 3) /* if we can't place 3 chars of the word on this line, take the next */ - wlen = 0; - } - else if(compare.x-cursor->start_x > cursor->line_width) - { - new_line = 1; - wlen = 0; - } - - batch_end = current + wlen; - } - - while(current < batch_end) - { - const char *tmp; - float advance = 0; - int character = 0; - int nextcharacter = 0; - FONTCHAR *chr; - - // TODO: UTF-8 decode - character = str_utf8_decode(¤t); - tmp = current; - nextcharacter = str_utf8_decode(&tmp); - - if(character == '\n') - { - draw_x = cursor->start_x; - draw_y += size; - draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */ - draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y; - continue; - } - - chr = font_get_char(font, sizedata, character); - - if(chr) - { - if(cursor->flags&TEXTFLAG_RENDER) - { - Graphics()->QuadsSetSubset(chr->uvs[0], chr->uvs[1], chr->uvs[2], chr->uvs[3]); - Graphics()->QuadsDrawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size); - } - - advance = chr->advance_x + font_kerning(font, character, nextcharacter)/size; - } - - if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width) - { - /* we hit the end of the line, no more to render or count */ - current = end; - break; - } - - draw_x += advance*size; - cursor->charcount++; - } - - if(new_line) - { - draw_x = cursor->start_x; - draw_y += size; - got_new_line = 1; - draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */ - draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y; - } - } - - if(cursor->flags&TEXTFLAG_RENDER) - Graphics()->QuadsEnd(); - } - - cursor->x = draw_x; - - if(got_new_line) - cursor->y = draw_y; -} diff --git a/src/engine/client/ec_inp.cpp b/src/engine/client/ec_inp.cpp deleted file mode 100644 index cf956471..00000000 --- a/src/engine/client/ec_inp.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> -#include "SDL.h" - -#include <base/system.h> -#include <engine/e_client_interface.h> -#include <engine/e_config.h> -#include <engine/client/graphics.h> - -static struct -{ - unsigned char presses; - unsigned char releases; -} input_count[2][1024] = {{{0}}, {{0}}}; - -static unsigned char input_state[2][1024] = {{0}, {0}}; - -static int input_current = 0; -static int input_grabbed = 0; - -static unsigned int last_release = 0; -static unsigned int release_delta = -1; - -// TODO: Refactor: Remove this -extern IEngineGraphics *Graphics(); - -void inp_mouse_relative(int *x, int *y) -{ - int nx = 0, ny = 0; - float sens = config.inp_mousesens/100.0f; - - if(config.inp_grab) - SDL_GetRelativeMouseState(&nx, &ny); - else - { - if(input_grabbed) - { - SDL_GetMouseState(&nx,&ny); - SDL_WarpMouse(Graphics()->ScreenWidth()/2,Graphics()->ScreenHeight()/2); - nx -= Graphics()->ScreenWidth()/2; ny -= Graphics()->ScreenHeight()/2; - } - } - - *x = nx*sens; - *y = ny*sens; -} - -enum -{ - INPUT_BUFFER_SIZE=32 -}; - -static INPUT_EVENT input_events[INPUT_BUFFER_SIZE]; -static int num_events = 0; - -static void add_event(int unicode, int key, int flags) -{ - if(num_events != INPUT_BUFFER_SIZE) - { - input_events[num_events].unicode = unicode; - input_events[num_events].key = key; - input_events[num_events].flags = flags; - num_events++; - } -} - -int inp_num_events() -{ - return num_events; -} - -void inp_clear_events() -{ - num_events = 0; -} - -INPUT_EVENT inp_get_event(int index) -{ - if(index < 0 || index >= num_events) - { - INPUT_EVENT e = {0,0}; - return e; - } - - return input_events[index]; -} - -void inp_init() -{ - SDL_EnableUNICODE(1); - SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); -} - -void inp_mouse_mode_absolute() -{ - SDL_ShowCursor(1); - input_grabbed = 0; - if(config.inp_grab) - SDL_WM_GrabInput(SDL_GRAB_OFF); -} - -void inp_mouse_mode_relative() -{ - SDL_ShowCursor(0); - input_grabbed = 1; - if(config.inp_grab) - SDL_WM_GrabInput(SDL_GRAB_ON); -} - -int inp_mouse_doubleclick() -{ - return release_delta < (time_freq() >> 2); -} - -void inp_clear_key_states() -{ - mem_zero(input_state, sizeof(input_state)); - mem_zero(input_count, sizeof(input_count)); -} - -int inp_key_presses(int key) -{ - return input_count[input_current][key].presses; -} - -int inp_key_releases(int key) -{ - return input_count[input_current][key].releases; -} - -int inp_key_state(int key) -{ - return input_state[input_current][key]; -} - -int inp_key_pressed(int key) { return input_state[input_current][key]; } -int inp_key_was_pressed(int key) { return input_state[input_current^1][key]; } -int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } -int inp_button_pressed(int button) { return input_state[input_current][button]; } - -void inp_update() -{ - int i; - - if(input_grabbed && !Graphics()->WindowActive()) - inp_mouse_mode_absolute(); - - /*if(!input_grabbed && Graphics()->WindowActive()) - inp_mouse_mode_relative();*/ - - /* clear and begin count on the other one */ - input_current^=1; - mem_zero(&input_count[input_current], sizeof(input_count[input_current])); - mem_zero(&input_state[input_current], sizeof(input_state[input_current])); - - { - Uint8 *state = SDL_GetKeyState(&i); - if(i >= KEY_LAST) - i = KEY_LAST-1; - mem_copy(input_state[input_current], state, i); - } - - /* these states must always be updated manually because they are not in the GetKeyState from SDL */ - i = SDL_GetMouseState(NULL, NULL); - if(i&SDL_BUTTON(1)) input_state[input_current][KEY_MOUSE_1] = 1; /* 1 is left */ - if(i&SDL_BUTTON(3)) input_state[input_current][KEY_MOUSE_2] = 1; /* 3 is right */ - if(i&SDL_BUTTON(2)) input_state[input_current][KEY_MOUSE_3] = 1; /* 2 is middle */ - if(i&SDL_BUTTON(4)) input_state[input_current][KEY_MOUSE_4] = 1; - if(i&SDL_BUTTON(5)) input_state[input_current][KEY_MOUSE_5] = 1; - if(i&SDL_BUTTON(6)) input_state[input_current][KEY_MOUSE_6] = 1; - if(i&SDL_BUTTON(7)) input_state[input_current][KEY_MOUSE_7] = 1; - if(i&SDL_BUTTON(8)) input_state[input_current][KEY_MOUSE_8] = 1; - - { - SDL_Event event; - - while(SDL_PollEvent(&event)) - { - int key = -1; - int action = INPFLAG_PRESS; - switch (event.type) - { - /* handle keys */ - case SDL_KEYDOWN: - /*if(event.key.keysym.unicode < 255) */ - add_event(event.key.keysym.unicode, 0, 0); - key = event.key.keysym.sym; - break; - case SDL_KEYUP: - action = INPFLAG_RELEASE; - key = event.key.keysym.sym; - break; - - /* handle mouse buttons */ - case SDL_MOUSEBUTTONUP: - action = INPFLAG_RELEASE; - - if(event.button.button == 1) - { - release_delta = time_get() - last_release; - last_release = time_get(); - } - - /* fall through */ - case SDL_MOUSEBUTTONDOWN: - if(event.button.button == SDL_BUTTON_LEFT) key = KEY_MOUSE_1; - if(event.button.button == SDL_BUTTON_RIGHT) key = KEY_MOUSE_2; - if(event.button.button == SDL_BUTTON_MIDDLE) key = KEY_MOUSE_3; - if(event.button.button == SDL_BUTTON_WHEELUP) key = KEY_MOUSE_WHEEL_UP; - if(event.button.button == SDL_BUTTON_WHEELDOWN) key = KEY_MOUSE_WHEEL_DOWN; - if(event.button.button == 6) key = KEY_MOUSE_6; - if(event.button.button == 7) key = KEY_MOUSE_7; - if(event.button.button == 8) key = KEY_MOUSE_8; - break; - - /* other messages */ - case SDL_QUIT: - /* TODO: cleaner exit */ - exit(0); - break; - } - - /* */ - if(key != -1) - { - input_count[input_current][key].presses++; - if(action == INPFLAG_PRESS) - input_state[input_current][key] = 1; - add_event(0, key, action); - } - - } - } -} diff --git a/src/engine/client/ec_snd.cpp b/src/engine/client/ec_snd.cpp deleted file mode 100644 index 3baea982..00000000 --- a/src/engine/client/ec_snd.cpp +++ /dev/null @@ -1,471 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <engine/e_config.h> -#include <engine/e_engine.h> - -#include "SDL.h" - -extern "C" { // wavpack - #include <engine/external/wavpack/wavpack.h> -} -#include <stdio.h> -#include <stdlib.h> -#include <math.h> - -enum -{ - NUM_SAMPLES = 512, - NUM_VOICES = 64, - NUM_CHANNELS = 16, - - MAX_FRAMES = 1024 -}; - -typedef struct -{ - short *data; - int num_frames; - int rate; - int channels; - int loop_start; - int loop_end; -} SAMPLE; - -typedef struct -{ - int vol; - int pan; -} CHANNEL; - -typedef struct -{ - SAMPLE *snd; - CHANNEL *channel; - int tick; - int vol; /* 0 - 255 */ - int flags; - int x, y; -} VOICE; - -static SAMPLE samples[NUM_SAMPLES] = { {0} }; -static VOICE voices[NUM_VOICES] = { {0} }; -static CHANNEL channels[NUM_CHANNELS] = { {255, 0} }; - -static LOCK sound_lock = 0; -static int sound_enabled = 0; - -static int center_x = 0; -static int center_y = 0; - -static int mixing_rate = 48000; -static volatile int sound_volume = 100; - -static int next_voice = 0; - -void snd_set_channel(int cid, float vol, float pan) -{ - channels[cid].vol = (int)(vol*255.0f); - channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */ -} - -static int play(int cid, int sid, int flags, float x, float y) -{ - int vid = -1; - int i; - - lock_wait(sound_lock); - - /* search for voice */ - for(i = 0; i < NUM_VOICES; i++) - { - int id = (next_voice + i) % NUM_VOICES; - if(!voices[id].snd) - { - vid = id; - next_voice = id+1; - break; - } - } - - /* voice found, use it */ - if(vid != -1) - { - voices[vid].snd = &samples[sid]; - voices[vid].channel = &channels[cid]; - voices[vid].tick = 0; - voices[vid].vol = 255; - voices[vid].flags = flags; - voices[vid].x = (int)x; - voices[vid].y = (int)y; - } - - lock_release(sound_lock); - return vid; -} - -int snd_play_at(int cid, int sid, int flags, float x, float y) -{ - return play(cid, sid, flags|SNDFLAG_POS, x, y); -} - -int snd_play(int cid, int sid, int flags) -{ - return play(cid, sid, flags, 0, 0); -} - -void snd_stop(int vid) -{ - /* TODO: a nice fade out */ - lock_wait(sound_lock); - voices[vid].snd = 0; - lock_release(sound_lock); -} - -/* TODO: there should be a faster way todo this */ -static short int2short(int i) -{ - if(i > 0x7fff) - return 0x7fff; - else if(i < -0x7fff) - return -0x7fff; - return i; -} - -static int iabs(int i) -{ - if(i<0) - return -i; - return i; -} - -static void mix(short *final_out, unsigned frames) -{ - int mix_buffer[MAX_FRAMES*2] = {0}; - int master_vol; - - /* aquire lock while we are mixing */ - lock_wait(sound_lock); - - master_vol = sound_volume; - - for(unsigned i = 0; i < NUM_VOICES; i++) - { - if(voices[i].snd) - { - /* mix voice */ - VOICE *v = &voices[i]; - int *out = mix_buffer; - - int step = v->snd->channels; /* setup input sources */ - short *in_l = &v->snd->data[v->tick*step]; - short *in_r = &v->snd->data[v->tick*step+1]; - - unsigned end = v->snd->num_frames-v->tick; - - int rvol = v->channel->vol; - int lvol = v->channel->vol; - - /* make sure that we don't go outside the sound data */ - if(frames < end) - end = frames; - - /* check if we have a mono sound */ - if(v->snd->channels == 1) - in_r = in_l; - - /* volume calculation */ - if(v->flags&SNDFLAG_POS && v->channel->pan) - { - /* TODO: we should respect the channel panning value */ - const int range = 1500; /* magic value, remove */ - int dx = v->x - center_x; - int dy = v->y - center_y; - int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */ - int p = iabs(dx); - if(dist < range) - { - /* panning */ - if(dx > 0) - lvol = ((range-p)*lvol)/range; - else - rvol = ((range-p)*rvol)/range; - - /* falloff */ - lvol = (lvol*(range-dist))/range; - rvol = (rvol*(range-dist))/range; - } - else - { - lvol = 0; - rvol = 0; - } - } - - /* process all frames */ - for(unsigned s = 0; s < end; s++) - { - *out++ += (*in_l)*lvol; - *out++ += (*in_r)*rvol; - in_l += step; - in_r += step; - v->tick++; - } - - /* free voice if not used any more */ - if(v->tick == v->snd->num_frames) - v->snd = 0; - - } - } - - - /* release the lock */ - lock_release(sound_lock); - - { - /* clamp accumulated values */ - /* TODO: this seams slow */ - for(unsigned i = 0; i < frames; i++) - { - int j = i<<1; - int vl = ((mix_buffer[j]*master_vol)/101)>>8; - int vr = ((mix_buffer[j+1]*master_vol)/101)>>8; - - final_out[j] = int2short(vl); - final_out[j+1] = int2short(vr); - } - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(final_out, sizeof(short), frames * 2); -#endif -} - -static void sdlcallback(void *unused, Uint8 *stream, int len) -{ - mix((short *)stream, len/2/2); -} - -int snd_init() -{ - SDL_AudioSpec format; - - sound_lock = lock_create(); - - if(!config.snd_enable) - return 0; - - mixing_rate = config.snd_rate; - - /* Set 16-bit stereo audio at 22Khz */ - format.freq = config.snd_rate; - format.format = AUDIO_S16; - format.channels = 2; - format.samples = config.snd_buffer_size; - format.callback = sdlcallback; - format.userdata = NULL; - - /* Open the audio device and start playing sound! */ - if(SDL_OpenAudio(&format, NULL) < 0) - { - dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError()); - return -1; - } - else - dbg_msg("client/sound", "sound init successful"); - - SDL_PauseAudio(0); - - sound_enabled = 1; - snd_update(); /* update the volume */ - return 0; -} - -// TODO: Refactor: Remove this -extern IEngineGraphics *Graphics(); - - -int snd_update() -{ - /* update volume */ - int wanted_volume = config.snd_volume; - - if(!Graphics()->WindowActive() && config.snd_nonactive_mute) - wanted_volume = 0; - - if(wanted_volume != sound_volume) - { - lock_wait(sound_lock); - sound_volume = wanted_volume; - lock_release(sound_lock); - } - - return 0; -} - -int snd_shutdown() -{ - SDL_CloseAudio(); - lock_destroy(sound_lock); - return 0; -} - -int snd_alloc_id() -{ - /* TODO: linear search, get rid of it */ - unsigned sid; - for(sid = 0; sid < NUM_SAMPLES; sid++) - { - if(samples[sid].data == 0x0) - return sid; - } - - return -1; -} - -static void rate_convert(int sid) -{ - SAMPLE *snd = &samples[sid]; - int num_frames = 0; - short *new_data = 0; - int i; - - /* make sure that we need to convert this sound */ - if(!snd->data || snd->rate == mixing_rate) - return; - - /* allocate new data */ - num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate); - new_data = (short *)mem_alloc(num_frames*snd->channels*sizeof(short), 1); - - for(i = 0; i < num_frames; i++) - { - /* resample TODO: this should be done better, like linear atleast */ - float a = i/(float)num_frames; - int f = (int)(a*snd->num_frames); - if(f >= snd->num_frames) - f = snd->num_frames-1; - - /* set new data */ - if(snd->channels == 1) - new_data[i] = snd->data[f]; - else if(snd->channels == 2) - { - new_data[i*2] = snd->data[f*2]; - new_data[i*2+1] = snd->data[f*2+1]; - } - } - - /* free old data and apply new */ - mem_free(snd->data); - snd->data = new_data; - snd->num_frames = num_frames; -} - - -static IOHANDLE file = NULL; - -static int read_data(void *buffer, int size) -{ - return io_read(file, buffer, size); -} - -int snd_load_wv(const char *filename) -{ - SAMPLE *snd; - int sid = -1; - char error[100]; - WavpackContext *context; - - /* don't waste memory on sound when we are stress testing */ - if(config.dbg_stress) - return -1; - - /* no need to load sound when we are running with no sound */ - if(!sound_enabled) - return 1; - - file = engine_openfile(filename, IOFLAG_READ); /* TODO: use system.h stuff for this */ - if(!file) - { - dbg_msg("sound/wv", "failed to open %s", filename); - return -1; - } - - sid = snd_alloc_id(); - if(sid < 0) - return -1; - snd = &samples[sid]; - - context = WavpackOpenFileInput(read_data, error); - if (context) - { - int samples = WavpackGetNumSamples(context); - int bitspersample = WavpackGetBitsPerSample(context); - unsigned int samplerate = WavpackGetSampleRate(context); - int channels = WavpackGetNumChannels(context); - int *data; - int *src; - short *dst; - int i; - - snd->channels = channels; - snd->rate = samplerate; - - if(snd->channels > 2) - { - dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - /* - if(snd->rate != 44100) - { - dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); - return -1; - }*/ - - if(bitspersample != 16) - { - dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); - return -1; - } - - data = (int *)mem_alloc(4*samples*channels, 1); - WavpackUnpackSamples(context, data, samples); /* TODO: check return value */ - src = data; - - snd->data = (short *)mem_alloc(2*samples*channels, 1); - dst = snd->data; - - for (i = 0; i < samples*channels; i++) - *dst++ = (short)*src++; - - mem_free(data); - - snd->num_frames = samples; - snd->loop_start = -1; - snd->loop_end = -1; - } - else - { - dbg_msg("sound/wv", "failed to open %s: %s", filename, error); - } - - io_close(file); - file = NULL; - - if(config.debug) - dbg_msg("sound/wv", "loaded %s", filename); - - rate_convert(sid); - return sid; -} - -void snd_set_listener_pos(float x, float y) -{ - center_x = (int)x; - center_y = (int)y; -} diff --git a/src/engine/client/ec_srvbrowse.cpp b/src/engine/client/ec_srvbrowse.cpp deleted file mode 100644 index 1b04937a..00000000 --- a/src/engine/client/ec_srvbrowse.cpp +++ /dev/null @@ -1,736 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> -#include <engine/e_network.h> -#include <engine/e_client_interface.h> -#include <engine/e_config.h> -#include <engine/e_memheap.h> -#include <engine/e_engine.h> - -#include <mastersrv/mastersrv.h> - -#include <string.h> -#include <stdlib.h> - -extern CNetClient m_NetClient; - - -/* ------ server browse ---- */ -/* TODO: move all this to a separate file */ - -typedef struct SERVERENTRY_t SERVERENTRY; -struct SERVERENTRY_t -{ - NETADDR addr; - int64 request_time; - int got_info; - SERVER_INFO info; - - SERVERENTRY *next_ip; /* ip hashed list */ - - SERVERENTRY *prev_req; /* request list */ - SERVERENTRY *next_req; -}; - -static HEAP *serverlist_heap = 0; -static SERVERENTRY **serverlist = 0; -static int *sorted_serverlist = 0; - -enum -{ - MAX_FAVORITES=256 -}; - -static NETADDR favorite_servers[MAX_FAVORITES]; -static int num_favorite_servers = 0; - -static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ - -static SERVERENTRY *first_req_server = 0; /* request list */ -static SERVERENTRY *last_req_server = 0; -static int num_requests = 0; - -static int need_refresh = 0; - -static int num_sorted_servers = 0; -static int num_sorted_servers_capacity = 0; -static int num_servers = 0; -static int num_server_capacity = 0; - -static int sorthash = 0; -static char filterstring[64] = {0}; -static char filtergametypestring[128] = {0}; - -/* the token is to keep server refresh separated from each other */ -static int current_token = 1; - -static int serverlist_type = 0; -static int64 broadcast_time = 0; - -int client_serverbrowse_lan() { return serverlist_type == BROWSETYPE_LAN; } -int client_serverbrowse_num() { return num_servers; } - -SERVER_INFO *client_serverbrowse_get(int index) -{ - if(index < 0 || index >= num_servers) - return 0; - return &serverlist[index]->info; -} - -int client_serverbrowse_sorted_num() { return num_sorted_servers; } - -SERVER_INFO *client_serverbrowse_sorted_get(int index) -{ - if(index < 0 || index >= num_sorted_servers) - return 0; - return &serverlist[sorted_serverlist[index]]->info; -} - - -int client_serverbrowse_num_requests() -{ - return num_requests; -} - -static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.name, b->info.name); -} - -static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.map, b->info.map); -} - -static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - if(a->info.latency > b->info.latency) return 1; - if(a->info.latency < b->info.latency) return -1; - return 0; -} - -static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.gametype, b->info.gametype); -} - -static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - if(a->info.progression > b->info.progression) return 1; - if(a->info.progression < b->info.progression) return -1; - return 0; -} - -static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - if(a->info.num_players > b->info.num_players) return 1; - if(a->info.num_players < b->info.num_players) return -1; - return 0; -} - -static void client_serverbrowse_filter() -{ - int i = 0, p = 0; - num_sorted_servers = 0; - - /* allocate the sorted list */ - if(num_sorted_servers_capacity < num_servers) - { - if(sorted_serverlist) - mem_free(sorted_serverlist); - num_sorted_servers_capacity = num_servers; - sorted_serverlist = (int *)mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); - } - - /* filter the servers */ - for(i = 0; i < num_servers; i++) - { - int filtered = 0; - - if(config.b_filter_empty && serverlist[i]->info.num_players == 0) - filtered = 1; - else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) - filtered = 1; - else if(config.b_filter_pw && serverlist[i]->info.flags&SRVFLAG_PASSWORD) - filtered = 1; - else if(config.b_filter_pure && (strcmp(serverlist[i]->info.gametype, "DM") != 0 && strcmp(serverlist[i]->info.gametype, "TDM") != 0 && strcmp(serverlist[i]->info.gametype, "CTF") != 0)) - filtered = 1; - else if(config.b_filter_pure_map && - !(strcmp(serverlist[i]->info.map, "dm1") == 0 || - strcmp(serverlist[i]->info.map, "dm2") == 0 || - strcmp(serverlist[i]->info.map, "dm6") == 0 || - strcmp(serverlist[i]->info.map, "dm7") == 0 || - strcmp(serverlist[i]->info.map, "dm8") == 0 || - strcmp(serverlist[i]->info.map, "dm9") == 0 || - strcmp(serverlist[i]->info.map, "ctf1") == 0 || - strcmp(serverlist[i]->info.map, "ctf2") == 0 || - strcmp(serverlist[i]->info.map, "ctf3") == 0 || - strcmp(serverlist[i]->info.map, "ctf4") == 0 || - strcmp(serverlist[i]->info.map, "ctf5") == 0) - ) - { - filtered = 1; - } - else if(config.b_filter_ping < serverlist[i]->info.latency) - filtered = 1; - else if(config.b_filter_compatversion && strncmp(serverlist[i]->info.version, modc_net_version(), 3) != 0) - filtered = 1; - else - { - if(config.b_filter_string[0] != 0) - { - int matchfound = 0; - - serverlist[i]->info.quicksearch_hit = 0; - - /* match against server name */ - if(str_find_nocase(serverlist[i]->info.name, config.b_filter_string)) - { - matchfound = 1; - serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_SERVERNAME; - } - - /* match against players */ - for(p = 0; p < serverlist[i]->info.num_players; p++) - { - if(str_find_nocase(serverlist[i]->info.players[p].name, config.b_filter_string)) - { - matchfound = 1; - serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_PLAYERNAME; - break; - } - } - - /* match against map */ - if(str_find_nocase(serverlist[i]->info.map, config.b_filter_string)) - { - matchfound = 1; - serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_MAPNAME; - } - - if(!matchfound) - filtered = 1; - } - - if(!filtered && config.b_filter_gametype[0] != 0) - { - /* match against game type */ - if(!str_find_nocase(serverlist[i]->info.gametype, config.b_filter_gametype)) - filtered = 1; - } - } - - if(filtered == 0) - sorted_serverlist[num_sorted_servers++] = i; - } -} - -static int client_serverbrowse_sorthash() -{ - int i = config.b_sort&0xf; - i |= config.b_filter_empty<<4; - i |= config.b_filter_full<<5; - i |= config.b_filter_pw<<6; - i |= config.b_sort_order<<7; - i |= config.b_filter_compatversion<<8; - i |= config.b_filter_pure<<9; - i |= config.b_filter_pure_map<<10; - i |= config.b_filter_ping<<16; - return i; -} - -static void client_serverbrowse_sort() -{ - int i; - - /* create filtered list */ - client_serverbrowse_filter(); - - /* sort */ - if(config.b_sort == BROWSESORT_NAME) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); - else if(config.b_sort == BROWSESORT_PING) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); - else if(config.b_sort == BROWSESORT_MAP) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); - else if(config.b_sort == BROWSESORT_NUMPLAYERS) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); - else if(config.b_sort == BROWSESORT_GAMETYPE) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); - else if(config.b_sort == BROWSESORT_PROGRESSION) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); - - /* invert the list if requested */ - if(config.b_sort_order) - { - for(i = 0; i < num_sorted_servers/2; i++) - { - int temp = sorted_serverlist[i]; - sorted_serverlist[i] = sorted_serverlist[num_sorted_servers-i-1]; - sorted_serverlist[num_sorted_servers-i-1] = temp; - } - } - - /* set indexes */ - for(i = 0; i < num_sorted_servers; i++) - serverlist[sorted_serverlist[i]]->info.sorted_index = i; - - str_copy(filtergametypestring, config.b_filter_gametype, sizeof(filtergametypestring)); - str_copy(filterstring, config.b_filter_string, sizeof(filterstring)); - sorthash = client_serverbrowse_sorthash(); -} - -static void client_serverbrowse_remove_request(SERVERENTRY *entry) -{ - if(entry->prev_req || entry->next_req || first_req_server == entry) - { - if(entry->prev_req) - entry->prev_req->next_req = entry->next_req; - else - first_req_server = entry->next_req; - - if(entry->next_req) - entry->next_req->prev_req = entry->prev_req; - else - last_req_server = entry->prev_req; - - entry->prev_req = 0; - entry->next_req = 0; - num_requests--; - } -} - -static SERVERENTRY *client_serverbrowse_find(NETADDR *addr) -{ - SERVERENTRY *entry = serverlist_ip[addr->ip[0]]; - - for(; entry; entry = entry->next_ip) - { - if(net_addr_comp(&entry->addr, addr) == 0) - return entry; - } - return (SERVERENTRY*)0; -} - -void client_serverbrowse_queuerequest(SERVERENTRY *entry) -{ - /* add it to the list of servers that we should request info from */ - entry->prev_req = last_req_server; - if(last_req_server) - last_req_server->next_req = entry; - else - first_req_server = entry; - last_req_server = entry; - - num_requests++; -} - -void client_serverbrowse_setinfo(SERVERENTRY *entry, SERVER_INFO *info) -{ - int fav = entry->info.favorite; - entry->info = *info; - entry->info.favorite = fav; - entry->info.netaddr = entry->addr; - - // all these are just for nice compability - if(entry->info.gametype[0] == '0' && entry->info.gametype[1] == 0) - str_copy(entry->info.gametype, "DM", sizeof(entry->info.gametype)); - else if(entry->info.gametype[0] == '1' && entry->info.gametype[1] == 0) - str_copy(entry->info.gametype, "TDM", sizeof(entry->info.gametype)); - else if(entry->info.gametype[0] == '2' && entry->info.gametype[1] == 0) - str_copy(entry->info.gametype, "CTF", sizeof(entry->info.gametype)); - - /*if(!request) - { - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - }*/ - - entry->got_info = 1; - client_serverbrowse_sort(); -} - -SERVERENTRY *client_serverbrowse_add(NETADDR *addr) -{ - int hash = addr->ip[0]; - SERVERENTRY *entry = 0; - int i; - - /* create new entry */ - entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); - mem_zero(entry, sizeof(SERVERENTRY)); - - /* set the info */ - entry->addr = *addr; - entry->info.netaddr = *addr; - - entry->info.latency = 999; - str_format(entry->info.address, sizeof(entry->info.address), "%d.%d.%d.%d:%d", - addr->ip[0], addr->ip[1], addr->ip[2], - addr->ip[3], addr->port); - str_format(entry->info.name, sizeof(entry->info.name), "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ - addr->ip[0], addr->ip[1], addr->ip[2], - addr->ip[3], addr->port); - - /*if(serverlist_type == BROWSETYPE_LAN) - entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();*/ - - /* check if it's a favorite */ - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(addr, &favorite_servers[i]) == 0) - entry->info.favorite = 1; - } - - /* add to the hash list */ - entry->next_ip = serverlist_ip[hash]; - serverlist_ip[hash] = entry; - - if(num_servers == num_server_capacity) - { - SERVERENTRY **newlist; - num_server_capacity += 100; - newlist = (SERVERENTRY **)mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); - mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); - mem_free(serverlist); - serverlist = newlist; - } - - /* add to list */ - serverlist[num_servers] = entry; - entry->info.server_index = num_servers; - num_servers++; - - return entry; -} - -void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info) -{ - SERVERENTRY *entry = 0; - if(type == BROWSESET_MASTER_ADD) - { - if(serverlist_type != BROWSETYPE_INTERNET) - return; - - if(!client_serverbrowse_find(addr)) - { - entry = client_serverbrowse_add(addr); - client_serverbrowse_queuerequest(entry); - } - } - else if(type == BROWSESET_FAV_ADD) - { - if(serverlist_type != BROWSETYPE_FAVORITES) - return; - - if(!client_serverbrowse_find(addr)) - { - entry = client_serverbrowse_add(addr); - client_serverbrowse_queuerequest(entry); - } - } - else if(type == BROWSESET_TOKEN) - { - if(token != current_token) - return; - - entry = client_serverbrowse_find(addr); - if(!entry) - entry = client_serverbrowse_add(addr); - if(entry) - { - client_serverbrowse_setinfo(entry, info); - if(serverlist_type == BROWSETYPE_LAN) - entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); - else - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - } - } - else if(type == BROWSESET_OLD_INTERNET) - { - entry = client_serverbrowse_find(addr); - if(entry) - { - client_serverbrowse_setinfo(entry, info); - - if(serverlist_type == BROWSETYPE_LAN) - entry->info.latency = (time_get()-broadcast_time)*1000/time_freq(); - else - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - } - } - else if(type == BROWSESET_OLD_LAN) - { - entry = client_serverbrowse_find(addr); - if(entry) - if(!entry) - entry = client_serverbrowse_add(addr); - if(entry) - client_serverbrowse_setinfo(entry, info); - } - - client_serverbrowse_sort(); -} - -void client_serverbrowse_refresh(int type) -{ - /* clear out everything */ - if(serverlist_heap) - memheap_destroy(serverlist_heap); - serverlist_heap = memheap_create(); - num_servers = 0; - num_sorted_servers = 0; - mem_zero(serverlist_ip, sizeof(serverlist_ip)); - first_req_server = 0; - last_req_server = 0; - num_requests = 0; - - /* next token */ - current_token = (current_token+1)&0xff; - - /* */ - serverlist_type = type; - - if(type == BROWSETYPE_LAN) - { - unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1]; - CNetChunk Packet; - int i; - - mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); - Buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token; - - Packet.m_ClientID = -1; - mem_zero(&Packet, sizeof(Packet)); - Packet.m_Address.ip[0] = 255; - Packet.m_Address.ip[1] = 255; - Packet.m_Address.ip[2] = 255; - Packet.m_Address.ip[3] = 255; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_DataSize = sizeof(Buffer); - Packet.m_pData = Buffer; - broadcast_time = time_get(); - - for(i = 8303; i <= 8310; i++) - { - Packet.m_Address.port = i; - m_NetClient.Send(&Packet); - } - - if(config.debug) - dbg_msg("client", "broadcasting for servers"); - } - else if(type == BROWSETYPE_INTERNET) - need_refresh = 1; - else if(type == BROWSETYPE_FAVORITES) - { - for(int i = 0; i < num_favorite_servers; i++) - client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0); - } -} - -static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry) -{ - /*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/ - CNetChunk Packet; - - if(config.debug) - { - dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", - addr->ip[0], addr->ip[1], addr->ip[2], - addr->ip[3], addr->port); - } - - /*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); - buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/ - - Packet.m_ClientID = -1; - Packet.m_Address = *addr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - /*p.data_size = sizeof(buffer); - p.data = buffer; - netclient_send(net, &p);*/ - - /* send old requtest style aswell */ - Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO); - Packet.m_pData = SERVERBROWSE_OLD_GETINFO; - m_NetClient.Send(&Packet); - - if(entry) - entry->request_time = time_get(); -} - -void client_serverbrowse_request(NETADDR *addr) -{ - client_serverbrowse_request_impl(addr, 0); -} - - -void client_serverbrowse_update() -{ - int64 timeout = time_freq(); - int64 now = time_get(); - int count; - SERVERENTRY *entry, *next; - - /* do server list requests */ - if(need_refresh && !mastersrv_refreshing()) - { - NETADDR addr; - CNetChunk Packet; - int i; - - need_refresh = 0; - - mem_zero(&Packet, sizeof(Packet)); - Packet.m_ClientID = -1; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST); - Packet.m_pData = SERVERBROWSE_GETLIST; - - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - addr = mastersrv_get(i); - if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) - continue; - - Packet.m_Address = addr; - m_NetClient.Send(&Packet); - } - - if(config.debug) - dbg_msg("client", "requesting server list"); - } - - /* do timeouts */ - entry = first_req_server; - while(1) - { - if(!entry) /* no more entries */ - break; - - next = entry->next_req; - - if(entry->request_time && entry->request_time+timeout < now) - { - /* timeout */ - client_serverbrowse_remove_request(entry); - num_requests--; - } - - entry = next; - } - - /* do timeouts */ - entry = first_req_server; - count = 0; - while(1) - { - if(!entry) /* no more entries */ - break; - - /* no more then 10 concurrent requests */ - if(count == config.b_max_requests) - break; - - if(entry->request_time == 0) - client_serverbrowse_request_impl(&entry->addr, entry); - - count++; - entry = entry->next_req; - } - - /* check if we need to resort */ - /* TODO: remove the strcmp */ - if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0 || strcmp(filtergametypestring, config.b_filter_gametype) != 0) - client_serverbrowse_sort(); -} - - -int client_serverbrowse_isfavorite(NETADDR addr) -{ - /* search for the address */ - int i; - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(&addr, &favorite_servers[i]) == 0) - return 1; - } - return 0; -} - -void client_serverbrowse_addfavorite(NETADDR addr) -{ - int i; - SERVERENTRY *entry; - - if(num_favorite_servers == MAX_FAVORITES) - return; - - /* make sure that we don't already have the server in our list */ - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(&addr, &favorite_servers[i]) == 0) - return; - } - - /* add the server to the list */ - favorite_servers[num_favorite_servers++] = addr; - entry = client_serverbrowse_find(&addr); - if(entry) - entry->info.favorite = 1; - dbg_msg("", "added fav, %p", entry); -} - -void client_serverbrowse_removefavorite(NETADDR addr) -{ - int i; - SERVERENTRY *entry; - - for(i = 0; i < num_favorite_servers; i++) - { - if(net_addr_comp(&addr, &favorite_servers[i]) == 0) - { - mem_move(&favorite_servers[i], &favorite_servers[i+1], num_favorite_servers-(i+1)); - num_favorite_servers--; - - entry = client_serverbrowse_find(&addr); - if(entry) - entry->info.favorite = 0; - - return; - } - } -} - -void client_serverbrowse_save() -{ - int i; - char addrstr[128]; - char buffer[256]; - for(i = 0; i < num_favorite_servers; i++) - { - net_addr_str(&favorite_servers[i], addrstr, sizeof(addrstr)); - str_format(buffer, sizeof(buffer), "add_favorite %s", addrstr); - engine_config_write_line(buffer); - } -} - - -int client_serverbrowse_refreshingmasters() -{ - return mastersrv_refreshing(); -} diff --git a/src/engine/client/editor.h b/src/engine/client/editor.h deleted file mode 100644 index 336a46d4..00000000 --- a/src/engine/client/editor.h +++ /dev/null @@ -1,10 +0,0 @@ - -class IEditor -{ -public: - virtual ~IEditor() {} - virtual void Init(class IGraphics *pGraphics) = 0; - virtual void UpdateAndRender() = 0; -}; - -extern IEditor *CreateEditor(); diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp new file mode 100644 index 00000000..7c355fb8 --- /dev/null +++ b/src/engine/client/graphics.cpp @@ -0,0 +1,938 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info + +#include <base/detect.h> + +#include "SDL.h" + +#ifdef CONF_FAMILY_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include <windows.h> +#endif + +#ifdef CONF_PLATFORM_MACOSX + #include <OpenGL/gl.h> + #include <OpenGL/glu.h> +#else + #include <GL/gl.h> + #include <GL/glu.h> +#endif + +#include <base/system.h> +#include <engine/external/pnglite/pnglite.h> + +#include <engine/shared/engine.h> +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/storage.h> +#include <engine/keys.h> + +#include <math.h> + +#include "graphics.h" + +// compressed textures +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 + +#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + + +static CVideoMode g_aFakeModes[] = { + {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, + {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, + {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, + {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, + {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, + {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, + {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, + {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, + {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, + {2048,1536,8,8,8}, + + {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, + {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, + {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, + {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, + {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, + {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, + {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, + {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, + {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, + {2048,1536,5,6,5} +}; + +void CGraphics_OpenGL::Flush() +{ + if(m_NumVertices == 0) + return; + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glVertexPointer(3, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + if(m_RenderEnable) + { + if(m_Drawing == DRAWING_QUADS) + glDrawArrays(GL_QUADS, 0, m_NumVertices); + else if(m_Drawing == DRAWING_LINES) + glDrawArrays(GL_LINES, 0, m_NumVertices); + } + + // Reset pointer + m_NumVertices = 0; +} + +void CGraphics_OpenGL::AddVertices(int Count) +{ + m_NumVertices += Count; + if((m_NumVertices + Count) >= MAX_VERTICES) + Flush(); +} + +void CGraphics_OpenGL::Rotate4(CPoint *pCenter, CVertex *pPoints) +{ + float c = cosf(m_Rotation); + float s = sinf(m_Rotation); + float x, y; + int i; + + for(i = 0; i < 4; i++) + { + x = pPoints[i].m_Pos.x - pCenter->x; + y = pPoints[i].m_Pos.y - pCenter->y; + pPoints[i].m_Pos.x = x * c - y * s + pCenter->x; + pPoints[i].m_Pos.y = x * s + y * c + pCenter->y; + } +} + +unsigned char CGraphics_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset) +{ + return (pData[(v*w+u)*4+Offset]+ + pData[(v*w+u+1)*4+Offset]+ + pData[((v+1)*w+u)*4+Offset]+ + pData[((v+1)*w+u+1)*4+Offset])/4; +} + +CGraphics_OpenGL::CGraphics_OpenGL() +{ + m_NumVertices = 0; + + m_ScreenX0 = 0; + m_ScreenY0 = 0; + m_ScreenX1 = 0; + m_ScreenY1 = 0; + + m_ScreenWidth = -1; + m_ScreenHeight = -1; + + m_Rotation = 0; + m_Drawing = 0; + m_InvalidTexture = 0; + + m_TextureMemoryUsage = 0; + + m_RenderEnable = true; + m_DoScreenshot = false; +} + +void CGraphics_OpenGL::ClipEnable(int x, int y, int w, int h) +{ + //if(no_gfx) return; + glScissor(x, ScreenHeight()-(y+h), w, h); + glEnable(GL_SCISSOR_TEST); +} + +void CGraphics_OpenGL::ClipDisable() +{ + //if(no_gfx) return; + glDisable(GL_SCISSOR_TEST); +} + +void CGraphics_OpenGL::BlendNone() +{ + glDisable(GL_BLEND); +} + +void CGraphics_OpenGL::BlendNormal() +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void CGraphics_OpenGL::BlendAdditive() +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); +} + +int CGraphics_OpenGL::MemoryUsage() const +{ + return m_TextureMemoryUsage; +} + +void CGraphics_OpenGL::MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY) +{ + m_ScreenX0 = TopLeftX; + m_ScreenY0 = TopLeftY; + m_ScreenX1 = BottomRightX; + m_ScreenY1 = BottomRightY; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(TopLeftX, BottomRightX, BottomRightY, TopLeftY, 1.0f, 10.f); +} + +void CGraphics_OpenGL::GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY) +{ + *pTopLeftX = m_ScreenX0; + *pTopLeftY = m_ScreenY0; + *pBottomRightX = m_ScreenX1; + *pBottomRightY = m_ScreenY1; +} + +void CGraphics_OpenGL::LinesBegin() +{ + dbg_assert(m_Drawing == 0, "called begin twice"); + m_Drawing = DRAWING_LINES; + SetColor(1,1,1,1); +} + +void CGraphics_OpenGL::LinesEnd() +{ + dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin"); + Flush(); + m_Drawing = 0; +} + +void CGraphics_OpenGL::LinesDraw(const CLineItem *pArray, int Num) +{ + dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin"); + + for(int i = 0; i < Num; ++i) + { + m_aVertices[m_NumVertices + 2*i].m_Pos.x = pArray[i].m_X0; + m_aVertices[m_NumVertices + 2*i].m_Pos.y = pArray[i].m_Y0; + m_aVertices[m_NumVertices + 2*i].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices + 2*i].m_Color = m_aColor[0]; + + m_aVertices[m_NumVertices + 2*i + 1].m_Pos.x = pArray[i].m_X1; + m_aVertices[m_NumVertices + 2*i + 1].m_Pos.y = pArray[i].m_Y1; + m_aVertices[m_NumVertices + 2*i + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 2*i + 1].m_Color = m_aColor[1]; + } + + AddVertices(2*Num); +} + +int CGraphics_OpenGL::UnloadTexture(int Index) +{ + if(Index == m_InvalidTexture) + return 0; + + if(Index < 0) + return 0; + + glDeleteTextures(1, &m_aTextures[Index].m_Tex); + m_aTextures[Index].m_Next = m_FirstFreeTexture; + m_TextureMemoryUsage -= m_aTextures[Index].m_MemSize; + m_FirstFreeTexture = Index; + return 0; +} + + +int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) +{ + int Mipmap = 1; + unsigned char *pTexData = (unsigned char *)pData; + unsigned char *pTmpData = 0; + int Oglformat = 0; + int StoreOglformat = 0; + int Tex = 0; + + // don't waste memory on texture if we are stress testing + if(g_Config.m_DbgStress) + return m_InvalidTexture; + + // grab texture + Tex = m_FirstFreeTexture; + m_FirstFreeTexture = m_aTextures[Tex].m_Next; + m_aTextures[Tex].m_Next = -1; + + // resample if needed + if(!(Flags&TEXLOAD_NORESAMPLE) && g_Config.m_GfxTextureQuality==0) + { + if(Width > 16 && Height > 16 && Format == CImageInfo::FORMAT_RGBA) + { + unsigned char *pTmpData; + int c = 0; + int x, y; + + pTmpData = (unsigned char *)mem_alloc(Width*Height*4, 1); + + Width/=2; + Height/=2; + + for(y = 0; y < Height; y++) + for(x = 0; x < Width; x++, c++) + { + pTmpData[c*4] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 0); + pTmpData[c*4+1] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 1); + pTmpData[c*4+2] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 2); + pTmpData[c*4+3] = Sample(Width*2, Height*2, pTexData, x*2,y*2, 3); + } + pTexData = pTmpData; + } + } + + Oglformat = GL_RGBA; + if(Format == CImageInfo::FORMAT_RGB) + Oglformat = GL_RGB; + else if(Format == CImageInfo::FORMAT_ALPHA) + Oglformat = GL_ALPHA; + + // upload texture + if(g_Config.m_GfxTextureCompression) + { + StoreOglformat = GL_COMPRESSED_RGBA_ARB; + if(StoreFormat == CImageInfo::FORMAT_RGB) + StoreOglformat = GL_COMPRESSED_RGB_ARB; + else if(StoreFormat == CImageInfo::FORMAT_ALPHA) + StoreOglformat = GL_COMPRESSED_ALPHA_ARB; + } + else + { + StoreOglformat = GL_RGBA; + if(StoreFormat == CImageInfo::FORMAT_RGB) + StoreOglformat = GL_RGB; + else if(StoreFormat == CImageInfo::FORMAT_ALPHA) + StoreOglformat = GL_ALPHA; + } + + glGenTextures(1, &m_aTextures[Tex].m_Tex); + glBindTexture(GL_TEXTURE_2D, m_aTextures[Tex].m_Tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + gluBuild2DMipmaps(GL_TEXTURE_2D, StoreOglformat, Width, Height, Oglformat, GL_UNSIGNED_BYTE, pTexData); + + // calculate memory usage + { + int PixelSize = 4; + if(StoreFormat == CImageInfo::FORMAT_RGB) + PixelSize = 3; + else if(StoreFormat == CImageInfo::FORMAT_ALPHA) + PixelSize = 1; + + m_aTextures[Tex].m_MemSize = Width*Height*PixelSize; + if(Mipmap) + { + while(Width > 2 && Height > 2) + { + Width>>=1; + Height>>=1; + m_aTextures[Tex].m_MemSize += Width*Height*PixelSize; + } + } + } + + m_TextureMemoryUsage += m_aTextures[Tex].m_MemSize; + mem_free(pTmpData); + return Tex; +} + +// simple uncompressed RGBA loaders +int CGraphics_OpenGL::LoadTexture(const char *pFilename, int StoreFormat, int Flags) +{ + int l = str_length(pFilename); + int Id; + CImageInfo Img; + + if(l < 3) + return -1; + if(LoadPNG(&Img, pFilename)) + { + if (StoreFormat == CImageInfo::FORMAT_AUTO) + StoreFormat = Img.m_Format; + + Id = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, StoreFormat, Flags); + mem_free(Img.m_pData); + return Id; + } + + return m_InvalidTexture; +} + +int CGraphics_OpenGL::LoadPNG(CImageInfo *pImg, const char *pFilename) +{ + char aCompleteFilename[512]; + unsigned char *pBuffer; + png_t Png; // ignore_convention + + // open file for reading + png_init(0,0); // ignore_convention + + IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, aCompleteFilename, sizeof(aCompleteFilename)); + if(File) + io_close(File); + + if(png_open_file(&Png, aCompleteFilename) != PNG_NO_ERROR) // ignore_convention + { + dbg_msg("game/png", "failed to open file. filename='%s'", aCompleteFilename); + return 0; + } + + if(Png.depth != 8 || (Png.color_type != PNG_TRUECOLOR && Png.color_type != PNG_TRUECOLOR_ALPHA)) // ignore_convention + { + dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename); + png_close_file(&Png); // ignore_convention + return 0; + } + + pBuffer = (unsigned char *)mem_alloc(Png.width * Png.height * Png.bpp, 1); // ignore_convention + png_get_data(&Png, pBuffer); // ignore_convention + png_close_file(&Png); // ignore_convention + + pImg->m_Width = Png.width; // ignore_convention + pImg->m_Height = Png.height; // ignore_convention + if(Png.color_type == PNG_TRUECOLOR) // ignore_convention + pImg->m_Format = CImageInfo::FORMAT_RGB; + else if(Png.color_type == PNG_TRUECOLOR_ALPHA) // ignore_convention + pImg->m_Format = CImageInfo::FORMAT_RGBA; + pImg->m_pData = pBuffer; + return 1; +} + +void CGraphics_OpenGL::ScreenshotDirect(const char *pFilename) +{ + // fetch image data + int y; + int w = m_ScreenWidth; + int h = m_ScreenHeight; + unsigned char *pPixelData = (unsigned char *)mem_alloc(w*(h+1)*4, 1); + unsigned char *pTempRow = pPixelData+w*h*4; + glReadPixels(0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pPixelData); + + // flip the pixel because opengl works from bottom left corner + for(y = 0; y < h/2; y++) + { + mem_copy(pTempRow, pPixelData+y*w*4, w*4); + mem_copy(pPixelData+y*w*4, pPixelData+(h-y-1)*w*4, w*4); + mem_copy(pPixelData+(h-y-1)*w*4, pTempRow,w*4); + } + + // find filename + { + char aWholePath[1024]; + png_t Png; // ignore_convention + + IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, aWholePath, sizeof(aWholePath)); + if(File) + io_close(File); + + // save png + dbg_msg("client", "saved screenshot to '%s'", aWholePath); + png_open_file_write(&Png, aWholePath); // ignore_convention + png_set_data(&Png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pPixelData); // ignore_convention + png_close_file(&Png); // ignore_convention + } + + // clean up + mem_free(pPixelData); +} + +void CGraphics_OpenGL::TextureSet(int TextureID) +{ + dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin"); + if(TextureID == -1) + { + glDisable(GL_TEXTURE_2D); + } + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_aTextures[TextureID].m_Tex); + } +} + +void CGraphics_OpenGL::Clear(float r, float g, float b) +{ + glClearColor(r,g,b,0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void CGraphics_OpenGL::QuadsBegin() +{ + dbg_assert(m_Drawing == 0, "called quads_begin twice"); + m_Drawing = DRAWING_QUADS; + + QuadsSetSubset(0,0,1,1); + QuadsSetRotation(0); + SetColor(1,1,1,1); +} + +void CGraphics_OpenGL::QuadsEnd() +{ + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin"); + Flush(); + m_Drawing = 0; +} + +void CGraphics_OpenGL::QuadsSetRotation(float Angle) +{ + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin"); + m_Rotation = Angle; +} + +void CGraphics_OpenGL::SetColorVertex(const CColorVertex *pArray, int Num) +{ + dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin"); + + for(int i = 0; i < Num; ++i) + { + m_aColor[pArray[i].m_Index].r = pArray[i].m_R; + m_aColor[pArray[i].m_Index].g = pArray[i].m_G; + m_aColor[pArray[i].m_Index].b = pArray[i].m_B; + m_aColor[pArray[i].m_Index].a = pArray[i].m_A; + } +} + +void CGraphics_OpenGL::SetColor(float r, float g, float b, float a) +{ + dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin"); + CColorVertex Array[4] = { + CColorVertex(0, r, g, b, a), + CColorVertex(1, r, g, b, a), + CColorVertex(2, r, g, b, a), + CColorVertex(3, r, g, b, a)}; + SetColorVertex(Array, 4); +} + +void CGraphics_OpenGL::QuadsSetSubset(float TlU, float TlV, float BrU, float BrV) +{ + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin"); + + m_aTexture[0].u = TlU; m_aTexture[1].u = BrU; + m_aTexture[0].v = TlV; m_aTexture[1].v = TlV; + + m_aTexture[3].u = TlU; m_aTexture[2].u = BrU; + m_aTexture[3].v = BrV; m_aTexture[2].v = BrV; +} + +void CGraphics_OpenGL::QuadsSetSubsetFree( + float x0, float y0, float x1, float y1, + float x2, float y2, float x3, float y3) +{ + m_aTexture[0].u = x0; m_aTexture[0].v = y0; + m_aTexture[1].u = x1; m_aTexture[1].v = y1; + m_aTexture[2].u = x2; m_aTexture[2].v = y2; + m_aTexture[3].u = x3; m_aTexture[3].v = y3; +} + +void CGraphics_OpenGL::QuadsDraw(CQuadItem *pArray, int Num) +{ + for(int i = 0; i < Num; ++i) + { + pArray[i].m_X -= pArray[i].m_Width/2; + pArray[i].m_Y -= pArray[i].m_Height/2; + } + + QuadsDrawTL(pArray, Num); +} + +void CGraphics_OpenGL::QuadsDrawTL(const CQuadItem *pArray, int Num) +{ + CPoint Center; + + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin"); + + for(int i = 0; i < Num; ++i) + { + Center.x = pArray[i].m_X + pArray[i].m_Width/2; + Center.y = pArray[i].m_Y + pArray[i].m_Height/2; + Center.z = 0; + + m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X; + m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y; + m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0]; + + m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X + pArray[i].m_Width; + m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y; + m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1]; + + m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X + pArray[i].m_Width; + m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height; + m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[2]; + m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[2]; + + m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X; + m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height; + m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[3]; + m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[3]; + + if(m_Rotation != 0) + Rotate4(&Center, &m_aVertices[m_NumVertices + 4*i]); + } + + AddVertices(4*Num); +} + +void CGraphics_OpenGL::QuadsDrawFreeform(const CFreeformItem *pArray, int Num) +{ + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); + + for(int i = 0; i < Num; ++i) + { + m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X0; + m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y0; + m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0]; + + m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X1; + m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y1; + m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1]; + + m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X3; + m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y3; + m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[3]; + m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[3]; + + m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X2; + m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y2; + m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[2]; + m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[2]; + } + + AddVertices(4*Num); +} + +void CGraphics_OpenGL::QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) +{ + float StartX = x; + + QuadsBegin(); + SetColor(r,g,b,a); + + while(*pText) + { + char c = *pText; + pText++; + + if(c == '\n') + { + x = StartX; + y += Size; + } + else + { + QuadsSetSubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + CQuadItem QuadItem(x, y, Size, Size); + QuadsDrawTL(&QuadItem, 1); + x += Size/2; + } + } + + QuadsEnd(); +} + +bool CGraphics_OpenGL::Init() +{ + m_pStorage = Kernel()->RequestInterface<IStorage>(); + + // Set all z to -5.0f + for(int i = 0; i < MAX_VERTICES; i++) + m_aVertices[i].m_Pos.z = -5.0f; + + // init textures + m_FirstFreeTexture = 0; + for(int i = 0; i < MAX_TEXTURES; i++) + m_aTextures[i].m_Next = i+1; + m_aTextures[MAX_TEXTURES-1].m_Next = -1; + + // set some default settings + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glAlphaFunc(GL_GREATER, 0); + glEnable(GL_ALPHA_TEST); + glDepthMask(0); + + // create null texture, will get id=0 + static const unsigned char aNullTextureData[] = { + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + }; + + m_InvalidTexture = LoadTextureRaw(4,4,CImageInfo::FORMAT_RGBA,aNullTextureData,CImageInfo::FORMAT_RGBA,TEXLOAD_NORESAMPLE); + dbg_msg("", "invalid texture id: %d %d", m_InvalidTexture, m_aTextures[m_InvalidTexture].m_Tex); + + return true; +} + +int CGraphics_SDL::TryInit() +{ + const SDL_VideoInfo *pInfo; + int Flags = SDL_OPENGL; + + m_ScreenWidth = g_Config.m_GfxScreenWidth; + m_ScreenHeight = g_Config.m_GfxScreenHeight; + + pInfo = SDL_GetVideoInfo(); + SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); + + // set flags + Flags = SDL_OPENGL; + Flags |= SDL_GL_DOUBLEBUFFER; + Flags |= SDL_HWPALETTE; + if(g_Config.m_DbgResizable) + Flags |= SDL_RESIZABLE; + + if(pInfo->hw_available) // ignore_convention + Flags |= SDL_HWSURFACE; + else + Flags |= SDL_SWSURFACE; + + if(pInfo->blit_hw) // ignore_convention + Flags |= SDL_HWACCEL; + + if(g_Config.m_GfxFullscreen) + Flags |= SDL_FULLSCREEN; + + // set gl attributes + if(g_Config.m_GfxFsaaSamples) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, g_Config.m_GfxFsaaSamples); + } + else + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + } + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, g_Config.m_GfxVsync); + + // set caption + SDL_WM_SetCaption("Teeworlds", "Teeworlds"); + + // create window + m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags); + if(m_pScreenSurface == NULL) + { + dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); + return -1; + } + + return 0; +} + + +int CGraphics_SDL::InitWindow() +{ + if(TryInit() == 0) + return 0; + + // try disabling fsaa + while(g_Config.m_GfxFsaaSamples) + { + g_Config.m_GfxFsaaSamples--; + + if(g_Config.m_GfxFsaaSamples) + dbg_msg("gfx", "lowering FSAA to %d and trying again", g_Config.m_GfxFsaaSamples); + else + dbg_msg("gfx", "disabling FSAA and trying again"); + + if(TryInit() == 0) + return 0; + } + + // try lowering the resolution + if(g_Config.m_GfxScreenWidth != 640 || g_Config.m_GfxScreenHeight != 480) + { + dbg_msg("gfx", "setting resolution to 640x480 and trying again"); + g_Config.m_GfxScreenWidth = 640; + g_Config.m_GfxScreenHeight = 480; + + if(TryInit() == 0) + return 0; + } + + dbg_msg("gfx", "out of ideas. failed to init graphics"); + + return -1; +} + + +CGraphics_SDL::CGraphics_SDL() +{ + m_pScreenSurface = 0; +} + +bool CGraphics_SDL::Init() +{ + { + int Systems = SDL_INIT_VIDEO; + + if(g_Config.m_SndEnable) + Systems |= SDL_INIT_AUDIO; + + if(g_Config.m_ClEventthread) + Systems |= SDL_INIT_EVENTTHREAD; + + if(SDL_Init(Systems) < 0) + { + dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError()); + return true; + } + } + + atexit(SDL_Quit); // ignore_convention + + #ifdef CONF_FAMILY_WINDOWS + if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) // ignore_convention + putenv("SDL_VIDEO_WINDOW_POS=8,27"); // ignore_convention + #endif + + if(InitWindow() != 0) + return true; + + SDL_ShowCursor(0); + + CGraphics_OpenGL::Init(); + + MapScreen(0,0,g_Config.m_GfxScreenWidth, g_Config.m_GfxScreenHeight); + return false; +} + +void CGraphics_SDL::Shutdown() +{ + // TODO: SDL, is this correct? + SDL_Quit(); +} + +void CGraphics_SDL::Minimize() +{ + SDL_WM_IconifyWindow(); +} + +void CGraphics_SDL::Maximize() +{ + // TODO: SDL +} + +int CGraphics_SDL::WindowActive() +{ + return SDL_GetAppState()&SDL_APPINPUTFOCUS; +} + +int CGraphics_SDL::WindowOpen() +{ + return SDL_GetAppState()&SDL_APPACTIVE; + +} + +void CGraphics_SDL::TakeScreenshot() +{ + m_DoScreenshot = true; +} + +void CGraphics_SDL::Swap() +{ + if(m_DoScreenshot) + { + // find filename + char aFilename[128]; + static int Index = 1; + + for(; Index < 10000; Index++) + { + IOHANDLE io; + str_format(aFilename, sizeof(aFilename), "screenshots/screenshot%05d.png", Index); + io = m_pStorage->OpenFile(aFilename, IOFLAG_READ); + if(io) + io_close(io); + else + break; + } + + ScreenshotDirect(aFilename); + m_DoScreenshot = false; + } + + SDL_GL_SwapBuffers(); + + if(g_Config.m_GfxFinish) + glFinish(); +} + + +int CGraphics_SDL::GetVideoModes(CVideoMode *pModes, int MaxModes) +{ + int NumModes = sizeof(g_aFakeModes)/sizeof(CVideoMode); + SDL_Rect **ppModes; + + if(g_Config.m_GfxDisplayAllModes) + { + int Count = sizeof(g_aFakeModes)/sizeof(CVideoMode); + mem_copy(pModes, g_aFakeModes, sizeof(g_aFakeModes)); + if(MaxModes < Count) + Count = MaxModes; + return Count; + } + + // TODO: fix this code on osx or windows + + ppModes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN); + if(ppModes == NULL) + { + // no modes + NumModes = 0; + } + else if(ppModes == (SDL_Rect**)-1) + { + // all modes + } + else + { + NumModes = 0; + for(int i = 0; ppModes[i]; ++i) + { + if(NumModes == MaxModes) + break; + pModes[NumModes].m_Width = ppModes[i]->w; + pModes[NumModes].m_Height = ppModes[i]->h; + pModes[NumModes].m_Red = 8; + pModes[NumModes].m_Green = 8; + pModes[NumModes].m_Blue = 8; + NumModes++; + } + } + + return NumModes; +} + +extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); } diff --git a/src/engine/client/graphics.h b/src/engine/client/graphics.h index 80dbf1b9..ff4c3562 100644 --- a/src/engine/client/graphics.h +++ b/src/engine/client/graphics.h @@ -1,69 +1,143 @@ -#include "../e_if_gfx.h" +#ifndef ENGINE_CLIENT_GRAPHICS_H +#define ENGINE_CLIENT_GRAPHICS_H -class IGraphics +class CGraphics_OpenGL : public IEngineGraphics { protected: - int m_ScreenWidth; - int m_ScreenHeight; -public: - virtual ~IGraphics() {} - - int ScreenWidth() const { return m_ScreenWidth; } - int ScreenHeight() const { return m_ScreenHeight; } - float ScreenAspect() const { return (float)ScreenWidth()/(float)ScreenHeight(); } - - virtual void Clear(float r, float g, float b) = 0; - - virtual void ClipEnable(int x, int y, int w, int h) = 0; - virtual void ClipDisable() = 0; - - virtual void MapScreen(float tl_x, float tl_y, float br_x, float br_y) = 0; - virtual void GetScreen(float *tl_x, float *tl_y, float *br_x, float *br_y) = 0; + class IStorage *m_pStorage; - virtual void BlendNone() = 0; - virtual void BlendNormal() = 0; - virtual void BlendAdditive() = 0; - - virtual int LoadPNG(IMAGE_INFO *pImg, const char *pFilename) =0; - virtual int UnloadTexture(int Index) = 0; - virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) = 0; - virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) = 0; - virtual void TextureSet(int TextureID) = 0; + // + typedef struct { float x, y, z; } CPoint; + typedef struct { float u, v; } CTexCoord; + typedef struct { float r, g, b, a; } CColor; + + typedef struct + { + CPoint m_Pos; + CTexCoord m_Tex; + CColor m_Color; + } CVertex; - virtual void LinesBegin() = 0; - virtual void LinesEnd() = 0; - virtual void LinesDraw(float x0, float y0, float x1, float y1) = 0; + enum + { + MAX_VERTICES = 32*1024, + MAX_TEXTURES = 1024*4, + + DRAWING_QUADS=1, + DRAWING_LINES=2 + }; + + CVertex m_aVertices[MAX_VERTICES]; + int m_NumVertices; + + CColor m_aColor[4]; + CTexCoord m_aTexture[4]; + + bool m_RenderEnable; + + float m_Rotation; + int m_Drawing; + bool m_DoScreenshot; + + float m_ScreenX0; + float m_ScreenY0; + float m_ScreenX1; + float m_ScreenY1; + + int m_InvalidTexture; + + struct CTexture + { + GLuint m_Tex; + int m_MemSize; + int m_Flags; + int m_Next; + }; + + CTexture m_aTextures[MAX_TEXTURES]; + int m_FirstFreeTexture; + int m_TextureMemoryUsage; + + void Flush(); + void AddVertices(int Count); + void Rotate4(CPoint *pCenter, CVertex *pPoints); - virtual void QuadsBegin() = 0; - virtual void QuadsEnd() = 0; - virtual void QuadsSetRotation(float Angle) = 0; - virtual void QuadsSetSubset(float tl_u, float tl_v, float br_u, float br_v) = 0; - virtual void QuadsSetSubsetFree(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) = 0; + static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset); +public: + CGraphics_OpenGL(); - virtual void QuadsDraw(float x, float y, float w, float h) = 0; - virtual void QuadsDrawTL(float x, float y, float w, float h) = 0; - virtual void QuadsDrawFreeform(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) = 0; - virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) = 0; + virtual void ClipEnable(int x, int y, int w, int h); + virtual void ClipDisable(); + + virtual void BlendNone(); + virtual void BlendNormal(); + virtual void BlendAdditive(); + + virtual int MemoryUsage() const; + + virtual void MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY); + virtual void GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY); + + virtual void LinesBegin(); + virtual void LinesEnd(); + virtual void LinesDraw(const CLineItem *pArray, int Num); - virtual void SetColorVertex(int i, float r, float g, float b, float a) = 0; - virtual void SetColor(float r, float g, float b, float a) = 0; + virtual int UnloadTexture(int Index); + virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags); + + // simple uncompressed RGBA loaders + virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags); + virtual int LoadPNG(CImageInfo *pImg, const char *pFilename); + + void ScreenshotDirect(const char *pFilename); + + virtual void TextureSet(int TextureID); + + virtual void Clear(float r, float g, float b); + + virtual void QuadsBegin(); + virtual void QuadsEnd(); + virtual void QuadsSetRotation(float Angle); + + virtual void SetColorVertex(const CColorVertex *pArray, int Num); + virtual void SetColor(float r, float g, float b, float a); + + virtual void QuadsSetSubset(float TlU, float TlV, float BrU, float BrV); + virtual void QuadsSetSubsetFree( + float x0, float y0, float x1, float y1, + float x2, float y2, float x3, float y3); + + virtual void QuadsDraw(CQuadItem *pArray, int Num); + virtual void QuadsDrawTL(const CQuadItem *pArray, int Num); + virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num); + virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText); - virtual void TakeScreenshot() = 0; + virtual bool Init(); }; -class IEngineGraphics : public IGraphics +class CGraphics_SDL : public CGraphics_OpenGL { -public: - virtual bool Init() = 0; - virtual void Shutdown() = 0; - virtual void Swap() = 0; - - virtual void Minimize() = 0; - virtual void Maximize() = 0; + SDL_Surface *m_pScreenSurface; - virtual int WindowActive() = 0; - virtual int WindowOpen() = 0; + int TryInit(); + int InitWindow(); +public: + CGraphics_SDL(); + + virtual bool Init(); + virtual void Shutdown(); + + virtual void Minimize(); + virtual void Maximize(); + + virtual int WindowActive(); + virtual int WindowOpen(); + + virtual void TakeScreenshot(); + virtual void Swap(); + + virtual int GetVideoModes(CVideoMode *pModes, int MaxModes); }; -extern IEngineGraphics *CreateEngineGraphics(); +#endif diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp new file mode 100644 index 00000000..9f546226 --- /dev/null +++ b/src/engine/client/input.cpp @@ -0,0 +1,208 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include "SDL.h" + +#include <base/system.h> +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/input.h> +#include <engine/keys.h> + +#include "input.h" + +//print >>f, "int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, \"-?-\")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; }" + +// this header is protected so you don't include it from anywere +#define KEYS_INCLUDE +#include "keynames.h" +#undef KEYS_INCLUDE + +void CInput::AddEvent(int Unicode, int Key, int Flags) +{ + if(m_NumEvents != INPUT_BUFFER_SIZE) + { + m_aInputEvents[m_NumEvents].m_Unicode = Unicode; + m_aInputEvents[m_NumEvents].m_Key = Key; + m_aInputEvents[m_NumEvents].m_Flags = Flags; + m_NumEvents++; + } +} + +CInput::CInput() +{ + mem_zero(m_aInputCount, sizeof(m_aInputCount)); + mem_zero(m_aInputState, sizeof(m_aInputState)); + mem_zero(m_Keys, sizeof(m_Keys)); + + m_InputCurrent = 0; + m_InputGrabbed = 0; + + m_LastRelease = 0; + m_ReleaseDelta = -1; + + m_NumEvents = 0; +} + +void CInput::Init() +{ + m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>(); + SDL_EnableUNICODE(1); + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); +} + +void CInput::MouseRelative(int *x, int *y) +{ + int nx = 0, ny = 0; + float Sens = g_Config.m_InpMousesens/100.0f; + + if(g_Config.m_InpGrab) + SDL_GetRelativeMouseState(&nx, &ny); + else + { + if(m_InputGrabbed) + { + SDL_GetMouseState(&nx,&ny); + SDL_WarpMouse(Graphics()->ScreenWidth()/2,Graphics()->ScreenHeight()/2); + nx -= Graphics()->ScreenWidth()/2; ny -= Graphics()->ScreenHeight()/2; + } + } + + *x = nx*Sens; + *y = ny*Sens; +} + +void CInput::MouseModeAbsolute() +{ + SDL_ShowCursor(1); + m_InputGrabbed = 0; + if(g_Config.m_InpGrab) + SDL_WM_GrabInput(SDL_GRAB_OFF); +} + +void CInput::MouseModeRelative() +{ + SDL_ShowCursor(0); + m_InputGrabbed = 1; + if(g_Config.m_InpGrab) + SDL_WM_GrabInput(SDL_GRAB_ON); +} + +int CInput::MouseDoubleClick() +{ + return m_ReleaseDelta < (time_freq() >> 2); +} + +void CInput::ClearKeyStates() +{ + mem_zero(m_aInputState, sizeof(m_aInputState)); + mem_zero(m_aInputCount, sizeof(m_aInputCount)); +} + +int CInput::KeyState(int Key) +{ + return m_aInputState[m_InputCurrent][Key]; +} + +void CInput::Update() +{ + if(m_InputGrabbed && !Graphics()->WindowActive()) + MouseModeAbsolute(); + + /*if(!input_grabbed && Graphics()->WindowActive()) + Input()->MouseModeRelative();*/ + + // clear and begin count on the other one + m_InputCurrent^=1; + mem_zero(&m_aInputCount[m_InputCurrent], sizeof(m_aInputCount[m_InputCurrent])); + mem_zero(&m_aInputState[m_InputCurrent], sizeof(m_aInputState[m_InputCurrent])); + + { + int i; + Uint8 *pState = SDL_GetKeyState(&i); + if(i >= KEY_LAST) + i = KEY_LAST-1; + mem_copy(m_aInputState[m_InputCurrent], pState, i); + } + + // these states must always be updated manually because they are not in the GetKeyState from SDL + int i = SDL_GetMouseState(NULL, NULL); + if(i&SDL_BUTTON(1)) m_aInputState[m_InputCurrent][KEY_MOUSE_1] = 1; // 1 is left + if(i&SDL_BUTTON(3)) m_aInputState[m_InputCurrent][KEY_MOUSE_2] = 1; // 3 is right + if(i&SDL_BUTTON(2)) m_aInputState[m_InputCurrent][KEY_MOUSE_3] = 1; // 2 is middle + if(i&SDL_BUTTON(4)) m_aInputState[m_InputCurrent][KEY_MOUSE_4] = 1; + if(i&SDL_BUTTON(5)) m_aInputState[m_InputCurrent][KEY_MOUSE_5] = 1; + if(i&SDL_BUTTON(6)) m_aInputState[m_InputCurrent][KEY_MOUSE_6] = 1; + if(i&SDL_BUTTON(7)) m_aInputState[m_InputCurrent][KEY_MOUSE_7] = 1; + if(i&SDL_BUTTON(8)) m_aInputState[m_InputCurrent][KEY_MOUSE_8] = 1; + + { + SDL_Event Event; + + while(SDL_PollEvent(&Event)) + { + int Key = -1; + int Action = IInput::FLAG_PRESS; + switch (Event.type) + { + // handle keys + case SDL_KEYDOWN: + AddEvent(Event.key.keysym.unicode, 0, 0); // ignore_convention + if(Event.key.keysym.unicode != 0 && Event.key.keysym.unicode < 256) // ignore_convention + { + Key = Event.key.keysym.unicode; // ignore_convention + m_Keys[Event.key.keysym.sym] = Event.key.keysym.unicode; // ignore_convention + } + else + Key = Event.key.keysym.sym; // ignore_convention + break; + case SDL_KEYUP: + Action = IInput::FLAG_RELEASE; + if(m_Keys[Event.key.keysym.sym] != 0) // ignore_convention + Key = m_Keys[Event.key.keysym.sym]; // ignore_convention + else + Key = Event.key.keysym.sym; // ignore_convention + break; + + // handle mouse buttons + case SDL_MOUSEBUTTONUP: + Action = IInput::FLAG_RELEASE; + + if(Event.button.button == 1) // ignore_convention + { + m_ReleaseDelta = time_get() - m_LastRelease; + m_LastRelease = time_get(); + } + + // fall through + case SDL_MOUSEBUTTONDOWN: + if(Event.button.button == SDL_BUTTON_LEFT) Key = KEY_MOUSE_1; // ignore_convention + if(Event.button.button == SDL_BUTTON_RIGHT) Key = KEY_MOUSE_2; // ignore_convention + if(Event.button.button == SDL_BUTTON_MIDDLE) Key = KEY_MOUSE_3; // ignore_convention + if(Event.button.button == SDL_BUTTON_WHEELUP) Key = KEY_MOUSE_WHEEL_UP; // ignore_convention + if(Event.button.button == SDL_BUTTON_WHEELDOWN) Key = KEY_MOUSE_WHEEL_DOWN; // ignore_convention + if(Event.button.button == 6) Key = KEY_MOUSE_6; // ignore_convention + if(Event.button.button == 7) Key = KEY_MOUSE_7; // ignore_convention + if(Event.button.button == 8) Key = KEY_MOUSE_8; // ignore_convention + break; + + // other messages + case SDL_QUIT: + // TODO: cleaner exit + exit(0); // ignore_convention + break; + } + + // + if(Key != -1) + { + m_aInputCount[m_InputCurrent][Key].m_Presses++; + if(Action == IInput::FLAG_PRESS) + m_aInputState[m_InputCurrent][Key] = 1; + AddEvent(0, Key, Action); + } + + } + } +} + + +IEngineInput *CreateEngineInput() { return new CInput; } diff --git a/src/engine/client/input.h b/src/engine/client/input.h new file mode 100644 index 00000000..bf7739ab --- /dev/null +++ b/src/engine/client/input.h @@ -0,0 +1,37 @@ +#ifndef ENGINE_CLIENT_INPUT_H +#define ENGINE_CLIENT_INPUT_H + +class CInput : public IEngineInput +{ + IEngineGraphics *m_pGraphics; + + int m_InputGrabbed; + + unsigned int m_LastRelease; + unsigned int m_ReleaseDelta; + + int m_Keys[1024]; + + void AddEvent(int Unicode, int Key, int Flags); + + IEngineGraphics *Graphics() { return m_pGraphics; } + +public: + CInput(); + + virtual void Init(); + + virtual void MouseRelative(int *x, int *y); + virtual void MouseModeAbsolute(); + virtual void MouseModeRelative(); + virtual int MouseDoubleClick(); + + void ClearKeyStates(); + int KeyState(int Key); + + int ButtonPressed(int Button) { return m_aInputState[m_InputCurrent][Button]; } + + virtual void Update(); +}; + +#endif diff --git a/src/engine/e_keynames.cpp b/src/engine/client/keynames.h index c81744b9..2f159a5a 100644 --- a/src/engine/e_keynames.cpp +++ b/src/engine/client/keynames.h @@ -1,10 +1,13 @@ /* AUTO GENERATED! DO NOT EDIT MANUALLY! */ +#ifndef KEYS_INCLUDE +#error do not include this header! +#endif + #include <string.h> -static const char key_strings[512][16] = +const char g_aaKeyStrings[512][16] = { - "first", "&1", "&2", @@ -519,6 +522,3 @@ static const char key_strings[512][16] = "&511", }; -const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; } -int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; } - diff --git a/src/engine/client/sound.cpp b/src/engine/client/sound.cpp new file mode 100644 index 00000000..df8fa66b --- /dev/null +++ b/src/engine/client/sound.cpp @@ -0,0 +1,485 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> +#include <engine/shared/config.h> + +#include "SDL.h" + +#include "sound.h" + +extern "C" { // wavpack + #include <engine/external/wavpack/wavpack.h> +} +#include <math.h> + +enum +{ + NUM_SAMPLES = 512, + NUM_VOICES = 64, + NUM_CHANNELS = 16, + + MAX_FRAMES = 1024 +}; + +struct CSample +{ + short *m_pData; + int m_NumFrames; + int m_Rate; + int m_Channels; + int m_LoopStart; + int m_LoopEnd; +}; + +struct CChannel +{ + int m_Vol; + int m_Pan; +} ; + +struct CVoice +{ + CSample *m_pSample; + CChannel *m_pChannel; + int m_Tick; + int m_Vol; // 0 - 255 + int m_Flags; + int m_X, m_Y; +} ; + +static CSample m_aSamples[NUM_SAMPLES] = { {0} }; +static CVoice m_aVoices[NUM_VOICES] = { {0} }; +static CChannel m_aChannels[NUM_CHANNELS] = { {255, 0} }; + +static LOCK m_SoundLock = 0; +static int m_SoundEnabled = 0; + +static int m_CenterX = 0; +static int m_CenterY = 0; + +static int m_MixingRate = 48000; +static volatile int m_SoundVolume = 100; + +static int m_NextVoice = 0; + + +// TODO: there should be a faster way todo this +static short Int2Short(int i) +{ + if(i > 0x7fff) + return 0x7fff; + else if(i < -0x7fff) + return -0x7fff; + return i; +} + +static int IntAbs(int i) +{ + if(i<0) + return -i; + return i; +} + +static void Mix(short *pFinalOut, unsigned Frames) +{ + int aMixBuffer[MAX_FRAMES*2] = {0}; + int MasterVol; + + // aquire lock while we are mixing + lock_wait(m_SoundLock); + + MasterVol = m_SoundVolume; + + for(unsigned i = 0; i < NUM_VOICES; i++) + { + if(m_aVoices[i].m_pSample) + { + // mix voice + CVoice *v = &m_aVoices[i]; + int *pOut = aMixBuffer; + + int Step = v->m_pSample->m_Channels; // setup input sources + short *pInL = &v->m_pSample->m_pData[v->m_Tick*Step]; + short *pInR = &v->m_pSample->m_pData[v->m_Tick*Step+1]; + + unsigned End = v->m_pSample->m_NumFrames-v->m_Tick; + + int Rvol = v->m_pChannel->m_Vol; + int Lvol = v->m_pChannel->m_Vol; + + // make sure that we don't go outside the sound data + if(Frames < End) + End = Frames; + + // check if we have a mono sound + if(v->m_pSample->m_Channels == 1) + pInR = pInL; + + // volume calculation + if(v->m_Flags&ISound::FLAG_POS && v->m_pChannel->m_Pan) + { + // TODO: we should respect the channel panning value + const int Range = 1500; // magic value, remove + int dx = v->m_X - m_CenterX; + int dy = v->m_Y - m_CenterY; + int Dist = sqrtf((float)dx*dx+dy*dy); // float here. nasty + int p = IntAbs(dx); + if(Dist < Range) + { + // panning + if(dx > 0) + Lvol = ((Range-p)*Lvol)/Range; + else + Rvol = ((Range-p)*Rvol)/Range; + + // falloff + Lvol = (Lvol*(Range-Dist))/Range; + Rvol = (Rvol*(Range-Dist))/Range; + } + else + { + Lvol = 0; + Rvol = 0; + } + } + + // process all frames + for(unsigned s = 0; s < End; s++) + { + *pOut++ += (*pInL)*Lvol; + *pOut++ += (*pInR)*Rvol; + pInL += Step; + pInR += Step; + v->m_Tick++; + } + + // free voice if not used any more + if(v->m_Tick == v->m_pSample->m_NumFrames) + v->m_pSample = 0; + + } + } + + + // release the lock + lock_release(m_SoundLock); + + { + // clamp accumulated values + // TODO: this seams slow + for(unsigned i = 0; i < Frames; i++) + { + int j = i<<1; + int vl = ((aMixBuffer[j]*MasterVol)/101)>>8; + int vr = ((aMixBuffer[j+1]*MasterVol)/101)>>8; + + pFinalOut[j] = Int2Short(vl); + pFinalOut[j+1] = Int2Short(vr); + } + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(pFinalOut, sizeof(short), Frames * 2); +#endif +} + +static void SdlCallback(void *pUnused, Uint8 *pStream, int Len) +{ + (void)pUnused; + Mix((short *)pStream, Len/2/2); +} + + +int CSound::Init() +{ + m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>(); + m_pStorage = Kernel()->RequestInterface<IStorage>(); + + SDL_AudioSpec Format; + + m_SoundLock = lock_create(); + + if(!g_Config.m_SndEnable) + return 0; + + m_MixingRate = g_Config.m_SndRate; + + // Set 16-bit stereo audio at 22Khz + Format.freq = g_Config.m_SndRate; // ignore_convention + Format.format = AUDIO_S16; // ignore_convention + Format.channels = 2; // ignore_convention + Format.samples = g_Config.m_SndBufferSize; // ignore_convention + Format.callback = SdlCallback; // ignore_convention + Format.userdata = NULL; // ignore_convention + + // Open the audio device and start playing sound! + if(SDL_OpenAudio(&Format, NULL) < 0) + { + dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError()); + return -1; + } + else + dbg_msg("client/sound", "sound init successful"); + + SDL_PauseAudio(0); + + m_SoundEnabled = 1; + Update(); // update the volume + return 0; +} + +int CSound::Update() +{ + // update volume + int WantedVolume = g_Config.m_SndVolume; + + if(!m_pGraphics->WindowActive() && g_Config.m_SndNonactiveMute) + WantedVolume = 0; + + if(WantedVolume != m_SoundVolume) + { + lock_wait(m_SoundLock); + m_SoundVolume = WantedVolume; + lock_release(m_SoundLock); + } + + return 0; +} + +int CSound::Shutdown() +{ + SDL_CloseAudio(); + lock_destroy(m_SoundLock); + return 0; +} + +int CSound::AllocId() +{ + // TODO: linear search, get rid of it + for(unsigned SampleId = 0; SampleId < NUM_SAMPLES; SampleId++) + { + if(m_aSamples[SampleId].m_pData == 0x0) + return SampleId; + } + + return -1; +} + +void CSound::RateConvert(int SampleId) +{ + CSample *pSample = &m_aSamples[SampleId]; + int NumFrames = 0; + short *pNewData = 0; + + // make sure that we need to convert this sound + if(!pSample->m_pData || pSample->m_Rate == m_MixingRate) + return; + + // allocate new data + NumFrames = (int)((pSample->m_NumFrames/(float)pSample->m_Rate)*m_MixingRate); + pNewData = (short *)mem_alloc(NumFrames*pSample->m_Channels*sizeof(short), 1); + + for(int i = 0; i < NumFrames; i++) + { + // resample TODO: this should be done better, like linear atleast + float a = i/(float)NumFrames; + int f = (int)(a*pSample->m_NumFrames); + if(f >= pSample->m_NumFrames) + f = pSample->m_NumFrames-1; + + // set new data + if(pSample->m_Channels == 1) + pNewData[i] = pSample->m_pData[f]; + else if(pSample->m_Channels == 2) + { + pNewData[i*2] = pSample->m_pData[f*2]; + pNewData[i*2+1] = pSample->m_pData[f*2+1]; + } + } + + // free old data and apply new + mem_free(pSample->m_pData); + pSample->m_pData = pNewData; + pSample->m_NumFrames = NumFrames; +} + +int CSound::ReadData(void *pBuffer, int Size) +{ + return io_read(ms_File, pBuffer, Size); +} + +int CSound::LoadWV(const char *pFilename) +{ + CSample *pSample; + int SampleId = -1; + char aError[100]; + WavpackContext *pContext; + + // don't waste memory on sound when we are stress testing + if(g_Config.m_DbgStress) + return -1; + + // no need to load sound when we are running with no sound + if(!m_SoundEnabled) + return 1; + + if(!m_pStorage) + return -1; + + ms_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ); // TODO: use system.h stuff for this + if(!ms_File) + { + dbg_msg("sound/wv", "failed to open %s", pFilename); + return -1; + } + + SampleId = AllocId(); + if(SampleId < 0) + return -1; + pSample = &m_aSamples[SampleId]; + + pContext = WavpackOpenFileInput(ReadData, aError); + if (pContext) + { + int m_aSamples = WavpackGetNumSamples(pContext); + int BitsPerSample = WavpackGetBitsPerSample(pContext); + unsigned int SampleRate = WavpackGetSampleRate(pContext); + int m_aChannels = WavpackGetNumChannels(pContext); + int *pData; + int *pSrc; + short *pDst; + int i; + + pSample->m_Channels = m_aChannels; + pSample->m_Rate = SampleRate; + + if(pSample->m_Channels > 2) + { + dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", pFilename); + return -1; + } + + /* + if(snd->rate != 44100) + { + dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); + return -1; + }*/ + + if(BitsPerSample != 16) + { + dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", BitsPerSample, pFilename); + return -1; + } + + pData = (int *)mem_alloc(4*m_aSamples*m_aChannels, 1); + WavpackUnpackSamples(pContext, pData, m_aSamples); // TODO: check return value + pSrc = pData; + + pSample->m_pData = (short *)mem_alloc(2*m_aSamples*m_aChannels, 1); + pDst = pSample->m_pData; + + for (i = 0; i < m_aSamples*m_aChannels; i++) + *pDst++ = (short)*pSrc++; + + mem_free(pData); + + pSample->m_NumFrames = m_aSamples; + pSample->m_LoopStart = -1; + pSample->m_LoopEnd = -1; + } + else + { + dbg_msg("sound/wv", "failed to open %s: %s", pFilename, aError); + } + + io_close(ms_File); + ms_File = NULL; + + if(g_Config.m_Debug) + dbg_msg("sound/wv", "loaded %s", pFilename); + + RateConvert(SampleId); + return SampleId; +} + +void CSound::SetListenerPos(float x, float y) +{ + m_CenterX = (int)x; + m_CenterY = (int)y; +} + + +void CSound::SetChannel(int ChannelId, float Vol, float Pan) +{ + m_aChannels[ChannelId].m_Vol = (int)(Vol*255.0f); + m_aChannels[ChannelId].m_Pan = (int)(Pan*255.0f); // TODO: this is only on and off right now +} + +int CSound::Play(int ChannelId, int SampleId, int Flags, float x, float y) +{ + int VoiceId = -1; + int i; + + lock_wait(m_SoundLock); + + // search for voice + for(i = 0; i < NUM_VOICES; i++) + { + int id = (m_NextVoice + i) % NUM_VOICES; + if(!m_aVoices[id].m_pSample) + { + VoiceId = id; + m_NextVoice = id+1; + break; + } + } + + // voice found, use it + if(VoiceId != -1) + { + m_aVoices[VoiceId].m_pSample = &m_aSamples[SampleId]; + m_aVoices[VoiceId].m_pChannel = &m_aChannels[ChannelId]; + m_aVoices[VoiceId].m_Tick = 0; + m_aVoices[VoiceId].m_Vol = 255; + m_aVoices[VoiceId].m_Flags = Flags; + m_aVoices[VoiceId].m_X = (int)x; + m_aVoices[VoiceId].m_Y = (int)y; + } + + lock_release(m_SoundLock); + return VoiceId; +} + +int CSound::PlayAt(int ChannelId, int SampleId, int Flags, float x, float y) +{ + return Play(ChannelId, SampleId, Flags|ISound::FLAG_POS, x, y); +} + +int CSound::Play(int ChannelId, int SampleId, int Flags) +{ + return Play(ChannelId, SampleId, Flags, 0, 0); +} + +void CSound::Stop(int VoiceId) +{ + // TODO: a nice fade out + lock_wait(m_SoundLock); + m_aVoices[VoiceId].m_pSample = 0; + lock_release(m_SoundLock); +} + +void CSound::StopAll() +{ + // TODO: a nice fade out + lock_wait(m_SoundLock); + for(int i = 0; i < NUM_VOICES; i++) + { + m_aVoices[i].m_pSample = 0; + } + lock_release(m_SoundLock); +} + +IOHANDLE CSound::ms_File = 0; + +IEngineSound *CreateEngineSound() { return new CSound; } + diff --git a/src/engine/client/sound.h b/src/engine/client/sound.h new file mode 100644 index 00000000..9c94c6ad --- /dev/null +++ b/src/engine/client/sound.h @@ -0,0 +1,39 @@ +#ifndef ENGINE_CLIENT_SOUND_H +#define ENGINE_CLIENT_SOUND_H + +#include <engine/sound.h> +#include <engine/storage.h> +#include <engine/graphics.h> +#include <engine/shared/engine.h> + +class CSound : public IEngineSound +{ +public: + IEngineGraphics *m_pGraphics; + IStorage *m_pStorage; + + virtual int Init(); + + int Update(); + int Shutdown(); + int AllocId(); + + static void RateConvert(int SampleId); + + // TODO: Refactor: clean this mess up + static IOHANDLE ms_File; + static int ReadData(void *pBuffer, int Size); + + virtual int LoadWV(const char *pFilename); + + virtual void SetListenerPos(float x, float y); + virtual void SetChannel(int ChannelId, float Vol, float Pan); + + int Play(int ChannelId, int SampleId, int Flags, float x, float y); + virtual int PlayAt(int ChannelId, int SampleId, int Flags, float x, float y); + virtual int Play(int ChannelId, int SampleId, int Flags); + virtual void Stop(int VoiceId); + virtual void StopAll(); +}; + +#endif diff --git a/src/engine/client/srvbrowse.cpp b/src/engine/client/srvbrowse.cpp new file mode 100644 index 00000000..e0997467 --- /dev/null +++ b/src/engine/client/srvbrowse.cpp @@ -0,0 +1,721 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <algorithm> // sort + +#include <base/system.h> +#include <engine/shared/network.h> +#include <engine/shared/protocol.h> +#include <engine/shared/config.h> +#include <engine/shared/memheap.h> +#include <engine/shared/engine.h> + +#include <engine/masterserver.h> +#include <engine/config.h> + +#include <mastersrv/mastersrv.h> + +#include "srvbrowse.h" + +class SortWrap +{ + typedef bool (CServerBrowser::*SortFunc)(int, int) const; + SortFunc m_pfnSort; + CServerBrowser *m_pThis; +public: + SortWrap(CServerBrowser *t, SortFunc f) : m_pfnSort(f), m_pThis(t) {} + bool operator()(int a, int b) { return (m_pThis->*m_pfnSort)(a, b); } +}; + +CServerBrowser::CServerBrowser() +{ + m_pMasterServer = 0; + m_ppServerlist = 0; + m_pSortedServerlist = 0; + + m_NumFavoriteServers = 0; + + mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp)); + + m_pFirstReqServer = 0; // request list + m_pLastReqServer = 0; + m_NumRequests = 0; + + m_NeedRefresh = 0; + + m_NumSortedServers = 0; + m_NumSortedServersCapacity = 0; + m_NumServers = 0; + m_NumServerCapacity = 0; + + m_Sorthash = 0; + m_aFilterString[0] = 0; + m_aFilterGametypeString[0] = 0; + + // the token is to keep server refresh separated from each other + m_CurrentToken = 1; + + m_ServerlistType = 0; + m_BroadcastTime = 0; +} + +void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVersion) +{ + m_pNetClient = pClient; + str_copy(m_aNetVersion, pNetVersion, sizeof(m_aNetVersion)); + m_pMasterServer = Kernel()->RequestInterface<IMasterServer>(); + IConfig *pConfig = Kernel()->RequestInterface<IConfig>(); + if(pConfig) + pConfig->RegisterCallback(ConfigSaveCallback, this); +} + +const CServerInfo *CServerBrowser::SortedGet(int Index) const +{ + if(Index < 0 || Index >= m_NumSortedServers) + return 0; + return &m_ppServerlist[m_pSortedServerlist[Index]]->m_Info; +} + + +bool CServerBrowser::SortCompareName(int Index1, int Index2) const +{ + CServerEntry *a = m_ppServerlist[Index1]; + CServerEntry *b = m_ppServerlist[Index2]; + return str_comp(a->m_Info.m_aName, b->m_Info.m_aName) < 0; +} + +bool CServerBrowser::SortCompareMap(int Index1, int Index2) const +{ + CServerEntry *a = m_ppServerlist[Index1]; + CServerEntry *b = m_ppServerlist[Index2]; + return str_comp(a->m_Info.m_aMap, b->m_Info.m_aMap) < 0; +} + +bool CServerBrowser::SortComparePing(int Index1, int Index2) const +{ + CServerEntry *a = m_ppServerlist[Index1]; + CServerEntry *b = m_ppServerlist[Index2]; + return a->m_Info.m_Latency < b->m_Info.m_Latency; +} + +bool CServerBrowser::SortCompareGametype(int Index1, int Index2) const +{ + CServerEntry *a = m_ppServerlist[Index1]; + CServerEntry *b = m_ppServerlist[Index2]; + return str_comp(a->m_Info.m_aGameType, b->m_Info.m_aGameType) < 0; +} + +bool CServerBrowser::SortCompareProgression(int Index1, int Index2) const +{ + CServerEntry *a = m_ppServerlist[Index1]; + CServerEntry *b = m_ppServerlist[Index2]; + return a->m_Info.m_Progression < b->m_Info.m_Progression; +} + +bool CServerBrowser::SortCompareNumPlayers(int Index1, int Index2) const +{ + CServerEntry *a = m_ppServerlist[Index1]; + CServerEntry *b = m_ppServerlist[Index2]; + return a->m_Info.m_NumPlayers < b->m_Info.m_NumPlayers; +} + +void CServerBrowser::Filter() +{ + int i = 0, p = 0; + m_NumSortedServers = 0; + + // allocate the sorted list + if(m_NumSortedServersCapacity < m_NumServers) + { + if(m_pSortedServerlist) + mem_free(m_pSortedServerlist); + m_NumSortedServersCapacity = m_NumServers; + m_pSortedServerlist = (int *)mem_alloc(m_NumSortedServersCapacity*sizeof(int), 1); + } + + // filter the servers + for(i = 0; i < m_NumServers; i++) + { + int Filtered = 0; + + if(g_Config.m_BrFilterEmpty && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) + Filtered = 1; + else if(g_Config.m_BrFilterFull && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) + Filtered = 1; + else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD) + Filtered = 1; + else if(g_Config.m_BrFilterPure && + (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 && + str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 && + str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0)) + { + Filtered = 1; + } + else if(g_Config.m_BrFilterPureMap && + !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0) + ) + { + Filtered = 1; + } + else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency) + Filtered = 1; + else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0) + Filtered = 1; + else + { + if(g_Config.m_BrFilterString[0] != 0) + { + int MatchFound = 0; + + m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0; + + // match against server name + if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString)) + { + MatchFound = 1; + m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME; + } + + // match against players + for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumPlayers; p++) + { + if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aPlayers[p].m_aName, g_Config.m_BrFilterString)) + { + MatchFound = 1; + m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYERNAME; + break; + } + } + + // match against map + if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString)) + { + MatchFound = 1; + m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME; + } + + if(!MatchFound) + Filtered = 1; + } + + if(!Filtered && g_Config.m_BrFilterGametype[0] != 0) + { + // match against game type + if(!str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) + Filtered = 1; + } + } + + if(Filtered == 0) + m_pSortedServerlist[m_NumSortedServers++] = i; + } +} + +int CServerBrowser::SortHash() const +{ + int i = g_Config.m_BrSort&0xf; + i |= g_Config.m_BrFilterEmpty<<4; + i |= g_Config.m_BrFilterFull<<5; + i |= g_Config.m_BrFilterPw<<6; + i |= g_Config.m_BrSortOrder<<7; + i |= g_Config.m_BrFilterCompatversion<<8; + i |= g_Config.m_BrFilterPure<<9; + i |= g_Config.m_BrFilterPureMap<<10; + i |= g_Config.m_BrFilterPing<<16; + return i; +} + +void CServerBrowser::Sort() +{ + int i; + + // create filtered list + Filter(); + + // sort + if(g_Config.m_BrSort == IServerBrowser::SORT_NAME) + std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareName)); + else if(g_Config.m_BrSort == IServerBrowser::SORT_PING) + std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortComparePing)); + else if(g_Config.m_BrSort == IServerBrowser::SORT_MAP) + std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareMap)); + else if(g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS) + std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareNumPlayers)); + else if(g_Config.m_BrSort == IServerBrowser::SORT_GAMETYPE) + std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareGametype)); + else if(g_Config.m_BrSort == IServerBrowser::SORT_PROGRESSION) + std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareProgression)); + + // invert the list if requested + if(g_Config.m_BrSortOrder) + { + for(i = 0; i < m_NumSortedServers/2; i++) + { + int Temp = m_pSortedServerlist[i]; + m_pSortedServerlist[i] = m_pSortedServerlist[m_NumSortedServers-i-1]; + m_pSortedServerlist[m_NumSortedServers-i-1] = Temp; + } + } + + // set indexes + for(i = 0; i < m_NumSortedServers; i++) + m_ppServerlist[m_pSortedServerlist[i]]->m_Info.m_SortedIndex = i; + + str_copy(m_aFilterGametypeString, g_Config.m_BrFilterGametype, sizeof(m_aFilterGametypeString)); + str_copy(m_aFilterString, g_Config.m_BrFilterString, sizeof(m_aFilterString)); + m_Sorthash = SortHash(); +} + +void CServerBrowser::RemoveRequest(CServerEntry *pEntry) +{ + if(pEntry->m_pPrevReq || pEntry->m_pNextReq || m_pFirstReqServer == pEntry) + { + if(pEntry->m_pPrevReq) + pEntry->m_pPrevReq->m_pNextReq = pEntry->m_pNextReq; + else + m_pFirstReqServer = pEntry->m_pNextReq; + + if(pEntry->m_pNextReq) + pEntry->m_pNextReq->m_pPrevReq = pEntry->m_pPrevReq; + else + m_pLastReqServer = pEntry->m_pPrevReq; + + pEntry->m_pPrevReq = 0; + pEntry->m_pNextReq = 0; + m_NumRequests--; + } +} + +CServerBrowser::CServerEntry *CServerBrowser::Find(const NETADDR &Addr) +{ + CServerEntry *pEntry = m_aServerlistIp[Addr.ip[0]]; + + for(; pEntry; pEntry = pEntry->m_pNextIp) + { + if(net_addr_comp(&pEntry->m_Addr, &Addr) == 0) + return pEntry; + } + return (CServerEntry*)0; +} + +void CServerBrowser::QueueRequest(CServerEntry *pEntry) +{ + // add it to the list of servers that we should request info from + pEntry->m_pPrevReq = m_pLastReqServer; + if(m_pLastReqServer) + m_pLastReqServer->m_pNextReq = pEntry; + else + m_pFirstReqServer = pEntry; + m_pLastReqServer = pEntry; + + m_NumRequests++; +} + +void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info) +{ + int Fav = pEntry->m_Info.m_Favorite; + pEntry->m_Info = Info; + pEntry->m_Info.m_Favorite = Fav; + pEntry->m_Info.m_NetAddr = pEntry->m_Addr; + + // all these are just for nice compability + if(pEntry->m_Info.m_aGameType[0] == '0' && pEntry->m_Info.m_aGameType[1] == 0) + str_copy(pEntry->m_Info.m_aGameType, "DM", sizeof(pEntry->m_Info.m_aGameType)); + else if(pEntry->m_Info.m_aGameType[0] == '1' && pEntry->m_Info.m_aGameType[1] == 0) + str_copy(pEntry->m_Info.m_aGameType, "TDM", sizeof(pEntry->m_Info.m_aGameType)); + else if(pEntry->m_Info.m_aGameType[0] == '2' && pEntry->m_Info.m_aGameType[1] == 0) + str_copy(pEntry->m_Info.m_aGameType, "CTF", sizeof(pEntry->m_Info.m_aGameType)); + + /*if(!request) + { + pEntry->m_Info.latency = (time_get()-pEntry->request_time)*1000/time_freq(); + RemoveRequest(pEntry); + }*/ + + pEntry->m_GotInfo = 1; + Sort(); +} + +CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr) +{ + int Hash = Addr.ip[0]; + CServerEntry *pEntry = 0; + int i; + + // create new pEntry + pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry)); + mem_zero(pEntry, sizeof(CServerEntry)); + + // set the info + pEntry->m_Addr = Addr; + pEntry->m_Info.m_NetAddr = Addr; + + pEntry->m_Info.m_Latency = 999; + str_format(pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), "%d.%d.%d.%d:%d", + Addr.ip[0], Addr.ip[1], Addr.ip[2], + Addr.ip[3], Addr.port); + str_format(pEntry->m_Info.m_aName, sizeof(pEntry->m_Info.m_aName), "\255%d.%d.%d.%d:%d", // the \255 is to make sure that it's sorted last + Addr.ip[0], Addr.ip[1], Addr.ip[2], + Addr.ip[3], Addr.port); + + /*if(serverlist_type == IServerBrowser::TYPE_LAN) + pEntry->m_Info.latency = (time_get()-broadcast_time)*1000/time_freq();*/ + + // check if it's a favorite + for(i = 0; i < m_NumFavoriteServers; i++) + { + if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0) + pEntry->m_Info.m_Favorite = 1; + } + + // add to the hash list + pEntry->m_pNextIp = m_aServerlistIp[Hash]; + m_aServerlistIp[Hash] = pEntry; + + if(m_NumServers == m_NumServerCapacity) + { + CServerEntry **ppNewlist; + m_NumServerCapacity += 100; + ppNewlist = (CServerEntry **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1); + mem_copy(ppNewlist, m_ppServerlist, m_NumServers*sizeof(CServerEntry*)); + mem_free(m_ppServerlist); + m_ppServerlist = ppNewlist; + } + + // add to list + m_ppServerlist[m_NumServers] = pEntry; + pEntry->m_Info.m_ServerIndex = m_NumServers; + m_NumServers++; + + return pEntry; +} + +void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo) +{ + CServerEntry *pEntry = 0; + if(Type == IServerBrowser::SET_MASTER_ADD) + { + if(m_ServerlistType != IServerBrowser::TYPE_INTERNET) + return; + + if(!Find(Addr)) + { + pEntry = Add(Addr); + QueueRequest(pEntry); + } + } + else if(Type == IServerBrowser::SET_FAV_ADD) + { + if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES) + return; + + if(!Find(Addr)) + { + pEntry = Add(Addr); + QueueRequest(pEntry); + } + } + else if(Type == IServerBrowser::SET_TOKEN) + { + if(Token != m_CurrentToken) + return; + + pEntry = Find(Addr); + if(!pEntry) + pEntry = Add(Addr); + if(pEntry) + { + SetInfo(pEntry, *pInfo); + if(m_ServerlistType == IServerBrowser::TYPE_LAN) + pEntry->m_Info.m_Latency = (time_get()-m_BroadcastTime)*1000/time_freq(); + else + pEntry->m_Info.m_Latency = (time_get()-pEntry->m_RequestTime)*1000/time_freq(); + RemoveRequest(pEntry); + } + } + else if(Type == IServerBrowser::SET_OLD_INTERNET) + { + pEntry = Find(Addr); + if(pEntry) + { + SetInfo(pEntry, *pInfo); + + if(m_ServerlistType == IServerBrowser::TYPE_LAN) + pEntry->m_Info.m_Latency = (time_get()-m_BroadcastTime)*1000/time_freq(); + else + pEntry->m_Info.m_Latency = (time_get()-pEntry->m_RequestTime)*1000/time_freq(); + RemoveRequest(pEntry); + } + } + else if(Type == IServerBrowser::SET_OLD_LAN) + { + pEntry = Find(Addr); + if(pEntry) + if(!pEntry) + pEntry = Add(Addr); + if(pEntry) + SetInfo(pEntry, *pInfo); + } + + Sort(); +} + +void CServerBrowser::Refresh(int Type) +{ + // clear out everything + m_ServerlistHeap.Reset(); + m_NumServers = 0; + m_NumSortedServers = 0; + mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp)); + m_pFirstReqServer = 0; + m_pLastReqServer = 0; + m_NumRequests = 0; + + // next token + m_CurrentToken = (m_CurrentToken+1)&0xff; + + // + m_ServerlistType = Type; + + if(Type == IServerBrowser::TYPE_LAN) + { + unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1]; + CNetChunk Packet; + int i; + + mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); + Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken; + + Packet.m_ClientID = -1; + mem_zero(&Packet, sizeof(Packet)); + Packet.m_Address.ip[0] = 255; + Packet.m_Address.ip[1] = 255; + Packet.m_Address.ip[2] = 255; + Packet.m_Address.ip[3] = 255; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(Buffer); + Packet.m_pData = Buffer; + m_BroadcastTime = time_get(); + + for(i = 8303; i <= 8310; i++) + { + Packet.m_Address.port = i; + m_pNetClient->Send(&Packet); + } + + if(g_Config.m_Debug) + dbg_msg("client", "broadcasting for servers"); + } + else if(Type == IServerBrowser::TYPE_INTERNET) + m_NeedRefresh = 1; + else if(Type == IServerBrowser::TYPE_FAVORITES) + { + for(int i = 0; i < m_NumFavoriteServers; i++) + Set(m_aFavoriteServers[i], IServerBrowser::SET_FAV_ADD, -1, 0); + } +} + +void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const +{ + //unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1]; + CNetChunk Packet; + + if(g_Config.m_Debug) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + Addr.ip[0], Addr.ip[1], Addr.ip[2], + Addr.ip[3], Addr.port); + } + + /*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); + buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/ + + Packet.m_ClientID = -1; + Packet.m_Address = Addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + /*p.data_size = sizeof(buffer); + p.data = buffer; + netclient_send(net, &p);*/ + + // send old request style aswell + Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO); + Packet.m_pData = SERVERBROWSE_OLD_GETINFO; + m_pNetClient->Send(&Packet); + + if(pEntry) + pEntry->m_RequestTime = time_get(); +} + +void CServerBrowser::Request(const NETADDR &Addr) const +{ + RequestImpl(Addr, 0); +} + + +void CServerBrowser::Update() +{ + int64 Timeout = time_freq(); + int64 Now = time_get(); + int Count; + CServerEntry *pEntry, *pNext; + + // do server list requests + if(m_NeedRefresh && !m_pMasterServer->IsRefreshing()) + { + NETADDR Addr; + CNetChunk Packet; + int i; + + m_NeedRefresh = 0; + + mem_zero(&Packet, sizeof(Packet)); + Packet.m_ClientID = -1; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST); + Packet.m_pData = SERVERBROWSE_GETLIST; + + for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + Addr = m_pMasterServer->GetAddr(i); + if(!Addr.ip[0] && !Addr.ip[1] && !Addr.ip[2] && !Addr.ip[3]) + continue; + + Packet.m_Address = Addr; + m_pNetClient->Send(&Packet); + } + + if(g_Config.m_Debug) + dbg_msg("client", "requesting server list"); + } + + // do timeouts + pEntry = m_pFirstReqServer; + while(1) + { + if(!pEntry) // no more entries + break; + + pNext = pEntry->m_pNextReq; + + if(pEntry->m_RequestTime && pEntry->m_RequestTime+Timeout < Now) + { + // timeout + RemoveRequest(pEntry); + m_NumRequests--; + } + + pEntry = pNext; + } + + // do timeouts + pEntry = m_pFirstReqServer; + Count = 0; + while(1) + { + if(!pEntry) // no more entries + break; + + // no more then 10 concurrent requests + if(Count == g_Config.m_BrMaxRequests) + break; + + if(pEntry->m_RequestTime == 0) + RequestImpl(pEntry->m_Addr, pEntry); + + Count++; + pEntry = pEntry->m_pNextReq; + } + + // check if we need to resort + // TODO: remove the str_comp + if(m_Sorthash != SortHash() || str_comp(m_aFilterString, g_Config.m_BrFilterString) != 0 || str_comp(m_aFilterGametypeString, g_Config.m_BrFilterGametype) != 0) + Sort(); +} + + +bool CServerBrowser::IsFavorite(const NETADDR &Addr) const +{ + // search for the address + int i; + for(i = 0; i < m_NumFavoriteServers; i++) + { + if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0) + return true; + } + return false; +} + +void CServerBrowser::AddFavorite(const NETADDR &Addr) +{ + CServerEntry *pEntry; + + if(m_NumFavoriteServers == MAX_FAVORITES) + return; + + // make sure that we don't already have the server in our list + for(int i = 0; i < m_NumFavoriteServers; i++) + { + if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0) + return; + } + + // add the server to the list + m_aFavoriteServers[m_NumFavoriteServers++] = Addr; + pEntry = Find(Addr); + if(pEntry) + pEntry->m_Info.m_Favorite = 1; + + if(g_Config.m_Debug) + dbg_msg("", "added fav, %d.%d.%d.%d:%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], Addr.port); +} + +void CServerBrowser::RemoveFavorite(const NETADDR &Addr) +{ + int i; + CServerEntry *pEntry; + + for(i = 0; i < m_NumFavoriteServers; i++) + { + if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0) + { + mem_move(&m_aFavoriteServers[i], &m_aFavoriteServers[i+1], sizeof(NETADDR)*(m_NumFavoriteServers-(i+1))); + m_NumFavoriteServers--; + + pEntry = Find(Addr); + if(pEntry) + pEntry->m_Info.m_Favorite = 0; + + return; + } + } +} + + +bool CServerBrowser::IsRefreshingMasters() const +{ + return m_pMasterServer->IsRefreshing(); +} + + +void CServerBrowser::ConfigSaveCallback(IConfig *pConfig, void *pUserData) +{ + CServerBrowser *pSelf = (CServerBrowser *)pUserData; + + int i; + char aAddrStr[128]; + char aBuffer[256]; + for(i = 0; i < pSelf->m_NumFavoriteServers; i++) + { + net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr)); + str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr); + pConfig->WriteLine(aBuffer); + } +} diff --git a/src/engine/client/srvbrowse.h b/src/engine/client/srvbrowse.h new file mode 100644 index 00000000..1c255792 --- /dev/null +++ b/src/engine/client/srvbrowse.h @@ -0,0 +1,111 @@ +#ifndef ENGINE_CLIENT_SRVBROWSE_H +#define ENGINE_CLIENT_SRVBROWSE_H + +#include <engine/serverbrowser.h> + +class CServerBrowser : public IServerBrowser +{ +public: + class CServerEntry + { + public: + NETADDR m_Addr; + int64 m_RequestTime; + int m_GotInfo; + CServerInfo m_Info; + + CServerEntry *m_pNextIp; // ip hashed list + + CServerEntry *m_pPrevReq; // request list + CServerEntry *m_pNextReq; + }; + + enum + { + MAX_FAVORITES=256 + }; + + CServerBrowser(); + + // interface functions + void Refresh(int Type); + bool IsRefreshingMasters() const; + + int NumServers() const { return m_NumServers; } + + int NumSortedServers() const { return m_NumSortedServers; } + const CServerInfo *SortedGet(int Index) const; + + bool IsFavorite(const NETADDR &Addr) const; + void AddFavorite(const NETADDR &Addr); + void RemoveFavorite(const NETADDR &Addr); + + // + void Update(); + void Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo); + void Request(const NETADDR &Addr) const; + + void SetBaseInfo(class CNetClient *pClient, const char *pNetVersion); + +private: + CNetClient *m_pNetClient; + IMasterServer *m_pMasterServer; + char m_aNetVersion[128]; + + CHeap m_ServerlistHeap; + CServerEntry **m_ppServerlist; + int *m_pSortedServerlist; + + NETADDR m_aFavoriteServers[MAX_FAVORITES]; + int m_NumFavoriteServers; + + CServerEntry *m_aServerlistIp[256]; // ip hash list + + CServerEntry *m_pFirstReqServer; // request list + CServerEntry *m_pLastReqServer; + int m_NumRequests; + + int m_NeedRefresh; + + int m_NumSortedServers; + int m_NumSortedServersCapacity; + int m_NumServers; + int m_NumServerCapacity; + + int m_Sorthash; + char m_aFilterString[64]; + char m_aFilterGametypeString[128]; + + // the token is to keep server refresh separated from each other + int m_CurrentToken; + + int m_ServerlistType; + int64 m_BroadcastTime; + + // sorting criterions + bool SortCompareName(int Index1, int Index2) const; + bool SortCompareMap(int Index1, int Index2) const; + bool SortComparePing(int Index1, int Index2) const; + bool SortCompareGametype(int Index1, int Index2) const; + bool SortCompareProgression(int Index1, int Index2) const; + bool SortCompareNumPlayers(int Index1, int Index2) const; + + // + void Filter(); + void Sort(); + int SortHash() const; + + CServerEntry *Find(const NETADDR &Addr); + CServerEntry *Add(const NETADDR &Addr); + + void RemoveRequest(CServerEntry *pEntry); + void QueueRequest(CServerEntry *pEntry); + + void RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const; + + void SetInfo(CServerEntry *pEntry, const CServerInfo &Info); + + static void ConfigSaveCallback(IConfig *pConfig, void *pUserData); +}; + +#endif diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp new file mode 100644 index 00000000..b05d49f8 --- /dev/null +++ b/src/engine/client/text.cpp @@ -0,0 +1,718 @@ +#include <base/system.h> +#include <base/math.h> +#include <engine/graphics.h> +#include <engine/textrender.h> + +#ifdef CONF_FAMILY_WINDOWS + #include <windows.h> +#endif + +#ifdef CONF_PLATFORM_MACOSX + #include <OpenGL/gl.h> + #include <OpenGL/glu.h> +#else + #include <GL/gl.h> + #include <GL/glu.h> +#endif + +// ft2 texture +#include <ft2build.h> +#include FT_FREETYPE_H + +// TODO: Refactor: clean this up + + +enum +{ + MAX_CHARACTERS = 64, +}; + + +static int aFontSizes[] = {8,9,10,11,12,13,14,15,16,17,18,19,20,36}; +#define NUM_FONT_SIZES (sizeof(aFontSizes)/sizeof(int)) + +struct CFontChar +{ + int m_Id; + + // these values are scaled to the pFont size + // width * font_size == real_size + float m_Width; + float m_Height; + float m_OffsetX; + float m_OffsetY; + float m_AdvanceX; + + float m_aUvs[4]; + int64 m_TouchTime; +}; + +struct CFontSizeData +{ + int m_FontSize; + FT_Face *m_pFace; + + unsigned m_aTextures[2]; + int m_TextureWidth; + int m_TextureHeight; + + int m_NumXChars; + int m_NumYChars; + + int m_CharMaxWidth; + int m_CharMaxHeight; + + CFontChar m_aCharacters[MAX_CHARACTERS*MAX_CHARACTERS]; + + int m_CurrentCharacter; +}; + +struct CFont +{ + char m_aFilename[128]; + FT_Face m_FtFace; + CFontSizeData m_aSizes[NUM_FONT_SIZES]; +}; + + +class CTextRender : public IEngineTextRender +{ + IGraphics *m_pGraphics; + IGraphics *Graphics() { return m_pGraphics; } + + int WordLength(const char *pText) + { + int s = 1; + while(1) + { + if(*pText == 0) + return s-1; + if(*pText == '\n' || *pText == '\t' || *pText == ' ') + return s; + pText++; + s++; + } + } + + float m_TextR; + float m_TextG; + float m_TextB; + float m_TextA; + + int m_FontTextureFormat; + + struct CFont *m_pDefaultFont; + + FT_Library m_FTLibrary; + + int GetFontSizeIndex(int Pixelsize) + { + for(unsigned i = 0; i < NUM_FONT_SIZES; i++) + { + if(aFontSizes[i] >= Pixelsize) + return i; + } + + return NUM_FONT_SIZES-1; + } + + + + void Grow(unsigned char *pIn, unsigned char *pOut, int w, int h) + { + for(int y = 0; y < h; y++) + for(int x = 0; x < w; x++) + { + int c = pIn[y*w+x]; + + for(int sy = -1; sy <= 1; sy++) + for(int sx = -1; sx <= 1; sx++) + { + int GetX = x+sx; + int GetY = y+sy; + if (GetX >= 0 && GetY >= 0 && GetX < w && GetY < h) + { + int Index = GetY*w+GetX; + if(pIn[Index] > c) + c = pIn[Index]; + } + } + + pOut[y*w+x] = c; + } + } + + void InitTexture(CFontSizeData *pSizeData, int CharWidth, int CharHeight, int Xchars, int Ychars) + { + static int FontMemoryUsage = 0; + int Width = CharWidth*Xchars; + int Height = CharHeight*Ychars; + void *pMem = mem_alloc(Width*Height, 1); + mem_zero(pMem, Width*Height); + + if(pSizeData->m_aTextures[0] == 0) + glGenTextures(2, pSizeData->m_aTextures); + else + FontMemoryUsage -= pSizeData->m_TextureWidth*pSizeData->m_TextureHeight*2; + + pSizeData->m_NumXChars = Xchars; + pSizeData->m_NumYChars = Ychars; + pSizeData->m_TextureWidth = Width; + pSizeData->m_TextureHeight = Height; + pSizeData->m_CurrentCharacter = 0; + + for(int i = 0; i < 2; i++) + { + glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, m_FontTextureFormat, Width, Height, 0, m_FontTextureFormat, GL_UNSIGNED_BYTE, pMem); + FontMemoryUsage += Width*Height; + } + + dbg_msg("", "pFont memory usage: %d", FontMemoryUsage); + + mem_free(pMem); + } + + void IncreaseTextureSize(CFontSizeData *pSizeData) + { + if(pSizeData->m_TextureWidth < pSizeData->m_TextureHeight) + pSizeData->m_NumXChars <<= 1; + else + pSizeData->m_NumYChars <<= 1; + InitTexture(pSizeData, pSizeData->m_CharMaxWidth, pSizeData->m_CharMaxHeight, pSizeData->m_NumXChars, pSizeData->m_NumYChars); + } + + + // TODO: Refactor: move this into a pFont class + void InitIndex(CFont *pFont, int Index) + { + int OutlineThickness = 1; + CFontSizeData *pSizeData = &pFont->m_aSizes[Index]; + + pSizeData->m_FontSize = aFontSizes[Index]; + FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, pSizeData->m_FontSize); + + if(pSizeData->m_FontSize >= 18) + OutlineThickness = 2; + + { + unsigned GlyphIndex; + int MaxH = 0; + int MaxW = 0; + + int Charcode = FT_Get_First_Char(pFont->m_FtFace, &GlyphIndex); + while(GlyphIndex != 0) + { + // do stuff + FT_Load_Glyph(pFont->m_FtFace, GlyphIndex, FT_LOAD_DEFAULT); + + if(pFont->m_FtFace->glyph->metrics.width > MaxW) MaxW = pFont->m_FtFace->glyph->metrics.width; // ignore_convention + if(pFont->m_FtFace->glyph->metrics.height > MaxH) MaxH = pFont->m_FtFace->glyph->metrics.height; // ignore_convention + Charcode = FT_Get_Next_Char(pFont->m_FtFace, Charcode, &GlyphIndex); + } + + MaxW = (MaxW>>6)+2+OutlineThickness*2; + MaxH = (MaxH>>6)+2+OutlineThickness*2; + + for(pSizeData->m_CharMaxWidth = 1; pSizeData->m_CharMaxWidth < MaxW; pSizeData->m_CharMaxWidth <<= 1); + for(pSizeData->m_CharMaxHeight = 1; pSizeData->m_CharMaxHeight < MaxH; pSizeData->m_CharMaxHeight <<= 1); + } + + //dbg_msg("pFont", "init size %d, texture size %d %d", pFont->sizes[index].font_size, w, h); + //FT_New_Face(m_FTLibrary, "data/fonts/vera.ttf", 0, &pFont->ft_face); + InitTexture(pSizeData, pSizeData->m_CharMaxWidth, pSizeData->m_CharMaxHeight, 8, 8); + } + + CFontSizeData *GetSize(CFont *pFont, int Pixelsize) + { + int Index = GetFontSizeIndex(Pixelsize); + if(pFont->m_aSizes[Index].m_FontSize != aFontSizes[Index]) + InitIndex(pFont, Index); + return &pFont->m_aSizes[Index]; + } + + + void UploadGlyph(CFontSizeData *pSizeData, int Texnum, int SlotId, int Chr, const void *pData) + { + int x = (SlotId%pSizeData->m_NumXChars) * (pSizeData->m_TextureWidth/pSizeData->m_NumXChars); + int y = (SlotId/pSizeData->m_NumXChars) * (pSizeData->m_TextureHeight/pSizeData->m_NumYChars); + + glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[Texnum]); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, + pSizeData->m_TextureWidth/pSizeData->m_NumXChars, + pSizeData->m_TextureHeight/pSizeData->m_NumYChars, + m_FontTextureFormat, GL_UNSIGNED_BYTE, pData); + } + + // 8k of data used for rendering glyphs + unsigned char ms_aGlyphData[(4096/64) * (4096/64)]; + unsigned char ms_aGlyphDataOutlined[(4096/64) * (4096/64)]; + + int GetSlot(CFontSizeData *pSizeData) + { + int CharCount = pSizeData->m_NumXChars*pSizeData->m_NumYChars; + if(pSizeData->m_CurrentCharacter < CharCount) + { + int i = pSizeData->m_CurrentCharacter; + pSizeData->m_CurrentCharacter++; + return i; + } + + // kick out the oldest + // TODO: remove this linear search + { + int Oldest = 0; + for(int i = 1; i < CharCount; i++) + { + if(pSizeData->m_aCharacters[i].m_TouchTime < pSizeData->m_aCharacters[Oldest].m_TouchTime) + Oldest = i; + } + + if(time_get()-pSizeData->m_aCharacters[Oldest].m_TouchTime < time_freq()) + { + IncreaseTextureSize(pSizeData); + return GetSlot(pSizeData); + } + + return Oldest; + } + } + + int RenderGlyph(CFont *pFont, CFontSizeData *pSizeData, int Chr) + { + FT_Bitmap *pBitmap; + int SlotId = 0; + int SlotW = pSizeData->m_TextureWidth / pSizeData->m_NumXChars; + int SlotH = pSizeData->m_TextureHeight / pSizeData->m_NumYChars; + int SlotSize = SlotW*SlotH; + int OutlineThickness = 1; + int x = 1; + int y = 1; + int px, py; + + FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, pSizeData->m_FontSize); + + if(FT_Load_Char(pFont->m_FtFace, Chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP)) + { + dbg_msg("pFont", "error loading glyph %d", Chr); + return -1; + } + + pBitmap = &pFont->m_FtFace->glyph->bitmap; // ignore_convention + + // fetch slot + SlotId = GetSlot(pSizeData); + if(SlotId < 0) + return -1; + + // adjust spacing + if(pSizeData->m_FontSize >= 18) + OutlineThickness = 2; + x += OutlineThickness; + y += OutlineThickness; + + // prepare glyph data + mem_zero(ms_aGlyphData, SlotSize); + + if(pBitmap->pixel_mode == FT_PIXEL_MODE_GRAY) // ignore_convention + { + for(py = 0; py < pBitmap->rows; py++) // ignore_convention + for(px = 0; px < pBitmap->width; px++) // ignore_convention + ms_aGlyphData[(py+y)*SlotW+px+x] = pBitmap->buffer[py*pBitmap->pitch+px]; // ignore_convention + } + else if(pBitmap->pixel_mode == FT_PIXEL_MODE_MONO) // ignore_convention + { + for(py = 0; py < pBitmap->rows; py++) // ignore_convention + for(px = 0; px < pBitmap->width; px++) // ignore_convention + { + if(pBitmap->buffer[py*pBitmap->pitch+px/8]&(1<<(7-(px%8)))) // ignore_convention + ms_aGlyphData[(py+y)*SlotW+px+x] = 255; + } + } + + if(0) for(py = 0; py < SlotW; py++) + for(px = 0; px < SlotH; px++) + ms_aGlyphData[py*SlotW+px] = 255; + + // upload the glyph + UploadGlyph(pSizeData, 0, SlotId, Chr, ms_aGlyphData); + + if(OutlineThickness == 1) + { + Grow(ms_aGlyphData, ms_aGlyphDataOutlined, SlotW, SlotH); + UploadGlyph(pSizeData, 1, SlotId, Chr, ms_aGlyphDataOutlined); + } + else + { + Grow(ms_aGlyphData, ms_aGlyphDataOutlined, SlotW, SlotH); + Grow(ms_aGlyphDataOutlined, ms_aGlyphData, SlotW, SlotH); + UploadGlyph(pSizeData, 1, SlotId, Chr, ms_aGlyphData); + } + + // set char info + { + CFontChar *pFontchr = &pSizeData->m_aCharacters[SlotId]; + float Scale = 1.0f/pSizeData->m_FontSize; + float Uscale = 1.0f/pSizeData->m_TextureWidth; + float Vscale = 1.0f/pSizeData->m_TextureHeight; + int Height = pBitmap->rows + OutlineThickness*2 + 2; // ignore_convention + int Width = pBitmap->width + OutlineThickness*2 + 2; // ignore_convention + + pFontchr->m_Id = Chr; + pFontchr->m_Height = Height * Scale; + pFontchr->m_Width = Width * Scale; + pFontchr->m_OffsetX = (pFont->m_FtFace->glyph->bitmap_left-1) * Scale; // ignore_convention + pFontchr->m_OffsetY = (pSizeData->m_FontSize - pFont->m_FtFace->glyph->bitmap_top) * Scale; // ignore_convention + pFontchr->m_AdvanceX = (pFont->m_FtFace->glyph->advance.x>>6) * Scale; // ignore_convention + + pFontchr->m_aUvs[0] = (SlotId%pSizeData->m_NumXChars) / (float)(pSizeData->m_NumXChars); + pFontchr->m_aUvs[1] = (SlotId/pSizeData->m_NumXChars) / (float)(pSizeData->m_NumYChars); + pFontchr->m_aUvs[2] = pFontchr->m_aUvs[0] + Width*Uscale; + pFontchr->m_aUvs[3] = pFontchr->m_aUvs[1] + Height*Vscale; + } + + return SlotId; + } + + CFontChar *GetChar(CFont *pFont, CFontSizeData *pSizeData, int Chr) + { + CFontChar *pFontchr = NULL; + + // search for the character + // TODO: remove this linear search + int i; + for(i = 0; i < pSizeData->m_CurrentCharacter; i++) + { + if(pSizeData->m_aCharacters[i].m_Id == Chr) + { + pFontchr = &pSizeData->m_aCharacters[i]; + break; + } + } + + // check if we need to render the character + if(!pFontchr) + { + int Index = RenderGlyph(pFont, pSizeData, Chr); + if(Index >= 0) + pFontchr = &pSizeData->m_aCharacters[Index]; + } + + // touch the character + // TODO: don't call time_get here + if(pFontchr) + pFontchr->m_TouchTime = time_get(); + + return pFontchr; + } + + // must only be called from the rendering function as the pFont must be set to the correct size + void RenderSetup(CFont *pFont, int size) + { + FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, size); + } + + float Kerning(CFont *pFont, int Left, int Right) + { + FT_Vector Kerning = {0,0}; + FT_Get_Kerning(pFont->m_FtFace, Left, Right, FT_KERNING_DEFAULT, &Kerning); + return (Kerning.x>>6); + } + + +public: + CTextRender() + { + m_pGraphics = 0; + + m_TextR = 1; + m_TextG = 1; + m_TextB = 1; + m_TextA = 1; + + m_pDefaultFont = 0; + + // GL_LUMINANCE can be good for debugging + m_FontTextureFormat = GL_ALPHA; + } + + virtual void Init() + { + m_pGraphics = Kernel()->RequestInterface<IGraphics>(); + FT_Init_FreeType(&m_FTLibrary); + } + + + virtual CFont *LoadFont(const char *pFilename) + { + CFont *pFont = (CFont *)mem_alloc(sizeof(CFont), 1); + + mem_zero(pFont, sizeof(*pFont)); + str_copy(pFont->m_aFilename, pFilename, sizeof(pFont->m_aFilename)); + + if(FT_New_Face(m_FTLibrary, pFont->m_aFilename, 0, &pFont->m_FtFace)) + { + mem_free(pFont); + return NULL; + } + + for(unsigned i = 0; i < NUM_FONT_SIZES; i++) + pFont->m_aSizes[i].m_FontSize = -1; + + dbg_msg("textrender", "loaded pFont from '%s'", pFilename); + return pFont; + }; + + virtual void DestroyFont(CFont *pFont) + { + mem_free(pFont); + } + + virtual void SetDefaultFont(struct CFont *pFont) + { + dbg_msg("textrender", "default pFont set %p", pFont); + m_pDefaultFont = pFont; + } + + + virtual void SetCursor(CTextCursor *pCursor, float x, float y, float FontSize, int Flags) + { + mem_zero(pCursor, sizeof(*pCursor)); + pCursor->m_FontSize = FontSize; + pCursor->m_StartX = x; + pCursor->m_StartY = y; + pCursor->m_X = x; + pCursor->m_Y = y; + pCursor->m_LineCount = 1; + pCursor->m_LineWidth = -1; + pCursor->m_Flags = Flags; + pCursor->m_CharCount = 0; + } + + + virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, int MaxWidth) + { + CTextCursor Cursor; + SetCursor(&Cursor, x, y, Size, TEXTFLAG_RENDER); + Cursor.m_LineWidth = MaxWidth; + TextEx(&Cursor, pText, -1); + } + + virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int Length) + { + CTextCursor Cursor; + SetCursor(&Cursor, 0, 0, Size, 0); + TextEx(&Cursor, pText, Length); + return Cursor.m_X; + } + + virtual float TextLineCount(void *pFontSetV, float Size, const char *pText, int LineWidth) + { + CTextCursor Cursor; + SetCursor(&Cursor, 0, 0, Size, 0); + Cursor.m_LineWidth = LineWidth; + TextEx(&Cursor, pText, -1); + return Cursor.m_LineCount; + } + + virtual void TextColor(float r, float g, float b, float a) + { + m_TextR = r; + m_TextG = g; + m_TextB = b; + m_TextA = a; + } + + virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length) + { + CFont *pFont = pCursor->m_pFont; + CFontSizeData *pSizeData = NULL; + + //dbg_msg("textrender", "rendering text '%s'", text); + + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + float FakeToScreenX, FakeToScreenY; + int ActualX, ActualY; + + int ActualSize; + int i; + int GotNewLine = 0; + float DrawX, DrawY; + float CursorX, CursorY; + const char *pEnd; + + float Size = pCursor->m_FontSize; + + // to correct coords, convert to screen coords, round, and convert back + Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + + FakeToScreenX = (Graphics()->ScreenWidth()/(ScreenX1-ScreenX0)); + FakeToScreenY = (Graphics()->ScreenHeight()/(ScreenY1-ScreenY0)); + ActualX = pCursor->m_X * FakeToScreenX; + ActualY = pCursor->m_Y * FakeToScreenY; + + CursorX = ActualX / FakeToScreenX; + CursorY = ActualY / FakeToScreenY; + + // same with size + ActualSize = Size * FakeToScreenY; + Size = ActualSize / FakeToScreenY; + + // fetch pFont data + if(!pFont) + pFont = m_pDefaultFont; + + if(!pFont) + return; + + pSizeData = GetSize(pFont, ActualSize); + RenderSetup(pFont, ActualSize); + + // set length + if(Length < 0) + Length = str_length(pText); + + pEnd = pText + Length; + + // if we don't want to render, we can just skip the first outline pass + i = 1; + if(pCursor->m_Flags&TEXTFLAG_RENDER) + i = 0; + + for(;i < 2; i++) + { + const char *pCurrent = (char *)pText; + const char *pEnd = pCurrent+Length; + DrawX = CursorX; + DrawY = CursorY; + + if(pCursor->m_Flags&TEXTFLAG_RENDER) + { + // TODO: Make this better + glEnable(GL_TEXTURE_2D); + if (i == 0) + glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[1]); + else + glBindTexture(GL_TEXTURE_2D, pSizeData->m_aTextures[0]); + + Graphics()->QuadsBegin(); + if (i == 0) + Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f*m_TextA); + else + Graphics()->SetColor(m_TextR, m_TextG, m_TextB, m_TextA); + } + + while(pCurrent < pEnd) + { + int NewLine = 0; + const char *pBatchEnd = pEnd; + if(pCursor->m_LineWidth > 0 && !(pCursor->m_Flags&TEXTFLAG_STOP_AT_END)) + { + int Wlen = min(WordLength((char *)pCurrent), (int)(pEnd-pCurrent)); + CTextCursor Compare = *pCursor; + Compare.m_X = DrawX; + Compare.m_Y = DrawY; + Compare.m_Flags &= ~TEXTFLAG_RENDER; + Compare.m_LineWidth = -1; + TextEx(&Compare, pText, Wlen); + + if(Compare.m_X-DrawX > pCursor->m_LineWidth) + { + // word can't be fitted in one line, cut it + CTextCursor Cutter = *pCursor; + Cutter.m_CharCount = 0; + Cutter.m_X = DrawX; + Cutter.m_Y = DrawY; + Cutter.m_Flags &= ~TEXTFLAG_RENDER; + Cutter.m_Flags |= TEXTFLAG_STOP_AT_END; + + TextEx(&Cutter, (const char *)pCurrent, Wlen); + Wlen = Cutter.m_CharCount; + NewLine = 1; + + if(Wlen <= 3) // if we can't place 3 chars of the word on this line, take the next + Wlen = 0; + } + else if(Compare.m_X-pCursor->m_StartX > pCursor->m_LineWidth) + { + NewLine = 1; + Wlen = 0; + } + + pBatchEnd = pCurrent + Wlen; + } + + while(pCurrent < pBatchEnd) + { + const char *pTmp; + float Advance = 0; + int Character = 0; + int Nextcharacter = 0; + CFontChar *pChr; + + // TODO: UTF-8 decode + Character = str_utf8_decode(&pCurrent); + pTmp = pCurrent; + Nextcharacter = str_utf8_decode(&pTmp); + + if(Character == '\n') + { + DrawX = pCursor->m_StartX; + DrawY += Size; + DrawX = (int)(DrawX * FakeToScreenX) / FakeToScreenX; // realign + DrawY = (int)(DrawY * FakeToScreenY) / FakeToScreenY; + ++pCursor->m_LineCount; + continue; + } + + pChr = GetChar(pFont, pSizeData, Character); + + if(pChr) + { + if(pCursor->m_Flags&TEXTFLAG_RENDER) + { + Graphics()->QuadsSetSubset(pChr->m_aUvs[0], pChr->m_aUvs[1], pChr->m_aUvs[2], pChr->m_aUvs[3]); + IGraphics::CQuadItem QuadItem(DrawX+pChr->m_OffsetX*Size, DrawY+pChr->m_OffsetY*Size, pChr->m_Width*Size, pChr->m_Height*Size); + Graphics()->QuadsDrawTL(&QuadItem, 1); + } + + Advance = pChr->m_AdvanceX + Kerning(pFont, Character, Nextcharacter)/Size; + } + + if(pCursor->m_Flags&TEXTFLAG_STOP_AT_END && DrawX+Advance*Size-pCursor->m_StartX > pCursor->m_LineWidth) + { + // we hit the end of the line, no more to render or count + pCurrent = pEnd; + break; + } + + DrawX += Advance*Size; + pCursor->m_CharCount++; + } + + if(NewLine) + { + DrawX = pCursor->m_StartX; + DrawY += Size; + GotNewLine = 1; + DrawX = (int)(DrawX * FakeToScreenX) / FakeToScreenX; // realign + DrawY = (int)(DrawY * FakeToScreenY) / FakeToScreenY; + ++pCursor->m_LineCount; + } + } + + if(pCursor->m_Flags&TEXTFLAG_RENDER) + Graphics()->QuadsEnd(); + } + + pCursor->m_X = DrawX; + + if(GotNewLine) + pCursor->m_Y = DrawY; + } + +}; + +IEngineTextRender *CreateEngineTextRender() { return new CTextRender; } diff --git a/src/engine/config.h b/src/engine/config.h new file mode 100644 index 00000000..967d3593 --- /dev/null +++ b/src/engine/config.h @@ -0,0 +1,23 @@ +#ifndef ENGINE_CONFIG_H +#define ENGINE_CONFIG_H + +#include "kernel.h" + +class IConfig : public IInterface +{ + MACRO_INTERFACE("config", 0) +public: + typedef void (*SAVECALLBACKFUNC)(IConfig *pConfig, void *pUserData); + + virtual void Init() = 0; + virtual void Reset() = 0; + virtual void Save() = 0; + + virtual void RegisterCallback(SAVECALLBACKFUNC pfnFunc, void *pUserData) = 0; + + virtual void WriteLine(const char *pLine) = 0; +}; + +extern IConfig *CreateConfig(); + +#endif diff --git a/src/engine/console.h b/src/engine/console.h new file mode 100644 index 00000000..74d789e9 --- /dev/null +++ b/src/engine/console.h @@ -0,0 +1,58 @@ +#ifndef ENGINE_CONSOLE_H +#define ENGINE_CONSOLE_H + +#include "kernel.h" + +class IConsole : public IInterface +{ + MACRO_INTERFACE("console", 0) +public: + + // TODO: rework this interface to reduce the amount of virtual calls + class IResult + { + protected: + unsigned m_NumArgs; + public: + IResult() { m_NumArgs = 0; } + virtual ~IResult() {} + + virtual int GetInteger(unsigned Index) = 0; + virtual float GetFloat(unsigned Index) = 0; + virtual const char *GetString(unsigned Index) = 0; + + int NumArguments() const { return m_NumArgs; } + }; + + class CCommandInfo + { + public: + const char *m_pName; + const char *m_pHelp; + const char *m_pParams; + }; + + typedef void (*FPrintCallback)(const char *pStr, void *pUser); + typedef void (*FPossibleCallback)(const char *pCmd, void *pUser); + typedef void (*FCommandCallback)(IResult *pResult, void *pUserData); + typedef void (*FChainCommandCallback)(IResult *pResult, void *pUserData, FCommandCallback pfnCallback, void *pCallbackUserData); + + virtual CCommandInfo *GetCommandInfo(const char *pName) = 0; + virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) = 0; + virtual void ParseArguments(int NumArgs, const char **ppArguments) = 0; + + virtual void Register(const char *pName, const char *pParams, + int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) = 0; + virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser) = 0; + + virtual void ExecuteLine(const char *Sptr) = 0; + virtual void ExecuteLineStroked(int Stroke, const char *pStr) = 0; + virtual void ExecuteFile(const char *pFilename) = 0; + + virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) = 0; + virtual void Print(const char *pStr) = 0; +}; + +extern IConsole *CreateConsole(); + +#endif // FILE_ENGINE_CONSOLE_H diff --git a/src/engine/demo.h b/src/engine/demo.h new file mode 100644 index 00000000..7b353e7c --- /dev/null +++ b/src/engine/demo.h @@ -0,0 +1,29 @@ +#ifndef ENGINE_DEMO_H +#define ENGINE_DEMO_H + +#include "kernel.h" + +class IDemoPlayer : public IInterface +{ + MACRO_INTERFACE("demoplayer", 0) +public: + class CInfo + { + public: + bool m_Paused; + float m_Speed; + + int m_FirstTick; + int m_CurrentTick; + int m_LastTick; + }; + + ~IDemoPlayer() {} + virtual void SetSpeed(float Speed) = 0; + virtual int SetPos(float Precent) = 0; + virtual void Pause() = 0; + virtual void Unpause() = 0; + virtual const CInfo *BaseInfo() const = 0; +}; + +#endif diff --git a/src/engine/e_client_interface.h b/src/engine/e_client_interface.h deleted file mode 100644 index 079eabca..00000000 --- a/src/engine/e_client_interface.h +++ /dev/null @@ -1,16 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_CLIENT_INTERFACE_H -#define ENGINE_CLIENT_INTERFACE_H - -#include "e_if_other.h" -#include "e_if_client.h" -#include "e_if_snd.h" -#include "e_if_gfx.h" -#include "e_if_inp.h" -#include "e_if_msg.h" -#include "e_if_modc.h" - -#include "e_console.h" -#include "e_config.h" - -#endif diff --git a/src/engine/e_common_interface.h b/src/engine/e_common_interface.h deleted file mode 100644 index 9c95b48b..00000000 --- a/src/engine/e_common_interface.h +++ /dev/null @@ -1,8 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_COMMON_INTERFACE_H -#define ENGINE_COMMON_INTERFACE_H - -#include "e_if_other.h" -#include "e_if_msg.h" - -#endif diff --git a/src/engine/e_compression.cpp b/src/engine/e_compression.cpp deleted file mode 100644 index f4d6e0c0..00000000 --- a/src/engine/e_compression.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> - -/* Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign */ -unsigned char *vint_pack(unsigned char *dst, int i) -{ - *dst = (i>>25)&0x40; /* set sign bit if i<0 */ - i = i^(i>>31); /* if(i<0) i = ~i */ - - *dst |= i&0x3F; /* pack 6bit into dst */ - i >>= 6; /* discard 6 bits */ - if(i) - { - *dst |= 0x80; /* set extend bit */ - while(1) - { - dst++; - *dst = i&(0x7F); /* pack 7bit */ - i >>= 7; /* discard 7 bits */ - *dst |= (i!=0)<<7; /* set extend bit (may branch) */ - if(!i) - break; - } - } - - dst++; - return dst; -} - -const unsigned char *vint_unpack(const unsigned char *src, int *i) -{ - int sign = (*src>>6)&1; - *i = *src&0x3F; - - do - { - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7+7); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7+7+7); - } while(0); - - src++; - *i ^= -sign; /* if(sign) *i = ~(*i) */ - return src; -} - - -long intpack_decompress(const void *src_, int size, void *dst_) -{ - const unsigned char *src = (unsigned char *)src_; - const unsigned char *end = src + size; - int *dst = (int *)dst_; - while(src < end) - { - src = vint_unpack(src, dst); - dst++; - } - return (long)((unsigned char *)dst-(unsigned char *)dst_); -} - -long intpack_compress(const void *src_, int size, void *dst_) -{ - int *src = (int *)src_; - unsigned char *dst = (unsigned char *)dst_; - size /= 4; - while(size) - { - dst = vint_pack(dst, *src); - size--; - src++; - } - return (long)(dst-(unsigned char *)dst_); -} - diff --git a/src/engine/e_compression.h b/src/engine/e_compression.h deleted file mode 100644 index be5bf78f..00000000 --- a/src/engine/e_compression.h +++ /dev/null @@ -1,7 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -/* variable int packing */ -unsigned char *vint_pack(unsigned char *dst, int i); -const unsigned char *vint_unpack(const unsigned char *src, int *inout); -long intpack_compress(const void *src, int size, void *dst); -long intpack_decompress(const void *src, int size, void *dst); diff --git a/src/engine/e_config.cpp b/src/engine/e_config.cpp deleted file mode 100644 index 67a4c81a..00000000 --- a/src/engine/e_config.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#include <base/system.h> -#include "e_if_other.h" -#include "e_config.h" -#include "e_linereader.h" -#include "e_engine.h" - -CONFIGURATION config; - -void config_reset() -{ - #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) config.name = def; - #define MACRO_CONFIG_STR(name,len,def,flags,desc) str_copy(config.name, def, len); - - #include "e_config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} - -void config_save() -{ - char linebuf[1024*2]; - - #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %i", #name, config.name); engine_config_write_line(linebuf); } - #define MACRO_CONFIG_STR(name,len,def,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %s", #name, config.name); engine_config_write_line(linebuf); } - - #include "e_config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} - -#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c) { return c->name; } -#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; } -#include "e_config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; } -#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str) { str_copy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; } -#include "e_config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR diff --git a/src/engine/e_config.h b/src/engine/e_config.h deleted file mode 100644 index 6ca7a8ee..00000000 --- a/src/engine/e_config.h +++ /dev/null @@ -1,44 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef _CONFIG_H -#define _CONFIG_H - -typedef struct -{ - #define MACRO_CONFIG_INT(name,def,min,max,save,desc) int name; - #define MACRO_CONFIG_STR(name,len,def,save,desc) char name[len]; /* Flawfinder: ignore */ - #include "e_config_variables.h" - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} CONFIGURATION; - -extern CONFIGURATION config; - -void config_init(); -void config_reset(); -void config_save(); - -enum -{ - CFGFLAG_SAVE=1, - CFGFLAG_CLIENT=2, - CFGFLAG_SERVER=4 -}; - -typedef int (*CONFIG_INT_GETTER)(CONFIGURATION *c); -typedef const char *(*CONFIG_STR_GETTER)(CONFIGURATION *c); -typedef void (*CONFIG_INT_SETTER)(CONFIGURATION *c, int val); -typedef void (*CONFIG_STR_SETTER)(CONFIGURATION *c, const char *str); - -#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c); -#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c); -#include "e_config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val); -#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str); -#include "e_config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#endif diff --git a/src/engine/e_config_variables.h b/src/engine/e_config_variables.h deleted file mode 100644 index aa3da3f6..00000000 --- a/src/engine/e_config_variables.h +++ /dev/null @@ -1,77 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -/* TODO: remove this */ -#include "../game/variables.hpp" - - -MACRO_CONFIG_STR(player_name, 32, "nameless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the player") -MACRO_CONFIG_STR(clan_name, 32, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "(not used)") -MACRO_CONFIG_STR(password, 32, "", CFGFLAG_CLIENT, "Password to the server") -MACRO_CONFIG_STR(logfile, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filename to log all output to") - -MACRO_CONFIG_INT(cl_cpu_throttle, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") -MACRO_CONFIG_INT(cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "") - -MACRO_CONFIG_INT(cl_eventthread, 0, 0, 1, CFGFLAG_CLIENT, "Enables the usage of a thread to pump the events") - -MACRO_CONFIG_INT(inp_grab, 0, 0, 1, CFGFLAG_CLIENT, "Use forceful input grabbing method") - -MACRO_CONFIG_STR(b_filter_string, 64, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server browser filtering string") - -MACRO_CONFIG_INT(b_filter_full, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out full server in browser") -MACRO_CONFIG_INT(b_filter_empty, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser") -MACRO_CONFIG_INT(b_filter_pw, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out password protected servers in browser") -MACRO_CONFIG_INT(b_filter_ping, 999, 0, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ping to filter by in the server browser") -MACRO_CONFIG_STR(b_filter_gametype, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Game types to filter") -MACRO_CONFIG_INT(b_filter_pure, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard servers in browser") -MACRO_CONFIG_INT(b_filter_pure_map, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser") -MACRO_CONFIG_INT(b_filter_compatversion, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-compatible servers in browser") - -MACRO_CONFIG_INT(b_sort, 0, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") -MACRO_CONFIG_INT(b_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") -MACRO_CONFIG_INT(b_max_requests, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser") - -MACRO_CONFIG_INT(snd_buffer_size, 512, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size") -MACRO_CONFIG_INT(snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate") -MACRO_CONFIG_INT(snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable") -MACRO_CONFIG_INT(snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume") -MACRO_CONFIG_INT(snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use") - -MACRO_CONFIG_INT(snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") - -MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution width") -MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution height") -MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fullscreen") -MACRO_CONFIG_INT(gfx_alphabits, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Alpha bits for framebuffer (fullscreen only)") -MACRO_CONFIG_INT(gfx_color_depth, 24, 16, 24, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Colors bits for framebuffer (fullscreen only)") -MACRO_CONFIG_INT(gfx_clear, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clear screen before rendering") -MACRO_CONFIG_INT(gfx_vsync, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Vertical sync") -MACRO_CONFIG_INT(gfx_display_all_modes, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") -MACRO_CONFIG_INT(gfx_texture_compression, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use texture compression") -MACRO_CONFIG_INT(gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail") -MACRO_CONFIG_INT(gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") -MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples") -MACRO_CONFIG_INT(gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate") -MACRO_CONFIG_INT(gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") - -MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity") - -MACRO_CONFIG_STR(sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server name") -MACRO_CONFIG_STR(sv_bindaddr, 128, "", CFGFLAG_SERVER, "Address to bind the server to") -MACRO_CONFIG_INT(sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server") -MACRO_CONFIG_INT(sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers") -MACRO_CONFIG_STR(sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server") -MACRO_CONFIG_INT(sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server") -MACRO_CONFIG_INT(sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only") -MACRO_CONFIG_INT(sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing") -MACRO_CONFIG_STR(sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password") -MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1, CFGFLAG_SERVER, "Reload the current map") - -MACRO_CONFIG_INT(debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode") -MACRO_CONFIG_INT(dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems") -MACRO_CONFIG_INT(dbg_stress_network, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress network") -MACRO_CONFIG_INT(dbg_pref, 0, 0, 1, CFGFLAG_SERVER, "Performance outputs") -MACRO_CONFIG_INT(dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs") -MACRO_CONFIG_INT(dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings") -MACRO_CONFIG_STR(dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress") -MACRO_CONFIG_INT(dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables window resizing") diff --git a/src/engine/e_console.cpp b/src/engine/e_console.cpp deleted file mode 100644 index c641289d..00000000 --- a/src/engine/e_console.cpp +++ /dev/null @@ -1,464 +0,0 @@ -#include <base/system.h> -#include "e_if_other.h" -#include "e_console.h" -#include "e_config.h" -#include "e_engine.h" -#include "e_linereader.h" -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - - -#define CONSOLE_MAX_STR_LENGTH 1024 -/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */ -#define MAX_PARTS (CONSOLE_MAX_STR_LENGTH+1)/2 - -typedef struct -{ - char string_storage[CONSOLE_MAX_STR_LENGTH+1]; - char *args_start; - - const char *command; - const char *args[MAX_PARTS]; - unsigned int num_args; -} PARSE_RESULT; - -static char *str_skipblanks(char *str) -{ - while(*str && (*str == ' ' || *str == '\t' || *str == '\n')) - str++; - return str; -} - -static char *str_skiptoblank(char *str) -{ - while(*str && (*str != ' ' && *str != '\t' && *str != '\n')) - str++; - return str; -} - -/* static int digit(char c) { return '0' <= c && c <= '9'; } */ - -static int console_parse_start(PARSE_RESULT *result, const char *string, int length) -{ - char *str; - int len = sizeof(result->string_storage); - if(length < len) - len = length; - - str_copy(result->string_storage, string, length); - str = result->string_storage; - - /* get command */ - str = str_skipblanks(str); - result->command = str; - str = str_skiptoblank(str); - - if(*str) - { - str[0] = 0; - str++; - } - - result->args_start = str; - result->num_args = 0; - return 0; -} - -static int console_parse_args(PARSE_RESULT *result, const char *format) -{ - char command; - char *str; - int optional = 0; - int error = 0; - - str = result->args_start; - - while(1) - { - /* fetch command */ - command = *format; - format++; - - if(!command) - break; - - if(command == '?') - optional = 1; - else - { - str = str_skipblanks(str); - - if(!(*str)) /* error, non optional command needs value */ - { - if(!optional) - error = 1; - break; - } - - /* add token */ - if(*str == '"') - { - char *dst; - str++; - result->args[result->num_args++] = str; - - dst = str; /* we might have to process escape data */ - while(1) - { - if(str[0] == '"') - break; - else if(str[0] == '\\') - { - if(str[1] == '\\') - str++; /* skip due to escape */ - else if(str[1] == '"') - str++; /* skip due to escape */ - } - else if(str[0] == 0) - return 1; /* return error */ - - *dst = *str; - dst++; - str++; - } - - /* write null termination */ - *dst = 0; - } - else - { - result->args[result->num_args++] = str; - - if(command == 'r') /* rest of the string */ - break; - else if(command == 'i') /* validate int */ - str = str_skiptoblank(str); - else if(command == 'f') /* validate float */ - str = str_skiptoblank(str); - else if(command == 's') /* validate string */ - str = str_skiptoblank(str); - - if(str[0] != 0) /* check for end of string */ - { - str[0] = 0; - str++; - } - } - } - } - - return error; -} - -const char *console_arg_string(void *res, unsigned index) -{ - PARSE_RESULT *result = (PARSE_RESULT *)res; - if (index < 0 || index >= result->num_args) - return ""; - return result->args[index]; -} - -int console_arg_int(void *res, unsigned index) -{ - PARSE_RESULT *result = (PARSE_RESULT *)res; - if (index < 0 || index >= result->num_args) - return 0; - return atoi(result->args[index]); -} - -float console_arg_float(void *res, unsigned index) -{ - PARSE_RESULT *result = (PARSE_RESULT *)res; - if (index < 0 || index >= result->num_args) - return 0.0f; - return atof(result->args[index]); -} - -int console_arg_num(void *result) -{ - return ((PARSE_RESULT *)result)->num_args; -} - -static COMMAND *first_command = 0x0; - -COMMAND *console_find_command(const char *name) -{ - COMMAND *cmd; - for (cmd = first_command; cmd; cmd = cmd->next) - { - if (strcmp(cmd->name, name) == 0) - return cmd; - } - - return 0x0; -} - -void console_register(COMMAND *cmd) -{ - cmd->next = first_command; - first_command = cmd; -} - -static void (*print_callback)(const char *, void *) = 0x0; -static void *print_callback_userdata; - -void console_register_print_callback(void (*callback)(const char *, void *), void *user_data) -{ - print_callback = callback; - print_callback_userdata = user_data; -} - -void console_print(const char *str) -{ - if (print_callback) - print_callback(str, print_callback_userdata); -} - -void console_execute_line_stroked(int stroke, const char *str) -{ - PARSE_RESULT result; - COMMAND *command; - - char strokestr[2] = {'0', 0}; - if(stroke) - strokestr[0] = '1'; - - while(str) - { - const char *end = str; - const char *next_part = 0; - int in_string = 0; - - while(*end) - { - if(*end == '"') - in_string ^= 1; - else if(*end == '\\') /* escape sequences */ - { - if(end[1] == '"') - end++; - } - else if(!in_string) - { - if(*end == ';') /* command separator */ - { - next_part = end+1; - break; - } - else if(*end == '#') /* comment, no need to do anything more */ - break; - } - - end++; - } - - if(console_parse_start(&result, str, (end-str) + 1) != 0) - return; - - command = console_find_command(result.command); - - if(command) - { - int is_stroke_command = 0; - if(result.command[0] == '+') - { - /* insert the stroke direction token */ - result.args[result.num_args] = strokestr; - result.num_args++; - is_stroke_command = 1; - } - - if(stroke || is_stroke_command) - { - if(console_parse_args(&result, command->params)) - { - char buf[256]; - str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params); - console_print(buf); - } - else - command->callback(&result, command->user_data); - } - } - else - { - char buf[256]; - str_format(buf, sizeof(buf), "No such command: %s.", result.command); - console_print(buf); - } - - str = next_part; - } -} - -void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user) -{ - COMMAND *cmd; - for (cmd = first_command; cmd; cmd = cmd->next) - { - if(cmd->flags&flagmask) - { - if(str_find_nocase(cmd->name, str)) - callback(cmd->name, user); - } - } -} - - -COMMAND *console_get_command(const char *str) -{ - COMMAND *cmd; - for (cmd = first_command; cmd; cmd = cmd->next) - { - if(str_comp_nocase(cmd->name, str) == 0) - return cmd; - } - - return 0x0; -} - -void console_execute_line(const char *str) -{ - console_execute_line_stroked(1, str); -} - -static void console_execute_file_real(const char *filename) -{ - IOHANDLE file; - file = engine_openfile(filename, IOFLAG_READ); - - if(file) - { - char *line; - LINEREADER lr; - - dbg_msg("console", "executing '%s'", filename); - linereader_init(&lr, file); - - while((line = linereader_get(&lr))) - console_execute_line(line); - - io_close(file); - } - else - dbg_msg("console", "failed to open '%s'", filename); -} - -struct EXECFILE -{ - const char *filename; - struct EXECFILE *next; -}; - -void console_execute_file(const char *filename) -{ - static struct EXECFILE *first = 0; - struct EXECFILE this_file; - struct EXECFILE *cur; - struct EXECFILE *prev; - - /* make sure that this isn't being executed already */ - for(cur = first; cur; cur = cur->next) - if(strcmp(filename, cur->filename) == 0) - return; - - /* push this one to the stack */ - prev = first; - this_file.filename = filename; - this_file.next = first; - first = &this_file; - - /* execute file */ - console_execute_file_real(filename); - - /* pop this one from the stack */ - first = prev; -} - -static void con_echo(void *result, void *user_data) -{ - console_print(console_arg_string(result, 0)); -} - -static void con_exec(void *result, void *user_data) -{ - console_execute_file(console_arg_string(result, 0)); - -} - - -typedef struct -{ - CONFIG_INT_GETTER getter; - CONFIG_INT_SETTER setter; -} INT_VARIABLE_DATA; - -typedef struct -{ - CONFIG_STR_GETTER getter; - CONFIG_STR_SETTER setter; -} STR_VARIABLE_DATA; - -static void int_variable_command(void *result, void *user_data) -{ - INT_VARIABLE_DATA *data = (INT_VARIABLE_DATA *)user_data; - - if(console_arg_num(result)) - data->setter(&config, console_arg_int(result, 0)); - else - { - char buf[1024]; - str_format(buf, sizeof(buf), "Value: %d", data->getter(&config)); - console_print(buf); - } -} - -static void str_variable_command(void *result, void *user_data) -{ - STR_VARIABLE_DATA *data = (STR_VARIABLE_DATA *)user_data; - - if(console_arg_num(result)) - data->setter(&config, console_arg_string(result, 0)); - else - { - char buf[1024]; - str_format(buf, sizeof(buf), "Value: %s", data->getter(&config)); - console_print(buf); - } -} - -static void console_chain(void *result, void *user_data) -{ - COMMANDCHAIN *info = (COMMANDCHAIN *)user_data; - info->chain_callback(result, info->user_data, info->callback, info->callback_user_data); -} - -void console_chain_command(const char *cmd, COMMANDCHAIN *chaininfo, CONSOLE_CHAIN_CALLBACK cb, void *user) -{ - COMMAND *command = console_get_command(cmd); - - /* store info */ - chaininfo->chain_callback = cb; - chaininfo->callback = command->callback; - chaininfo->callback_user_data = command->user_data; - chaininfo->user_data = user; - - /* chain */ - command->callback = console_chain; - command->user_data = chaininfo; -} - -void console_init() -{ - MACRO_REGISTER_COMMAND("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_echo, 0x0, "Echo the text"); - MACRO_REGISTER_COMMAND("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_exec, 0x0, "Execute the specified file"); - - #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", flags, int_variable_command, &data, desc) } - #define MACRO_CONFIG_STR(name,len,def,flags,desc) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?r", flags, str_variable_command, &data, desc) } - - #include "e_config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} diff --git a/src/engine/e_console.h b/src/engine/e_console.h deleted file mode 100644 index a45bac10..00000000 --- a/src/engine/e_console.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _CONSOLE_H -#define _CONSOLE_H - -typedef void (*CONSOLE_CALLBACK)(void *result, void *user_data); -typedef void (*CONSOLE_CHAIN_CALLBACK)(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser); - -typedef struct COMMAND_t -{ - const char *name; - const char *params; - int flags; - CONSOLE_CALLBACK callback; - void *user_data; - const char *help; - struct COMMAND_t *next; -} COMMAND; - -typedef struct COMMANDCHAIN_t -{ - CONSOLE_CHAIN_CALLBACK chain_callback; - CONSOLE_CALLBACK callback; - void *callback_user_data; - void *user_data; -} COMMANDCHAIN; - -void console_init(); -void console_register(COMMAND *cmd); -void console_execute_line(const char *str); -void console_execute_line_stroked(int stroke, const char *str); -void console_execute_file(const char *filename); -void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user); -COMMAND *console_get_command(const char *cmd); -void console_chain_command(const char *cmd, COMMANDCHAIN *chaininfo, CONSOLE_CHAIN_CALLBACK cb, void *user); -void console_print(const char *str); -void console_register_print_callback(void (*callback)(const char *, void *user_data), void *user_data); - -/*int console_result_string(void *result, int index, const char **str); -int console_result_int(void *result, int index, int *i); -int console_result_float(void *result, int index, float *f);*/ - -const char *console_arg_string(void *result, unsigned index); -int console_arg_int(void *result, unsigned index); -float console_arg_float(void *result, unsigned index); -int console_arg_num(void *result); - -#define MACRO_REGISTER_COMMAND(name, params, flags, func, ptr, help) { static COMMAND cmd = { name, params, flags, func, ptr, help, 0x0}; console_register(&cmd); } - -#endif diff --git a/src/engine/e_datafile.cpp b/src/engine/e_datafile.cpp deleted file mode 100644 index 0317f9d0..00000000 --- a/src/engine/e_datafile.cpp +++ /dev/null @@ -1,677 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> -#include "e_datafile.h" -#include "e_engine.h" -#include <zlib.h> - -static const int DEBUG=0; - -typedef struct -{ - int type; - int start; - int num; -} DATAFILE_ITEM_TYPE; - -typedef struct -{ - int type_and_id; - int size; -} DATAFILE_ITEM; - -typedef struct -{ - char id[4]; - int version; - int size; - int swaplen; - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; -} DATAFILE_HEADER; - -typedef struct -{ - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; - char start[4]; -} DATAFILE_DATA; - -typedef struct -{ - DATAFILE_ITEM_TYPE *item_types; - int *item_offsets; - int *data_offsets; - int *data_sizes; - - char *item_start; - char *data_start; -} DATAFILE_INFO; - -struct DATAFILE_t -{ - IOHANDLE file; - DATAFILE_INFO info; - DATAFILE_HEADER header; - int data_start_offset; - char **data_ptrs; - char *data; -}; - -DATAFILE *datafile_load(const char *filename) -{ - DATAFILE *df; - IOHANDLE file; - DATAFILE_HEADER header; - unsigned readsize; - - unsigned *dst; - unsigned char *src; - unsigned j; - int size = 0; - int allocsize = 0; - - (void)dst; - (void)src; - (void)j; - - - dbg_msg("datafile", "datafile loading. filename='%s'", filename); - - file = engine_openfile(filename, IOFLAG_READ); - if(!file) - return 0; - - /* TODO: change this header */ - io_read(file, &header, sizeof(header)); - if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D') - { - if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A') - { - dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]); - return 0; - } - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int)); -#endif - if(header.version != 3 && header.version != 4) - { - dbg_msg("datafile", "wrong version. version=%x", header.version); - return 0; - } - - /* read in the rest except the data */ - size = 0; - size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE); - size += (header.num_items+header.num_raw_data)*sizeof(int); - if(header.version == 4) - size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */ - size += header.item_size; - - allocsize = size; - allocsize += sizeof(DATAFILE); /* add space for info structure */ - allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */ - - df = (DATAFILE*)mem_alloc(allocsize, 1); - df->header = header; - df->data_start_offset = sizeof(DATAFILE_HEADER) + size; - df->data_ptrs = (char**)(df+1); - df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *); - df->file = file; - - /* clear the data pointers */ - mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*)); - - /* read types, offsets, sizes and item data */ - readsize = io_read(file, df->data, size); - if(readsize != size) - { - dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); - return 0; - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(df->data, sizeof(int), header.swaplen / sizeof(int)); -#endif - - if(DEBUG) - { - dbg_msg("datafile", "allocsize=%d", allocsize); - dbg_msg("datafile", "readsize=%d", readsize); - dbg_msg("datafile", "swaplen=%d", header.swaplen); - dbg_msg("datafile", "item_size=%d", df->header.item_size); - } - - df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data; - df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types]; - df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items]; - df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data]; - - if(header.version == 4) - df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data]; - else - df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data]; - df->info.data_start = df->info.item_start + df->header.item_size; - - if(DEBUG) - dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); - - if(DEBUG) - { - /* - for(int i = 0; i < df->data.num_raw_data; i++) - { - void *p = datafile_get_data(df, i); - dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); - } - - for(int i = 0; i < datafile_num_items(df); i++) - { - int type, id; - void *data = datafile_get_item(df, i, &type, &id); - dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); - int *idata = (int*)data; - for(int k = 0; k < 3; k++) - dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); - } - - for(int i = 0; i < df->data.num_item_types; i++) - { - dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, - df->info.item_types[i].type, - df->info.item_types[i].start, - df->info.item_types[i].num); - for(int k = 0; k < df->info.item_types[i].num; k++) - { - int type, id; - datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); - if(type != df->info.item_types[i].type) - dbg_msg("map", "\tERROR"); - } - } - */ - } - - return df; -} - -int datafile_num_data(DATAFILE *df) -{ - return df->header.num_raw_data; -} - -/* always returns the size in the file */ -int datafile_get_datasize(DATAFILE *df, int index) -{ - if(index == df->header.num_raw_data-1) - return df->header.data_size-df->info.data_offsets[index]; - return df->info.data_offsets[index+1]-df->info.data_offsets[index]; -} - -static void *datafile_get_data_impl(DATAFILE *df, int index, int swap) -{ - /* load it if needed */ - if(!df->data_ptrs[index]) - { - /* fetch the data size */ - int datasize = datafile_get_datasize(df, index); - int swapsize = datasize; - - if(df->header.version == 4) - { - /* v4 has compressed data */ - void *temp = (char *)mem_alloc(datasize, 1); - unsigned long uncompressed_size = df->info.data_sizes[index]; - unsigned long s; - - dbg_msg("datafile", "loading data index=%d size=%d uncompressed=%d", index, datasize, uncompressed_size); - df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1); - - /* read the compressed data */ - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, temp, datasize); - - /* decompress the data, TODO: check for errors */ - s = uncompressed_size; - uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize); - swapsize = s; - - /* clean up the temporary buffers */ - mem_free(temp); - } - else - { - /* load the data */ - dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); - df->data_ptrs[index] = (char *)mem_alloc(datasize, 1); - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, df->data_ptrs[index], datasize); - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - if(swap && swapsize) - swap_endian(df->data_ptrs[index], sizeof(int), swapsize/sizeof(int)); -#endif - } - - return df->data_ptrs[index]; -} - -void *datafile_get_data(DATAFILE *df, int index) -{ - return datafile_get_data_impl(df, index, 0); -} - -void *datafile_get_data_swapped(DATAFILE *df, int index) -{ - return datafile_get_data_impl(df, index, 1); -} - -void datafile_unload_data(DATAFILE *df, int index) -{ - if(index < 0) - return; - - /* */ - mem_free(df->data_ptrs[index]); - df->data_ptrs[index] = 0x0; -} - -int datafile_get_itemsize(DATAFILE *df, int index) -{ - if(index == df->header.num_items-1) - return df->header.item_size-df->info.item_offsets[index]; - return df->info.item_offsets[index+1]-df->info.item_offsets[index]; -} - -void *datafile_get_item(DATAFILE *df, int index, int *type, int *id) -{ - DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]); - if(type) - *type = (i->type_and_id>>16)&0xffff; /* remove sign extention */ - if(id) - *id = i->type_and_id&0xffff; - return (void *)(i+1); -} - -void datafile_get_type(DATAFILE *df, int type, int *start, int *num) -{ - int i; - for(i = 0; i < df->header.num_item_types; i++) - { - if(df->info.item_types[i].type == type) - { - *start = df->info.item_types[i].start; - *num = df->info.item_types[i].num; - return; - } - } - - *start = 0; - *num = 0; -} - -void *datafile_find_item(DATAFILE *df, int type, int id) -{ - int start, num,i ; - int item_id; - void *item; - - datafile_get_type(df, type, &start, &num); - for(i = 0; i < num; i++) - { - item = datafile_get_item(df, start+i,0, &item_id); - if(id == item_id) - return item; - } - return 0; -} - -int datafile_num_items(DATAFILE *df) -{ - return df->header.num_items; -} - -void datafile_unload(DATAFILE *df) -{ - if(df) - { - /* free the data that is loaded */ - int i; - for(i = 0; i < df->header.num_raw_data; i++) - mem_free(df->data_ptrs[i]); - - io_close(df->file); - mem_free(df); - } -} - -/* DATAFILE output */ -typedef struct -{ - int uncompressed_size; - int compressed_size; - void *compressed_data; -} DATA_INFO; - -typedef struct -{ - int type; - int id; - int size; - int next; - int prev; - void *data; -} ITEM_INFO; - -typedef struct -{ - int num; - int first; - int last; -} ITEMTYPE_INFO; - -/* */ -struct DATAFILE_OUT_t -{ - IOHANDLE file; - int num_items; - int num_datas; - int num_item_types; - ITEMTYPE_INFO item_types[0xffff]; - ITEM_INFO items[1024]; - DATA_INFO datas[1024]; -}; - -DATAFILE_OUT *datafile_create(const char *filename) -{ - int i; - DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); - df->file = engine_openfile(filename, IOFLAG_WRITE); - if(!df->file) - { - mem_free(df); - return 0; - } - - df->num_items = 0; - df->num_datas = 0; - df->num_item_types = 0; - mem_zero(&df->item_types, sizeof(df->item_types)); - - for(i = 0; i < 0xffff; i++) - { - df->item_types[i].first = -1; - df->item_types[i].last = -1; - } - - return df; -} - -int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data) -{ - df->items[df->num_items].type = type; - df->items[df->num_items].id = id; - df->items[df->num_items].size = size; - - /* - dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); - int i; - for(i = 0; i < size/4; i++) - dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); - */ - - /* copy data */ - df->items[df->num_items].data = mem_alloc(size, 1); - mem_copy(df->items[df->num_items].data, data, size); - - if(!df->item_types[type].num) /* count item types */ - df->num_item_types++; - - /* link */ - df->items[df->num_items].prev = df->item_types[type].last; - df->items[df->num_items].next = -1; - - if(df->item_types[type].last != -1) - df->items[df->item_types[type].last].next = df->num_items; - df->item_types[type].last = df->num_items; - - if(df->item_types[type].first == -1) - df->item_types[type].first = df->num_items; - - df->item_types[type].num++; - - df->num_items++; - return df->num_items-1; -} - -int datafile_add_data(DATAFILE_OUT *df, int size, void *data) -{ - DATA_INFO *info = &df->datas[df->num_datas]; - unsigned long s = compressBound(size); - void *compdata = mem_alloc(s, 1); /* temporary buffer that we use duing compression */ - - int result = compress((Bytef*)compdata, &s, (Bytef*)data, size); - if(result != Z_OK) - { - dbg_msg("datafile", "compression error %d", result); - dbg_assert(0, "zlib error"); - } - - info->uncompressed_size = size; - info->compressed_size = (int)s; - info->compressed_data = mem_alloc(info->compressed_size, 1); - mem_copy(info->compressed_data, compdata, info->compressed_size); - mem_free(compdata); - - df->num_datas++; - return df->num_datas-1; -} - -int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data) -{ -#if defined(CONF_ARCH_ENDIAN_BIG) - void *swapped = mem_alloc(size, 1); /* temporary buffer that we use duing compression */ - int index; - mem_copy(swapped, data, size); - swap_endian(&swapped, sizeof(int), size/sizeof(int)); - index = datafile_add_data(df, size, swapped); - mem_free(swapped); - return index; -#else - return datafile_add_data(df, size, data); -#endif -} - - -int datafile_finish(DATAFILE_OUT *df) -{ - int itemsize = 0; - int i, count, offset; - int typessize, headersize, offsetsize, filesize, swapsize; - int datasize = 0; - DATAFILE_ITEM_TYPE info; - DATAFILE_ITEM itm; - DATAFILE_HEADER header; - - /* we should now write this file! */ - if(DEBUG) - dbg_msg("datafile", "writing"); - - /* calculate sizes */ - for(i = 0; i < df->num_items; i++) - { - if(DEBUG) - dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM)); - itemsize += df->items[i].size + sizeof(DATAFILE_ITEM); - } - - - for(i = 0; i < df->num_datas; i++) - datasize += df->datas[i].compressed_size; - - /* calculate the complete size */ - typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE); - headersize = sizeof(DATAFILE_HEADER); - offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); - filesize = headersize + typessize + offsetsize + itemsize + datasize; - swapsize = filesize - datasize; - - (void)swapsize; - - if(DEBUG) - dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); - - /* construct header */ - { - header.id[0] = 'D'; - header.id[1] = 'A'; - header.id[2] = 'T'; - header.id[3] = 'A'; - header.version = 4; - header.size = filesize - 16; - header.swaplen = swapsize - 16; - header.num_item_types = df->num_item_types; - header.num_items = df->num_items; - header.num_raw_data = df->num_datas; - header.item_size = itemsize; - header.data_size = datasize; - - /* TODO: apply swapping */ - /* write header */ - if(DEBUG) - dbg_msg("datafile", "headersize=%d", sizeof(header)); - io_write(df->file, &header, sizeof(header)); - } - - /* write types */ - for(i = 0, count = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write info */ - info.type = i; - info.start = count; - info.num = df->item_types[i].num; - if(DEBUG) - dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); - io_write(df->file, &info, sizeof(info)); - count += df->item_types[i].num; - } - } - - /* write item offsets */ - for(i = 0, offset = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - if(DEBUG) - dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->items[k].size + sizeof(DATAFILE_ITEM); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data offsets */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->datas[i].compressed_size; - } - - /* write data uncompressed sizes */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - /* - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - */ - io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int)); - } - - /* write items */ - for(i = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - itm.type_and_id = (i<<16)|df->items[k].id; - itm.size = df->items[k].size; - if(DEBUG) - dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); - - io_write(df->file, &itm, sizeof(itm)); - io_write(df->file, df->items[k].data, df->items[k].size); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data */ - for(i = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size); - io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size); - } - - /* free data */ - for(i = 0; i < df->num_items; i++) - mem_free(df->items[i].data); - - - io_close(df->file); - mem_free(df); - - if(DEBUG) - dbg_msg("datafile", "done"); - return 0; -} - -#define BUFFER_SIZE 64*1024 - -int datafile_crc(const char *filename) -{ - unsigned char buffer[BUFFER_SIZE]; - IOHANDLE file; - int crc = 0; - unsigned bytes = 0; - - file = engine_openfile(filename, IOFLAG_READ); - if(!file) - return 0; - - while(1) - { - bytes = io_read(file, buffer, BUFFER_SIZE); - if(bytes <= 0) - break; - crc = crc32(crc, buffer, bytes); - } - - io_close(file); - - return crc; -} diff --git a/src/engine/e_datafile.h b/src/engine/e_datafile.h deleted file mode 100644 index 203f415c..00000000 --- a/src/engine/e_datafile.h +++ /dev/null @@ -1,29 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -/* raw datafile access */ -typedef struct DATAFILE_t DATAFILE; - -/* read access */ -DATAFILE *datafile_load(const char *filename); -DATAFILE *datafile_load_old(const char *filename); -void *datafile_get_data(DATAFILE *df, int index); -void *datafile_get_data_swapped(DATAFILE *df, int index); /* makes sure that the data is 32bit LE ints when saved */ -int datafile_get_datasize(DATAFILE *df, int index); -void datafile_unload_data(DATAFILE *df, int index); -void *datafile_get_item(DATAFILE *df, int index, int *type, int *id); -int datafile_get_itemsize(DATAFILE *df, int index); -void datafile_get_type(DATAFILE *df, int type, int *start, int *num); -void *datafile_find_item(DATAFILE *df, int type, int id); -int datafile_num_items(DATAFILE *df); -int datafile_num_data(DATAFILE *df); -void datafile_unload(DATAFILE *df); - -int datafile_crc(const char *filename); - -/* write access */ -typedef struct DATAFILE_OUT_t DATAFILE_OUT; -DATAFILE_OUT *datafile_create(const char *filename); -int datafile_add_data(DATAFILE_OUT *df, int size, void *data); -int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data); -int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data); -int datafile_finish(DATAFILE_OUT *df); diff --git a/src/engine/e_demorec.cpp b/src/engine/e_demorec.cpp deleted file mode 100644 index 1bab1b8d..00000000 --- a/src/engine/e_demorec.cpp +++ /dev/null @@ -1,640 +0,0 @@ -#include <base/system.h> -#include "e_demorec.h" -#include "e_memheap.h" -#include "e_snapshot.h" -#include "e_compression.h" -#include "e_network.h" -#include "e_engine.h" -#include "e_if_other.h" - -static IOHANDLE record_file = 0; -static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1}; - -/* Record */ -static int record_lasttickmarker = -1; -static int record_lastkeyframe; -static unsigned char record_lastsnapshotdata[CSnapshot::MAX_SIZE]; - -int demorec_isrecording() { return record_file != 0; } - -int demorec_record_start(const char *filename, const char *netversion, const char *map, int crc, const char *type) -{ - DEMOREC_HEADER header; - if(record_file) - return -1; - - record_file = engine_openfile(filename, IOFLAG_WRITE); - - if(!record_file) - { - dbg_msg("demorec/record", "Unable to open '%s' for recording", filename); - return -1; - } - - /* write header */ - mem_zero(&header, sizeof(header)); - mem_copy(header.marker, header_marker, sizeof(header.marker)); - str_copy(header.netversion, netversion, sizeof(header.netversion)); - str_copy(header.map, map, sizeof(header.map)); - str_copy(header.type, type, sizeof(header.type)); - header.crc[0] = (crc>>24)&0xff; - header.crc[1] = (crc>>16)&0xff; - header.crc[2] = (crc>>8)&0xff; - header.crc[3] = (crc)&0xff; - io_write(record_file, &header, sizeof(header)); - - record_lastkeyframe = -1; - record_lasttickmarker = -1; - - dbg_msg("demorec/record", "Recording to '%s'", filename); - return 0; -} - -/* - Tickmarker - 7 = Always set - 6 = Keyframe flag - 0-5 = Delta tick - - Normal - 7 = Not set - 5-6 = Type - 0-4 = Size -*/ - -enum -{ - CHUNKTYPEFLAG_TICKMARKER = 0x80, - CHUNKTICKFLAG_KEYFRAME = 0x40, /* only when tickmarker is set*/ - - CHUNKMASK_TICK = 0x3f, - CHUNKMASK_TYPE = 0x60, - CHUNKMASK_SIZE = 0x1f, - - CHUNKTYPE_SNAPSHOT = 1, - CHUNKTYPE_MESSAGE = 2, - CHUNKTYPE_DELTA = 3, - - CHUNKFLAG_BIGSIZE = 0x10 -}; - -static void demorec_record_write_tickmarker(int tick, int keyframe) -{ - if(record_lasttickmarker == -1 || tick-record_lasttickmarker > 63 || keyframe) - { - unsigned char chunk[5]; - chunk[0] = CHUNKTYPEFLAG_TICKMARKER; - chunk[1] = (tick>>24)&0xff; - chunk[2] = (tick>>16)&0xff; - chunk[3] = (tick>>8)&0xff; - chunk[4] = (tick)&0xff; - - if(keyframe) - chunk[0] |= CHUNKTICKFLAG_KEYFRAME; - - io_write(record_file, chunk, sizeof(chunk)); - } - else - { - unsigned char chunk[1]; - chunk[0] = CHUNKTYPEFLAG_TICKMARKER | (tick-record_lasttickmarker); - io_write(record_file, chunk, sizeof(chunk)); - } - - record_lasttickmarker = tick; -} - -static void demorec_record_write(int type, const void *data, int size) -{ - char buffer[64*1024]; - char buffer2[64*1024]; - unsigned char chunk[3]; - - if(!record_file) - return; - - - /* pad the data with 0 so we get an alignment of 4, - else the compression won't work and miss some bytes */ - mem_copy(buffer2, data, size); - while(size&3) - buffer2[size++] = 0; - size = intpack_compress(buffer2, size, buffer); /* buffer2 -> buffer */ - size = CNetBase::Compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */ - - - chunk[0] = ((type&0x3)<<5); - if(size < 30) - { - chunk[0] |= size; - io_write(record_file, chunk, 1); - } - else - { - if(size < 256) - { - chunk[0] |= 30; - chunk[1] = size&0xff; - io_write(record_file, chunk, 2); - } - else - { - chunk[0] |= 31; - chunk[1] = size&0xff; - chunk[2] = size>>8; - io_write(record_file, chunk, 3); - } - } - - io_write(record_file, buffer2, size); -} - -void demorec_record_snapshot(int tick, const void *data, int size) -{ - if(record_lastkeyframe == -1 || (tick-record_lastkeyframe) > SERVER_TICK_SPEED*5) - { - /* write full tickmarker */ - demorec_record_write_tickmarker(tick, 1); - - /* write snapshot */ - demorec_record_write(CHUNKTYPE_SNAPSHOT, data, size); - - record_lastkeyframe = tick; - mem_copy(record_lastsnapshotdata, data, size); - } - else - { - /* create delta, prepend tick */ - char delta_data[CSnapshot::MAX_SIZE+sizeof(int)]; - int delta_size; - - /* write tickmarker */ - demorec_record_write_tickmarker(tick, 0); - - delta_size = CSnapshot::CreateDelta((CSnapshot*)record_lastsnapshotdata, (CSnapshot*)data, &delta_data); - if(delta_size) - { - /* record delta */ - demorec_record_write(CHUNKTYPE_DELTA, delta_data, delta_size); - mem_copy(record_lastsnapshotdata, data, size); - } - } -} - -void demorec_record_message(const void *data, int size) -{ - demorec_record_write(CHUNKTYPE_MESSAGE, data, size); -} - -int demorec_record_stop() -{ - if(!record_file) - return -1; - - dbg_msg("demorec/record", "Stopped recording"); - io_close(record_file); - record_file = 0; - return 0; -} - -/* Playback */ -typedef struct KEYFRAME -{ - long filepos; - int tick; -} KEYFRAME; - -typedef struct KEYFRAME_SEARCH -{ - KEYFRAME frame; - struct KEYFRAME_SEARCH *next; -} KEYFRAME_SEARCH; - -static IOHANDLE play_file = 0; -static DEMOREC_PLAYCALLBACK play_callback_snapshot = 0; -static DEMOREC_PLAYCALLBACK play_callback_message = 0; -static KEYFRAME *keyframes = 0; - -static DEMOREC_PLAYBACKINFO playbackinfo; -static unsigned char playback_lastsnapshotdata[CSnapshot::MAX_SIZE]; -static int playback_lastsnapshotdata_size = -1; - - -const DEMOREC_PLAYBACKINFO *demorec_playback_info() { return &playbackinfo; } -int demorec_isplaying() { return play_file != 0; } - -int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb) -{ - play_callback_snapshot = snapshot_cb; - play_callback_message = message_cb; - return 0; -} - -static int read_chunk_header(int *type, int *size, int *tick) -{ - unsigned char chunk = 0; - - *size = 0; - *type = 0; - - if(io_read(play_file, &chunk, sizeof(chunk)) != sizeof(chunk)) - return -1; - - if(chunk&CHUNKTYPEFLAG_TICKMARKER) - { - /* decode tick marker */ - int tickdelta = chunk&(CHUNKMASK_TICK); - *type = chunk&(CHUNKTYPEFLAG_TICKMARKER|CHUNKTICKFLAG_KEYFRAME); - - if(tickdelta == 0) - { - unsigned char tickdata[4]; - if(io_read(play_file, tickdata, sizeof(tickdata)) != sizeof(tickdata)) - return -1; - *tick = (tickdata[0]<<24) | (tickdata[1]<<16) | (tickdata[2]<<8) | tickdata[3]; - } - else - { - *tick += tickdelta; - } - - } - else - { - /* decode normal chunk */ - *type = (chunk&CHUNKMASK_TYPE)>>5; - *size = chunk&CHUNKMASK_SIZE; - - if(*size == 30) - { - unsigned char sizedata[1]; - if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata)) - return -1; - *size = sizedata[0]; - - } - else if(*size == 31) - { - unsigned char sizedata[2]; - if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata)) - return -1; - *size = (sizedata[1]<<8) | sizedata[0]; - } - } - - return 0; -} - -static void scan_file() -{ - long start_pos; - HEAP *heap = 0; - KEYFRAME_SEARCH *first_key = 0; - KEYFRAME_SEARCH *current_key = 0; - /*DEMOREC_CHUNK chunk;*/ - int chunk_size, chunk_type, chunk_tick = 0; - int i; - - heap = memheap_create(); - - start_pos = io_tell(play_file); - playbackinfo.seekable_points = 0; - - while(1) - { - long current_pos = io_tell(play_file); - - if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick)) - break; - - /* read the chunk */ - if(chunk_type&CHUNKTYPEFLAG_TICKMARKER) - { - if(chunk_type&CHUNKTICKFLAG_KEYFRAME) - { - KEYFRAME_SEARCH *key; - - /* save the position */ - key = (KEYFRAME_SEARCH *)memheap_allocate(heap, sizeof(KEYFRAME_SEARCH)); - key->frame.filepos = current_pos; - key->frame.tick = chunk_tick; - key->next = 0; - if(current_key) - current_key->next = key; - if(!first_key) - first_key = key; - current_key = key; - playbackinfo.seekable_points++; - } - - if(playbackinfo.first_tick == -1) - playbackinfo.first_tick = chunk_tick; - playbackinfo.last_tick = chunk_tick; - } - else if(chunk_size) - io_skip(play_file, chunk_size); - - } - - /* copy all the frames to an array instead for fast access */ - keyframes = (KEYFRAME*)mem_alloc(playbackinfo.seekable_points*sizeof(KEYFRAME), 1); - for(current_key = first_key, i = 0; current_key; current_key = current_key->next, i++) - keyframes[i] = current_key->frame; - - /* destroy the temporary heap and seek back to the start */ - memheap_destroy(heap); - io_seek(play_file, start_pos, IOSEEK_START); -} - -static void do_tick() -{ - static char compresseddata[CSnapshot::MAX_SIZE]; - static char decompressed[CSnapshot::MAX_SIZE]; - static char data[CSnapshot::MAX_SIZE]; - int chunk_type, chunk_tick, chunk_size; - int data_size; - int got_snapshot = 0; - - /* update ticks */ - playbackinfo.previous_tick = playbackinfo.current_tick; - playbackinfo.current_tick = playbackinfo.next_tick; - chunk_tick = playbackinfo.current_tick; - - while(1) - { - if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick)) - { - /* stop on error or eof */ - dbg_msg("demorec", "end of file"); - demorec_playback_pause(); - break; - } - - /* read the chunk */ - if(chunk_size) - { - if(io_read(play_file, compresseddata, chunk_size) != (unsigned)chunk_size) - { - /* stop on error or eof */ - dbg_msg("demorec", "error reading chunk"); - demorec_playback_stop(); - break; - } - - data_size = CNetBase::Decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed)); - if(data_size < 0) - { - /* stop on error or eof */ - dbg_msg("demorec", "error during network decompression"); - demorec_playback_stop(); - break; - } - - data_size = intpack_decompress(decompressed, data_size, data); - - if(data_size < 0) - { - dbg_msg("demorec", "error during intpack decompression"); - demorec_playback_stop(); - break; - } - } - - if(chunk_type == CHUNKTYPE_DELTA) - { - /* process delta snapshot */ - static char newsnap[CSnapshot::MAX_SIZE]; - - got_snapshot = 1; - - data_size = CSnapshot::UnpackDelta((CSnapshot*)playback_lastsnapshotdata, (CSnapshot*)newsnap, data, data_size); - - if(data_size >= 0) - { - if(play_callback_snapshot) - play_callback_snapshot(newsnap, data_size); - - playback_lastsnapshotdata_size = data_size; - mem_copy(playback_lastsnapshotdata, newsnap, data_size); - } - else - dbg_msg("demorec", "error duing unpacking of delta, err=%d", data_size); - } - else if(chunk_type == CHUNKTYPE_SNAPSHOT) - { - /* process full snapshot */ - got_snapshot = 1; - - playback_lastsnapshotdata_size = data_size; - mem_copy(playback_lastsnapshotdata, data, data_size); - if(play_callback_snapshot) - play_callback_snapshot(data, data_size); - } - else - { - /* if there were no snapshots in this tick, replay the last one */ - if(!got_snapshot && play_callback_snapshot && playback_lastsnapshotdata_size != -1) - { - got_snapshot = 1; - play_callback_snapshot(playback_lastsnapshotdata, playback_lastsnapshotdata_size); - } - - /* check the remaining types */ - if(chunk_type&CHUNKTYPEFLAG_TICKMARKER) - { - playbackinfo.next_tick = chunk_tick; - break; - } - else if(chunk_type == CHUNKTYPE_MESSAGE) - { - if(play_callback_message) - play_callback_message(data, data_size); - } - } - } -} - -void demorec_playback_pause() -{ - playbackinfo.paused = 1; -} - -void demorec_playback_unpause() -{ - if(playbackinfo.paused) - { - /*playbackinfo.start_tick = playbackinfo.current_tick; - playbackinfo.start_time = time_get();*/ - playbackinfo.paused = 0; - } -} - -int demorec_playback_load(const char *filename) -{ - play_file = engine_openfile(filename, IOFLAG_READ); - if(!play_file) - { - dbg_msg("demorec/playback", "could not open '%s'", filename); - return -1; - } - - /* clear the playback info */ - mem_zero(&playbackinfo, sizeof(playbackinfo)); - playbackinfo.first_tick = -1; - playbackinfo.last_tick = -1; - /*playbackinfo.start_tick = -1;*/ - playbackinfo.next_tick = -1; - playbackinfo.current_tick = -1; - playbackinfo.previous_tick = -1; - playbackinfo.speed = 1; - - playback_lastsnapshotdata_size = -1; - - /* read the header */ - io_read(play_file, &playbackinfo.header, sizeof(playbackinfo.header)); - if(mem_comp(playbackinfo.header.marker, header_marker, sizeof(header_marker)) != 0) - { - dbg_msg("demorec/playback", "'%s' is not a demo file", filename); - io_close(play_file); - play_file = 0; - return -1; - } - - /* scan the file for interessting points */ - scan_file(); - - /* ready for playback */ - return 0; -} - -int demorec_playback_nextframe() -{ - do_tick(); - return demorec_isplaying(); -} - -int demorec_playback_play() -{ - /* fill in previous and next tick */ - while(playbackinfo.previous_tick == -1 && demorec_isplaying()) - do_tick(); - - /* set start info */ - /*playbackinfo.start_tick = playbackinfo.previous_tick; - playbackinfo.start_time = time_get();*/ - playbackinfo.current_time = playbackinfo.previous_tick*time_freq()/SERVER_TICK_SPEED; - playbackinfo.last_update = time_get(); - return 0; -} - -int demorec_playback_set(float percent) -{ - int keyframe; - int wanted_tick; - if(!play_file) - return -1; - - /* -5 because we have to have a current tick and previous tick when we do the playback */ - wanted_tick = playbackinfo.first_tick + (int)((playbackinfo.last_tick-playbackinfo.first_tick)*percent) - 5; - - keyframe = (int)(playbackinfo.seekable_points*percent); - - if(keyframe < 0 || keyframe >= playbackinfo.seekable_points) - return -1; - - /* get correct key frame */ - if(keyframes[keyframe].tick < wanted_tick) - while(keyframe < playbackinfo.seekable_points-1 && keyframes[keyframe].tick < wanted_tick) - keyframe++; - - while(keyframe && keyframes[keyframe].tick > wanted_tick) - keyframe--; - - /* seek to the correct keyframe */ - io_seek(play_file, keyframes[keyframe].filepos, IOSEEK_START); - - /*playbackinfo.start_tick = -1;*/ - playbackinfo.next_tick = -1; - playbackinfo.current_tick = -1; - playbackinfo.previous_tick = -1; - - /* playback everything until we hit our tick */ - while(playbackinfo.previous_tick < wanted_tick) - do_tick(); - - demorec_playback_play(); - - return 0; -} - -void demorec_playback_setspeed(float speed) -{ - playbackinfo.speed = speed; -} - -int demorec_playback_update() -{ - int64 now = time_get(); - int64 deltatime = now-playbackinfo.last_update; - playbackinfo.last_update = now; - - if(!demorec_isplaying()) - return 0; - - if(playbackinfo.paused) - { - - } - else - { - int64 freq = time_freq(); - playbackinfo.current_time += (int64)(deltatime*(double)playbackinfo.speed); - - while(1) - { - int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; - - /* break if we are ready */ - if(curtick_start > playbackinfo.current_time) - break; - - /* do one more tick */ - do_tick(); - - if(playbackinfo.paused) - return 0; - } - - /* update intratick */ - { - int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED; - int64 prevtick_start = (playbackinfo.previous_tick)*freq/SERVER_TICK_SPEED; - playbackinfo.intratick = (playbackinfo.current_time - prevtick_start) / (float)(curtick_start-prevtick_start); - playbackinfo.ticktime = (playbackinfo.current_time - prevtick_start) / (float)freq; - } - - if(playbackinfo.current_tick == playbackinfo.previous_tick || - playbackinfo.current_tick == playbackinfo.next_tick) - { - dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d", - playbackinfo.previous_tick, playbackinfo.current_tick, playbackinfo.next_tick); - } - } - - return 0; -} - -int demorec_playback_stop() -{ - if(!play_file) - return -1; - - dbg_msg("demorec/playback", "Stopped playback"); - io_close(play_file); - play_file = 0; - mem_free(keyframes); - keyframes = 0; - return 0; -} - - diff --git a/src/engine/e_demorec.h b/src/engine/e_demorec.h deleted file mode 100644 index 9716b463..00000000 --- a/src/engine/e_demorec.h +++ /dev/null @@ -1,69 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef _DEMOREC_H -#define _DEMOREC_H - -typedef struct DEMOREC_HEADER -{ - char marker[8]; - char netversion[64]; - char map[64]; - unsigned char crc[4]; - char type[8]; -} DEMOREC_HEADER; - -typedef struct DEMOREC_CHUNK -{ - char type[2]; - unsigned short size; -} DEMOREC_CHUNK; - -typedef struct DEMOREC_TICKMARKER -{ - int tick; -} DEMOREC_TICKMARKER; - -typedef struct DEMOREC_PLAYBACKINFO -{ - DEMOREC_HEADER header; - - int paused; - float speed; - - int64 last_update; - int64 current_time; - - int first_tick; - int last_tick; - - int seekable_points; - - int next_tick; - int current_tick; - int previous_tick; - - float intratick; - float ticktime; -} DEMOREC_PLAYBACKINFO; - -int demorec_record_start(const char *filename, const char *netversion, const char *map, int map_crc, const char *type); -int demorec_isrecording(); -void demorec_record_snapshot(int tick, const void *data, int size); -void demorec_record_message(const void *data, int size); -int demorec_record_stop(); - -typedef void (*DEMOREC_PLAYCALLBACK)(void *data, int size); - -int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb); -int demorec_playback_load(const char *filename); -int demorec_playback_nextframe(); -int demorec_playback_play(); -void demorec_playback_pause(); -void demorec_playback_unpause(); -void demorec_playback_setspeed(float speed); -int demorec_playback_set(float precent); -int demorec_playback_update(); -const DEMOREC_PLAYBACKINFO *demorec_playback_info(); -int demorec_isplaying(); -int demorec_playback_stop(); - -#endif diff --git a/src/engine/e_engine.cpp b/src/engine/e_engine.cpp deleted file mode 100644 index 4475478a..00000000 --- a/src/engine/e_engine.cpp +++ /dev/null @@ -1,596 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#include <base/system.h> - -#include <engine/e_server_interface.h> -#include <engine/e_config.h> -#include <engine/e_console.h> -#include <engine/e_engine.h> -#include <engine/e_network.h> -#include "e_linereader.h" - -/* compiled-in data-dir path */ -#define DATA_DIR "data" - -static JOBPOOL hostlookuppool; -static int engine_find_datadir(char *argv0); - -static void con_dbg_dumpmem(void *result, void *user_data) -{ - mem_debug_dump(); -} - -static void con_dbg_lognetwork(void *result, void *user_data) -{ - CNetBase::OpenLog("network_sent.dat", "network_recv.dat"); -} - - -static char application_save_path[512] = {0}; -static char datadir[512] = {0}; - -const char *engine_savepath(const char *filename, char *buffer, int max) -{ - str_format(buffer, max, "%s/%s", application_save_path, filename); - return buffer; -} - -void engine_init(const char *appname) -{ - dbg_logger_stdout(); - dbg_logger_debugger(); - - /* */ - dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING); -#ifdef CONF_ARCH_ENDIAN_LITTLE - dbg_msg("engine", "arch is little endian"); -#elif defined(CONF_ARCH_ENDIAN_BIG) - dbg_msg("engine", "arch is big endian"); -#else - dbg_msg("engine", "unknown endian"); -#endif - - /* init the network */ - net_init(); - CNetBase::Init(); - - /* create storage location */ - { - char path[1024] = {0}; - fs_storage_path(appname, application_save_path, sizeof(application_save_path)); - if(fs_makedir(application_save_path) == 0) - { - str_format(path, sizeof(path), "%s/screenshots", application_save_path); - fs_makedir(path); - - str_format(path, sizeof(path), "%s/maps", application_save_path); - fs_makedir(path); - - str_format(path, sizeof(path), "%s/downloadedmaps", application_save_path); - fs_makedir(path); - - str_format(path, sizeof(path), "%s/demos", application_save_path); - fs_makedir(path); - } - } - - /* init console and add the console logger */ - console_init(); - dbg_logger(console_print); - - jobs_initpool(&hostlookuppool, 1); - - MACRO_REGISTER_COMMAND("dbg_dumpmem", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_dumpmem, 0x0, "Dump the memory"); - MACRO_REGISTER_COMMAND("dbg_lognetwork", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_lognetwork, 0x0, "Log the network"); - - /* reset the config */ - config_reset(); -} - - -void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user) -{ - char buffer[1024]; - - /* list current directory */ - if(types&LISTDIRTYPE_CURRENT) - { - fs_listdir(path, cb, user); - } - - /* list users directory */ - if(types&LISTDIRTYPE_SAVE) - { - engine_savepath(path, buffer, sizeof(buffer)); - fs_listdir(buffer, cb, user); - } - - /* list datadir directory */ - if(types&LISTDIRTYPE_DATA) - { - str_format(buffer, sizeof(buffer), "%s/%s", datadir, path); - fs_listdir(buffer, cb, user); - } -} - -void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags) -{ - if(flags&IOFLAG_WRITE) - engine_savepath(filename, buffer, buffer_size); - else - { - IOHANDLE handle = 0; - - /* check current directory */ - handle = io_open(filename, flags); - if(handle) - { - str_copy(buffer, filename, buffer_size); - io_close(handle); - return; - } - - /* check user directory */ - engine_savepath(filename, buffer, buffer_size); - handle = io_open(buffer, flags); - if(handle) - { - io_close(handle); - return; - } - - /* check normal data directory */ - str_format(buffer, buffer_size, "%s/%s", datadir, filename); - handle = io_open(buffer, flags); - if(handle) - { - io_close(handle); - return; - } - } - - buffer[0] = 0; -} - -IOHANDLE engine_openfile(const char *filename, int flags) -{ - char buffer[1024]; - - if(flags&IOFLAG_WRITE) - { - engine_savepath(filename, buffer, sizeof(buffer)); - return io_open(buffer, flags); - } - else - { - IOHANDLE handle = 0; - - /* check current directory */ - handle = io_open(filename, flags); - if(handle) - return handle; - - /* check user directory */ - engine_savepath(filename, buffer, sizeof(buffer)); - handle = io_open(buffer, flags); - if(handle) - return handle; - - /* check normal data directory */ - str_format(buffer, sizeof(buffer), "%s/%s", datadir, filename); - handle = io_open(buffer, flags); - if(handle) - return handle; - } - return 0; -} - -void engine_parse_arguments(int argc, char **argv) -{ - /* load the configuration */ - int i; - - /* check for datadir override */ - for(i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'd' && argv[i][2] == 0 && argc - i > 1) - { - str_copy(datadir, argv[i+1], sizeof(datadir)); - i++; - } - } - - /* search for data directory */ - engine_find_datadir(argv[0]); - - dbg_msg("engine/datadir", "paths used:"); - dbg_msg("engine/datadir", "\t."); - dbg_msg("engine/datadir", "\t%s", application_save_path); - dbg_msg("engine/datadir", "\t%s", datadir); - dbg_msg("engine/datadir", "saving files to: %s", application_save_path); - - - /* check for scripts to execute */ - for(i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) - { - console_execute_file(argv[i+1]); - i++; - } - } - - /* search arguments for overrides */ - { - int i; - for(i = 1; i < argc; i++) - console_execute_line(argv[i]); - } - - console_execute_file("autoexec.cfg"); - - /* open logfile if needed */ - if(config.logfile[0]) - dbg_logger_file(config.logfile); - - /* set default servers and load from disk*/ - mastersrv_default(); - mastersrv_load(); -} - - -static IOHANDLE config_file = 0; - -int engine_config_write_start() -{ - config_save(); - config_file = engine_openfile("settings.cfg", IOFLAG_WRITE); - if(config_file == 0) - return -1; - return 0; -} - -void engine_config_write_line(const char *line) -{ - if(config_file) - { -#if defined(CONF_FAMILY_WINDOWS) - static const char newline[] = "\r\n"; -#else - static const char newline[] = "\n"; -#endif - io_write(config_file, line, strlen(line)); - io_write(config_file, newline, sizeof(newline)-1); - } -} - -void engine_config_write_stop() -{ - io_close(config_file); - config_file = 0; -} -/* -void engine_writeconfig() -{ -}*/ - -static int perf_tick = 1; -static PERFORMACE_INFO *current = 0; - -void perf_init() -{ -} - -void perf_next() -{ - perf_tick++; - current = 0; -} - -void perf_start(PERFORMACE_INFO *info) -{ - if(info->tick != perf_tick) - { - info->parent = current; - info->first_child = 0; - info->next_child = 0; - - if(info->parent) - { - info->next_child = info->parent->first_child; - info->parent->first_child = info; - } - - info->tick = perf_tick; - info->biggest = 0; - info->total = 0; - } - - current = info; - current->start = time_get(); -} - -void perf_end() -{ - if(!current) - return; - - current->last_delta = time_get()-current->start; - current->total += current->last_delta; - - if(current->last_delta > current->biggest) - current->biggest = current->last_delta; - - current = current->parent; -} - -static void perf_dump_imp(PERFORMACE_INFO *info, int indent) -{ - char buf[512] = {0}; - int64 freq = time_freq(); - int i; - - for(i = 0; i < indent; i++) - buf[i] = ' '; - - str_format(&buf[indent], sizeof(buf)-indent, "%-20s %8.2f %8.2f", info->name, info->total*1000/(float)freq, info->biggest*1000/(float)freq); - dbg_msg("perf", "%s", buf); - - info = info->first_child; - while(info) - { - perf_dump_imp(info, indent+2); - info = info->next_child; - } -} - -void perf_dump(PERFORMACE_INFO *top) -{ - perf_dump_imp(top, 0); -} - -/* master server functions */ -typedef struct -{ - char hostname[128]; - NETADDR addr; - - HOSTLOOKUP lookup; -} MASTER_INFO; - -static MASTER_INFO master_servers[MAX_MASTERSERVERS] = {{{0}}}; -static int needs_update = -1; - -int mastersrv_refresh_addresses() -{ - int i; - - if(needs_update != -1) - return 0; - - dbg_msg("engine/mastersrv", "refreshing master server addresses"); - - /* add lookup jobs */ - for(i = 0; i < MAX_MASTERSERVERS; i++) - engine_hostlookup(&master_servers[i].lookup, master_servers[i].hostname); - - needs_update = 1; - return 0; -} - -void mastersrv_update() -{ - int i; - - /* check if we need to update */ - if(needs_update != 1) - return; - needs_update = 0; - - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - if(jobs_status(&master_servers[i].lookup.job) != JOBSTATUS_DONE) - needs_update = 1; - else - { - master_servers[i].addr = master_servers[i].lookup.addr; - master_servers[i].addr.port = 8300; - } - } - - if(!needs_update) - { - dbg_msg("engine/mastersrv", "saving addresses"); - mastersrv_save(); - } -} - -int mastersrv_refreshing() -{ - return needs_update; -} - -NETADDR mastersrv_get(int index) -{ - return master_servers[index].addr; -} - -const char *mastersrv_name(int index) -{ - return master_servers[index].hostname; -} - -void mastersrv_dump_servers() -{ - int i; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i, - master_servers[i].addr.ip[0], master_servers[i].addr.ip[1], - master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]); - } -} - -void mastersrv_default() -{ - int i; - mem_zero(master_servers, sizeof(master_servers)); - for(i = 0; i < MAX_MASTERSERVERS; i++) - sprintf(master_servers[i].hostname, "master%d.teeworlds.com", i+1); -} - -int mastersrv_load() -{ - LINEREADER lr; - IOHANDLE file; - int count = 0; - - /* try to open file */ - file = engine_openfile("masters.cfg", IOFLAG_READ); - if(!file) - return -1; - - linereader_init(&lr, file); - while(1) - { - MASTER_INFO info = {{0}}; - int ip[4]; - const char *line = linereader_get(&lr); - if(!line) - break; - - /* parse line */ - if(sscanf(line, "%s %d.%d.%d.%d", info.hostname, &ip[0], &ip[1], &ip[2], &ip[3]) == 5) - { - info.addr.ip[0] = (unsigned char)ip[0]; - info.addr.ip[1] = (unsigned char)ip[1]; - info.addr.ip[2] = (unsigned char)ip[2]; - info.addr.ip[3] = (unsigned char)ip[3]; - info.addr.port = 8300; - if(count != MAX_MASTERSERVERS) - { - master_servers[count] = info; - count++; - } - else - dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", line, MAX_MASTERSERVERS); - } - else - dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", line); - } - - io_close(file); - return 0; -} - -int mastersrv_save() -{ - IOHANDLE file; - int i; - - /* try to open file */ - file = engine_openfile("masters.cfg", IOFLAG_WRITE); - if(!file) - return -1; - - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - char buf[1024]; - str_format(buf, sizeof(buf), "%s %d.%d.%d.%d\n", master_servers[i].hostname, - master_servers[i].addr.ip[0], master_servers[i].addr.ip[1], - master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]); - - io_write(file, buf, strlen(buf)); - } - - io_close(file); - return 0; -} - - -int hostlookup_thread(void *user) -{ - HOSTLOOKUP *lookup = (HOSTLOOKUP *)user; - net_host_lookup(lookup->hostname, &lookup->addr, NETTYPE_IPV4); - return 0; -} - -void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname) -{ - str_copy(lookup->hostname, hostname, sizeof(lookup->hostname)); - jobs_add(&hostlookuppool, &lookup->job, hostlookup_thread, lookup); -} - -static int engine_find_datadir(char *argv0) -{ - /* 1) use provided data-dir override */ - if(datadir[0]) - { - if(fs_is_dir(datadir)) - return 0; - else - { - dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", datadir); - return -1; - } - } - - /* 2) use data-dir in PWD if present */ - if(fs_is_dir("data")) - { - strcpy(datadir, "data"); - return 0; - } - - /* 3) use compiled-in data-dir if present */ - if (fs_is_dir(DATA_DIR)) - { - strcpy(datadir, DATA_DIR); - return 0; - } - - /* 4) check for usable path in argv[0] */ - { - unsigned int pos = strrchr(argv0, '/') - argv0; - - if (pos < sizeof(datadir)) - { - char basedir[sizeof(datadir)]; - strncpy(basedir, argv0, pos); - basedir[pos] = '\0'; - str_format(datadir, sizeof(datadir), "%s/data", basedir); - - if (fs_is_dir(datadir)) - return 0; - } - } - -#if defined(CONF_FAMILY_UNIX) - /* 5) check for all default locations */ - { - const char *sdirs[] = { - "/usr/share/teeworlds", - "/usr/local/share/teeworlds" - "/opt/teeworlds" - }; - const int sdirs_count = sizeof(sdirs) / sizeof(sdirs[0]); - - int i; - for (i = 0; i < sdirs_count; i++) - { - if (fs_is_dir(sdirs[i])) - { - strcpy(datadir, sdirs[i]); - return 0; - } - } - } -#endif - - /* no data-dir found */ - dbg_msg("engine/datadir", "warning no data directory found"); - return -1; -} diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h deleted file mode 100644 index 94d49171..00000000 --- a/src/engine/e_engine.h +++ /dev/null @@ -1,51 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include "e_jobs.h" - -const char *engine_savepath(const char *filename, char *buffer, int max); -void engine_init(const char *appname); -void engine_parse_arguments(int argc, char **argv); - -int engine_config_write_start(); -void engine_config_write_line(const char *line); -void engine_config_write_stop(); - - -enum -{ - LISTDIRTYPE_SAVE=1, - LISTDIRTYPE_CURRENT=2, - LISTDIRTYPE_DATA=4, - LISTDIRTYPE_ALL = ~0 -}; - -void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user); -IOHANDLE engine_openfile(const char *filename, int flags); -void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags); - -int engine_stress(float probability); - -typedef struct HOSTLOOKUP -{ - JOB job; - char hostname[128]; - NETADDR addr; -} HOSTLOOKUP; - -void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname); - -enum -{ - MAX_MASTERSERVERS=4 -}; - -void mastersrv_default(); -int mastersrv_load(); -int mastersrv_save(); - -int mastersrv_refresh_addresses(); -void mastersrv_update(); -int mastersrv_refreshing(); -void mastersrv_dump_servers(); -NETADDR mastersrv_get(int index); -const char *mastersrv_name(int index); diff --git a/src/engine/e_huffman.cpp b/src/engine/e_huffman.cpp deleted file mode 100644 index 43914010..00000000 --- a/src/engine/e_huffman.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include <memory.h> /* memset */ -#include "e_huffman.h" - -typedef struct HUFFMAN_CONSTRUCT_NODE -{ - unsigned short node_id; - int frequency; -} HUFFMAN_CONSTRUCT_NODE; - -static void huffman_setbits_r(HUFFMAN_STATE *huff, HUFFMAN_NODE *node, int bits, int depth) -{ - if(node->leafs[1] != 0xffff) - huffman_setbits_r(huff, &huff->nodes[node->leafs[1]], bits|(1<<depth), depth+1); - if(node->leafs[0] != 0xffff) - huffman_setbits_r(huff, &huff->nodes[node->leafs[0]], bits, depth+1); - - if(node->num_bits) - { - node->bits = bits; - node->num_bits = depth; - } -} - -/* TODO: this should be something faster, but it's enough for now */ -void bubblesort(HUFFMAN_CONSTRUCT_NODE **list, int size) -{ - int i, changed = 1; - HUFFMAN_CONSTRUCT_NODE *temp; - - while(changed) - { - changed = 0; - for(i = 0; i < size-1; i++) - { - if(list[i]->frequency < list[i+1]->frequency) - { - temp = list[i]; - list[i] = list[i+1]; - list[i+1] = temp; - changed = 1; - } - } - } -} - -static void huffman_construct_tree(HUFFMAN_STATE *huff, const unsigned *frequencies) -{ - HUFFMAN_CONSTRUCT_NODE nodes_left_storage[HUFFMAN_MAX_SYMBOLS]; - HUFFMAN_CONSTRUCT_NODE *nodes_left[HUFFMAN_MAX_SYMBOLS]; - int num_nodes_left = HUFFMAN_MAX_SYMBOLS; - int i; - - /* add the symbols */ - for(i = 0; i < HUFFMAN_MAX_SYMBOLS; i++) - { - huff->nodes[i].num_bits = -1; - huff->nodes[i].symbol = i; - huff->nodes[i].leafs[0] = -1; - huff->nodes[i].leafs[1] = -1; - - if(i == HUFFMAN_EOF_SYMBOL) - nodes_left_storage[i].frequency = 1; - else - nodes_left_storage[i].frequency = frequencies[i]; - nodes_left_storage[i].node_id = i; - nodes_left[i] = &nodes_left_storage[i]; - - } - huff->num_nodes = HUFFMAN_MAX_SYMBOLS; - - /* construct the table */ - while(num_nodes_left > 1) - { - /* we can't rely on stdlib's qsort for this, it can generate different results on different implementations */ - bubblesort(nodes_left, num_nodes_left); - - huff->nodes[huff->num_nodes].num_bits = 0; - huff->nodes[huff->num_nodes].leafs[0] = nodes_left[num_nodes_left-1]->node_id; - huff->nodes[huff->num_nodes].leafs[1] = nodes_left[num_nodes_left-2]->node_id; - nodes_left[num_nodes_left-2]->node_id = huff->num_nodes; - nodes_left[num_nodes_left-2]->frequency = nodes_left[num_nodes_left-1]->frequency + nodes_left[num_nodes_left-2]->frequency; - - huff->num_nodes++; - num_nodes_left--; - } - - /* set start node */ - huff->start_node = &huff->nodes[huff->num_nodes-1]; - - /* build symbol bits */ - huffman_setbits_r(huff, huff->start_node, 0, 0); -} - -void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies) -{ - int i; - - /* make sure to cleanout every thing */ - memset(huff, 0, sizeof(HUFFMAN_STATE)); - - /* construct the tree */ - huffman_construct_tree(huff, frequencies); - - /* build decode LUT */ - for(i = 0; i < HUFFMAN_LUTSIZE; i++) - { - unsigned bits = i; - int k; - HUFFMAN_NODE *node = huff->start_node; - for(k = 0; k < HUFFMAN_LUTBITS; k++) - { - node = &huff->nodes[node->leafs[bits&1]]; - bits >>= 1; - - if(!node) - break; - - if(node->num_bits) - { - huff->decode_lut[i] = node; - break; - } - } - - if(k == HUFFMAN_LUTBITS) - huff->decode_lut[i] = node; - } - -} - -/*****************************************************************/ -int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size) -{ - /* this macro loads a symbol for a byte into bits and bitcount */ -#define HUFFMAN_MACRO_LOADSYMBOL(sym) \ - bits |= huff->nodes[sym].bits << bitcount; \ - bitcount += huff->nodes[sym].num_bits; - - /* this macro writes the symbol stored in bits and bitcount to the dst pointer */ -#define HUFFMAN_MACRO_WRITE() \ - while(bitcount >= 8) \ - { \ - *dst++ = (unsigned char)(bits&0xff); \ - if(dst == dst_end) \ - return -1; \ - bits >>= 8; \ - bitcount -= 8; \ - } - - /* setup buffer pointers */ - const unsigned char *src = (const unsigned char *)input; - const unsigned char *src_end = src + input_size; - unsigned char *dst = (unsigned char *)output; - unsigned char *dst_end = dst + output_size; - - /* symbol variables */ - unsigned bits = 0; - unsigned bitcount = 0; - - /* make sure that we have data that we want to compress */ - if(input_size) - { - /* {A} load the first symbol */ - int symbol = *src++; - - while(src != src_end) - { - /* {B} load the symbol */ - HUFFMAN_MACRO_LOADSYMBOL(symbol) - - /* {C} fetch next symbol, this is done here because it will reduce dependency in the code */ - symbol = *src++; - - /* {B} write the symbol loaded at */ - HUFFMAN_MACRO_WRITE() - } - - /* write the last symbol loaded from {C} or {A} in the case of only 1 byte input buffer */ - HUFFMAN_MACRO_LOADSYMBOL(symbol) - HUFFMAN_MACRO_WRITE() - } - - /* write EOF symbol */ - HUFFMAN_MACRO_LOADSYMBOL(HUFFMAN_EOF_SYMBOL) - HUFFMAN_MACRO_WRITE() - - /* write out the last bits */ - *dst++ = bits; - - /* return the size of the output */ - return (int)(dst - (const unsigned char *)output); - - /* remove macros */ -#undef HUFFMAN_MACRO_LOADSYMBOL -#undef HUFFMAN_MACRO_WRITE -} - -/*****************************************************************/ -int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size) -{ - /* setup buffer pointers */ - unsigned char *dst = (unsigned char *)output; - unsigned char *src = (unsigned char *)input; - unsigned char *dst_end = dst + output_size; - unsigned char *src_end = src + input_size; - - unsigned bits = 0; - unsigned bitcount = 0; - - HUFFMAN_NODE *eof = &huff->nodes[HUFFMAN_EOF_SYMBOL]; - HUFFMAN_NODE *node = 0; - - while(1) - { - /* {A} try to load a node now, this will reduce dependency at location {D} */ - node = 0; - if(bitcount >= HUFFMAN_LUTBITS) - node = huff->decode_lut[bits&HUFFMAN_LUTMASK]; - - /* {B} fill with new bits */ - while(bitcount < 24 && src != src_end) - { - bits |= (*src++) << bitcount; - bitcount += 8; - } - - /* {C} load symbol now if we didn't that earlier at location {A} */ - if(!node) - node = huff->decode_lut[bits&HUFFMAN_LUTMASK]; - - /* {D} check if we hit a symbol already */ - if(node->num_bits) - { - /* remove the bits for that symbol */ - bits >>= node->num_bits; - bitcount -= node->num_bits; - } - else - { - /* remove the bits that the lut checked up for us */ - bits >>= HUFFMAN_LUTBITS; - bitcount -= HUFFMAN_LUTBITS; - - /* walk the tree bit by bit */ - while(1) - { - /* traverse tree */ - node = &huff->nodes[node->leafs[bits&1]]; - - /* remove bit */ - bitcount--; - bits >>= 1; - - /* check if we hit a symbol */ - if(node->num_bits) - break; - - /* no more bits, decoding error */ - if(bitcount == 0) - return -1; - } - } - - /* check for eof */ - if(node == eof) - break; - - /* output character */ - if(dst == dst_end) - return -1; - *dst++ = node->symbol; - } - - /* return the size of the decompressed buffer */ - return (int)(dst - (const unsigned char *)output); -} diff --git a/src/engine/e_huffman.h b/src/engine/e_huffman.h deleted file mode 100644 index 635c74a1..00000000 --- a/src/engine/e_huffman.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef __HUFFMAN_HEADER__ -#define __HUFFMAN_HEADER__ - -enum -{ - HUFFMAN_EOF_SYMBOL = 256, - - HUFFMAN_MAX_SYMBOLS=HUFFMAN_EOF_SYMBOL+1, - HUFFMAN_MAX_NODES=HUFFMAN_MAX_SYMBOLS*2-1, - - HUFFMAN_LUTBITS = 10, - HUFFMAN_LUTSIZE = (1<<HUFFMAN_LUTBITS), - HUFFMAN_LUTMASK = (HUFFMAN_LUTSIZE-1) -}; - -typedef struct HUFFMAN_NODE -{ - /* symbol */ - unsigned bits; - unsigned num_bits; - - /* don't use pointers for this. shorts are smaller so we can fit more data into the cache */ - unsigned short leafs[2]; - - /* what the symbol represents */ - unsigned char symbol; -} HUFFMAN_NODE; - -typedef struct HUFFMAN_STATE -{ - HUFFMAN_NODE nodes[HUFFMAN_MAX_NODES]; - HUFFMAN_NODE *decode_lut[HUFFMAN_LUTSIZE]; - HUFFMAN_NODE *start_node; - int num_nodes; -} HUFFMAN_STATE; - -/* - Function: huffman_init - Inits the compressor/decompressor. - - Parameters: - huff - Pointer to the state to init - frequencies - A pointer to an array of 256 entries of the frequencies of the bytes - - Remarks: - - Does no allocation what so ever. - - You don't have to call any cleanup functions when you are done with it -*/ -void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies); - -/* - Function: huffman_compress - Compresses a buffer and outputs a compressed buffer. - - Parameters: - huff - Pointer to the huffman state - input - Buffer to compress - input_size - Size of the buffer to compress - output - Buffer to put the compressed data into - output_size - Size of the output buffer - - Returns: - Returns the size of the compressed data. Negative value on failure. -*/ -int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size); - -/* - Function: huffman_decompress - Decompresses a buffer - - Parameters: - huff - Pointer to the huffman state - input - Buffer to decompress - input_size - Size of the buffer to decompress - output - Buffer to put the uncompressed data into - output_size - Size of the output buffer - - Returns: - Returns the size of the uncompressed data. Negative value on failure. -*/ -int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size); - -#endif /* __HUFFMAN_HEADER__ */ diff --git a/src/engine/e_if_client.h b/src/engine/e_if_client.h deleted file mode 100644 index 86c6f5fc..00000000 --- a/src/engine/e_if_client.h +++ /dev/null @@ -1,579 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_CLIENT_H -#define ENGINE_IF_CLIENT_H - -/* - Title: Client Interface -*/ - -/* - Section: Constants -*/ - -enum -{ - /* Constants: Client States - CLIENTSTATE_OFFLINE - The client is offline. - CLIENTSTATE_CONNECTING - The client is trying to connect to a server. - CLIENTSTATE_LOADING - The client has connected to a server and is loading resources. - CLIENTSTATE_ONLINE - The client is connected to a server and running the game. - CLIENTSTATE_DEMOPLAYBACK - The client is playing a demo - CLIENTSTATE_QUITING - The client is quiting. - */ - CLIENTSTATE_OFFLINE=0, - CLIENTSTATE_CONNECTING, - CLIENTSTATE_LOADING, - CLIENTSTATE_ONLINE, - CLIENTSTATE_DEMOPLAYBACK, - CLIENTSTATE_QUITING, - - /* Constants: Image Formats - IMG_AUTO - Lets the engine choose the format. - IMG_RGB - 8-Bit uncompressed RGB - IMG_RGBA - 8-Bit uncompressed RGBA - IMG_ALPHA - 8-Bit uncompressed alpha - */ - IMG_AUTO=-1, - IMG_RGB=0, - IMG_RGBA=1, - IMG_ALPHA=2, - - /* Constants: Texture Loading Flags - TEXLOAD_NORESAMPLE - Prevents the texture from any resampling - */ - TEXLOAD_NORESAMPLE=1, - - /* Constants: Server Browser Sorting - BROWSESORT_NAME - Sort by name. - BROWSESORT_PING - Sort by ping. - BROWSESORT_MAP - Sort by map - BROWSESORT_GAMETYPE - Sort by game type. DM, TDM etc. - BROWSESORT_PROGRESSION - Sort by progression. - BROWSESORT_NUMPLAYERS - Sort after how many players there are on the server. - */ - BROWSESORT_NAME = 0, - BROWSESORT_PING, - BROWSESORT_MAP, - BROWSESORT_GAMETYPE, - BROWSESORT_PROGRESSION, - BROWSESORT_NUMPLAYERS, - - BROWSEQUICK_SERVERNAME=1, - BROWSEQUICK_PLAYERNAME=2, - BROWSEQUICK_MAPNAME=4, - - BROWSETYPE_INTERNET = 0, - BROWSETYPE_LAN = 1, - BROWSETYPE_FAVORITES = 2 -}; - -/* - Section: Structures -*/ - -/* - Structure: SERVER_INFO_PLAYER -*/ -typedef struct -{ - char name[48]; - int score; -} SERVER_INFO_PLAYER; - -/* - Structure: SERVER_INFO -*/ -typedef struct -{ - int sorted_index; - int server_index; - - NETADDR netaddr; - - int quicksearch_hit; - - int progression; - int max_players; - int num_players; - int flags; - int favorite; - int latency; /* in ms */ - char gametype[16]; - char name[64]; - char map[32]; - char version[32]; - char address[24]; - SERVER_INFO_PLAYER players[16]; -} SERVER_INFO; - -/* - Section: Functions -*/ - -/********************************************************************************** - Group: Time -**********************************************************************************/ -/* - Function: client_tick - Returns the tick of the current snapshot. -*/ -int client_tick(); - -/* - Function: client_prevtick - Returns the tick of the previous snapshot. -*/ -int client_prevtick(); - -/* - Function: client_intratick - Returns the current intratick. - - Remarks: - The intratick is how far gone the time is from the previous snapshot to the current. - 0.0 means that it on the previous snapshot. 0.5 means that it's halfway to the current, - and 1.0 means that it is on the current snapshot. It can go beyond 1.0 which means that - the client has started to extrapolate due to lack of data from the server. - - See Also: - <client_tick> -*/ -float client_intratick(); - -/* - Function: client_predtick - Returns the current predicted tick. -*/ -int client_predtick(); - -/* - Function: client_predintratick - Returns the current preticted intra tick. - - Remarks: - This is the same as <client_intratick> but for the current predicted tick and - previous predicted tick. - - See Also: - <client_intratick> -*/ -float client_predintratick(); - -/* - Function: client_ticktime - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -float client_ticktime(); - -/* - Function: client_tickspeed - Returns how many ticks per second the client is doing. - - Remarks: - This will be the same as the server tick speed. -*/ -int client_tickspeed(); - -/* - Function: client_frametime - Returns how long time the last frame took to process. -*/ -float client_frametime(); - -/* - Function: client_localtime - Returns the clients local time. - - Remarks: - The local time is set to 0 when the client starts and when - it connects to a server. Can be used for client side effects. -*/ -float client_localtime(); - -/********************************************************************************** - Group: Server Browser -**********************************************************************************/ - -/* - Function: client_serverbrowse_refresh - Issues a refresh of the server browser. - - Arguments: - type - What type of servers to browse, internet, lan or favorites. - - Remarks: - This will cause a broadcast on the local network if the lan argument is set. - Otherwise it call ask all the master servers for their servers lists. -*/ -void client_serverbrowse_refresh(int type); - -/* - Function: client_serverbrowse_sorted_get - Returns server info from the sorted list. - - Arguments: - index - Zero based index into the sorted list. - - See Also: - <client_serverbrowse_sorted_num> -*/ -SERVER_INFO *client_serverbrowse_sorted_get(int index); - -/* - Function: client_serverbrowse_sorted_num - Returns how many servers there are in the sorted list. - - See Also: - <client_serverbrowse_sorted_get> -*/ -int client_serverbrowse_sorted_num(); - -/* - Function: client_serverbrowse_get - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -SERVER_INFO *client_serverbrowse_get(int index); - -/* - Function: client_serverbrowse_num - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int client_serverbrowse_num(); - -/* - Function: client_serverbrowse_num_requests - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int client_serverbrowse_num_requests(); - -/* - Function: client_serverbrowse_update - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void client_serverbrowse_update(); - -/* - Function: client_serverbrowse_lan - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int client_serverbrowse_lan(); - -/* - Function: client_serverbrowse_isfavorite - Asks the server browser is a netaddr is in the favorite list - - Arguments: - addr - Address of the server to ask about. - - Returns: - Returns zero if it's not in the list, non-zero if it is. -*/ -int client_serverbrowse_isfavorite(NETADDR addr); - -/* - Function: client_serverbrowse_addfavorite - Adds a server to the favorite list - - Arguments: - addr - Address of the favorite server. -*/ -void client_serverbrowse_addfavorite(NETADDR addr); - -/* - Function: client_serverbrowse_removefavorite - Removes a server to the favorite list - - Arguments: - addr - Address of the favorite server. -*/ -void client_serverbrowse_removefavorite(NETADDR addr); - -/********************************************************************************** - Group: Actions -**********************************************************************************/ - - -/* - Function: client_connect - Connects to a server at the specified address. - - Arguments: - address - Address of the server to connect to. - - See Also: - <client_disconnect> -*/ -void client_connect(const char *address); - -/* - Function: client_disconnect - Disconnects from the current server. - - Remarks: - Does nothing if not connected to a server. - - See Also: - <client_connect, client_quit> -*/ -void client_disconnect(); - -/* - Function: client_quit - Tells to client to shutdown. - - See Also: - <client_disconnect> -*/ -void client_quit(); - -void client_entergame(); - -/* - Function: client_rcon - Sends a command to the server to execute on it's console. - - Arguments: - cmd - The command to send. - - Remarks: - The client must have the correct rcon password to connect. - - See Also: - <client_rcon_auth, client_rcon_authed> -*/ -void client_rcon(const char *cmd); - -/* - Function: client_rcon_auth - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <client_rcon, client_rcon_authed> -*/ -void client_rcon_auth(const char *name, const char *password); - -/* - Function: client_rcon_authed - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <client_rcon, client_rcon_auth> -*/ -int client_rcon_authed(); - -/********************************************************************************** - Group: Other -**********************************************************************************/ -/* - Function: client_latestversion - Returns 0 if there's no version difference - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *client_latestversion(); - -/* - Function: client_get_input - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int *client_get_input(int tick); - -/* - Function: client_direct_input - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void client_direct_input(int *input, int size); - -/* - Function: client_error_string - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *client_error_string(); - -/* - Function: client_connection_problems - Returns 1 if the client is connection problems. - - Remarks: - Connections problems usually means that the client havn't recvived any data - from the server in a while. -*/ -int client_connection_problems(); - -/* - Function: client_state - Returns the state of the client. - - See Also: - <Client States> -*/ -int client_state(); - -/* - Function: client_mapdownload_amount - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int client_mapdownload_amount(); - -/* - Function: client_mapdownload_totalsize - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int client_mapdownload_totalsize(); - -/* - Function: client_save_line - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void client_save_line(const char *line); - - - - - - -enum -{ - BROWSESET_MASTER_ADD=1, - BROWSESET_FAV_ADD, - BROWSESET_TOKEN, - BROWSESET_OLD_INTERNET, - BROWSESET_OLD_LAN -}; - -void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info); - -int client_serverbrowse_refreshingmasters(); - - -typedef struct DEMOPLAYBACK_INFO -{ - int first_tick; - int last_tick; - int current_tick; - int paused; - float speed; -} DEMOPLAYBACK_INFO; - -const char *client_demoplayer_play(const char *filename); -const DEMOPLAYBACK_INFO *client_demoplayer_getinfo(); -void client_demoplayer_setpos(float percent); -void client_demoplayer_setpause(int paused); -void client_demoplayer_setspeed(float speed); -const char *client_user_directory(); -void client_serverinfo(SERVER_INFO *serverinfo); -void client_serverinfo_request(); -void client_serverbrowse_request(NETADDR *addr); -#endif diff --git a/src/engine/e_if_gfx.h b/src/engine/e_if_gfx.h deleted file mode 100644 index 3d048e85..00000000 --- a/src/engine/e_if_gfx.h +++ /dev/null @@ -1,674 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_GFX_H -#define ENGINE_IF_GFX_H - -/* - Title: Graphics -*/ - -/* - Section: Structures -*/ - -/* - Structure: FONT -*/ -struct FONT; - -/* - Structure: IMAGE_INFO -*/ -typedef struct -{ - /* Variable: width - Contains the width of the image */ - int width; - - /* Variable: height - Contains the height of the image */ - int height; - - /* Variable: format - Contains the format of the image. See <Image Formats> for more information. */ - int format; - - /* Variable: data - Pointer to the image data. */ - void *data; -} IMAGE_INFO; - -/* - Structure: VIDEO_MODE -*/ -typedef struct -{ - int width, height; - int red, green, blue; -} VIDEO_MODE; - -/* - Section: Functions -*/ - -/* - Group: Quads -*/ - -/* - Function: gfx_quads_begin - Begins a quad drawing session. - - Remarks: - This functions resets the rotation, color and subset. - End the session by using <gfx_quads_end>. - You can't change texture or blending mode during a session. - - See Also: - <gfx_quads_end> -*/ -void gfx_quads_begin(); - -/* - Function: gfx_quads_end - Ends a quad session. - - See Also: - <gfx_quads_begin> -*/ -void gfx_quads_end(); - -/* - Function: gfx_quads_setrotation - Sets the rotation to use when drawing a quad. - - Arguments: - angle - Angle in radians. - - Remarks: - The angle is reset when <gfx_quads_begin> is called. -*/ -void gfx_quads_setrotation(float angle); - -/* - Function: gfx_quads_setsubset - Sets the uv coordinates to use. - - Arguments: - tl_u - Top-left U value. - tl_v - Top-left V value. - br_u - Bottom-right U value. - br_v - Bottom-right V value. - - Remarks: - O,0 is top-left of the texture and 1,1 is bottom-right. - The color is reset when <gfx_quads_begin> is called. -*/ -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v); - -/* - Function: gfx_quads_setsubset_free - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_quads_setsubset_free( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3); - -/* - Function: gfx_quads_drawTL - Draws a quad by specifying the top-left point. - - Arguments: - x - X coordinate of the top-left corner. - y - Y coordinate of the top-left corner. - width - Width of the quad. - height - Height of the quad. - - Remarks: - Rotation still occurs from the center of the quad. - You must call <gfx_quads_begin> before calling this function. - - See Also: - <gfx_quads_draw, gfx_quads_draw_freeform> -*/ -void gfx_quads_drawTL(float x, float y, float width, float height); - -/* - Function: gfx_quads_draw - Draws a quad by specifying the center point. - - Arguments: - x - X coordinate of the center. - y - Y coordinate of the center. - width - Width of the quad. - height - Height of the quad. - - Remarks: - You must call <gfx_quads_begin> before calling this function. - - See Also: - <gfx_quads_drawTL, gfx_quads_draw_freeform> -*/ -void gfx_quads_draw(float x, float y, float w, float h); - -/* - Function: gfx_quads_draw_freeform - Draws a quad by specifying the corner points. - - Arguments: - x0, y0 - Coordinates of the upper left corner. - x1, y1 - Coordinates of the upper right corner. - x2, y2 - Coordinates of the lower left corner. // TODO: DOUBLE CHECK!!! - x3, y3 - Coordinates of the lower right corner. // TODO: DOUBLE CHECK!!! - - See Also: - <gfx_quads_draw, gfx_quads_drawTL> -*/ -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3); - -/* - Function: gfx_quads_text - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_quads_text(float x, float y, float size, float r, float g, float b, float a, const char *text); - -/* - Group: Lines -*/ - -/* - Function: gfx_lines_begin - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_lines_begin(); - -/* - Function: gfx_lines_draw - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_lines_draw(float x0, float y0, float x1, float y1); - -/* - Function: gfx_minimize - Minimizes the window. - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_minimize(); - -/* - Function: gfx_minimize - Maximizes the window. - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_maximize(); - -/* - Function: gfx_lines_end - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_lines_end(); - -/* - Group: Text -*/ - - -/* - Function: gfx_text - TODO - - Arguments: - arg1 - desc - - Returns: - returns the number of lines written - See Also: - <other_func> -*/ -void gfx_text(void *font, float x, float y, float size, const char *text, int max_width); - -/* - Function: gfx_text_width - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -float gfx_text_width(void *font, float size, const char *text, int length); - -/* - Function: gfx_text_color - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_text_color(float r, float g, float b, float a); - -/* - Function: gfx_text_set_default_font - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_text_set_default_font(struct FONT *font); - -/* - Group: Other -*/ - -/* - Function: gfx_get_video_modes - Fetches a list of all the available video modes. - - Arguments: - list - An array to recive the modes. Must be able to contain maxcount number of modes. - maxcount - The maximum number of modes to fetch. - - Returns: - The number of video modes that was fetched. -*/ -int gfx_get_video_modes(VIDEO_MODE *list, int maxcount); - -/* image loaders */ - -/* - Function: gfx_load_png - Loads a PNG image from disk. - - Arguments: - img - Pointer to an structure to be filled out with the image information. - filename - Filename of the image to load. - - Returns: - Returns non-zero on success and zero on error. - - Remarks: - The caller are responsible for cleaning up the allocated memory in the IMAGE_INFO structure. - - See Also: - <gfx_load_texture> -*/ -int gfx_load_png(IMAGE_INFO *img, const char *filename); - -/* textures */ -/* - Function: gfx_load_texture - Loads a texture from a file. TGA and PNG supported. - - Arguments: - filename - Null terminated string to the file to load. - store_format - What format to store on gfx card as. - flags - controls how the texture is uploaded - - Returns: - An ID to the texture. -1 on failure. - - See Also: - <gfx_unload_texture, gfx_load_png> -*/ -int gfx_load_texture(const char *filename, int store_format, int flags); - -/* - Function: gfx_load_texture_raw - Loads a texture from memory. - - Arguments: - w - Width of the texture. - h - Height of the texture. - data - Pointer to the pixel data. - format - Format of the pixel data. - store_format - The format to store the texture on the graphics card. - flags - controls how the texture is uploaded - - Returns: - An ID to the texture. -1 on failure. - - Remarks: - The pixel data should be in RGBA format with 8 bit per component. - So the total size of the data should be w*h*4. - - See Also: - <gfx_unload_texture> -*/ -int gfx_load_texture_raw(int w, int h, int format, const void *data, int store_format, int flags); - -/* - Function: gfx_texture_set - Sets the active texture. - - Arguments: - id - ID to the texture to set. -*/ -void gfx_texture_set(int id); - -/* - Function: gfx_unload_texture - Unloads a texture. - - Arguments: - id - ID to the texture to unload. - - See Also: - <gfx_load_texture_tga>, <gfx_load_texture_raw> - - Remarks: - NOT IMPLEMENTED -*/ -int gfx_unload_texture(int id); - -/* - Function: gfx_clear - Clears the screen with the specified color. - - Arguments: - r - Red component. - g - Green component. - b - Red component. - - Remarks: - The value of the components are given in 0.0 - 1.0 ranges. -*/ -void gfx_clear(float r, float g, float b); - -/* - Function: gfx_screenaspect - Returns the aspect ratio between width and height. - - See Also: - <gfx_screenwidth>, <gfx_screenheight> -*/ -float gfx_screenaspect(); - -/* - Function: gfx_screenwidth - Returns the screen width. - - See Also: - <gfx_screenheight> -*/ -int gfx_screenwidth(); - -/* - Function: gfx_screenheight - Returns the screen height. - - See Also: - <gfx_screenwidth> -*/ -int gfx_screenheight(); - -/* - Function: gfx_mapscreen - Specifies the coordinate system for the screen. - - Arguments: - tl_x - Top-left X - tl_y - Top-left Y - br_x - Bottom-right X - br_y - Bottom-right y -*/ -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y); - -/* - Function: gfx_blend_normal - Set the active blending mode to normal (src, 1-src). - - Remarks: - This must be used before calling <gfx_quads_begin>. - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). - - See Also: - <gfx_blend_additive,gfx_blend_none> -*/ -void gfx_blend_normal(); - -/* - Function: gfx_blend_additive - Set the active blending mode to additive (src, one). - - Remarks: - This must be used before calling <gfx_quads_begin>. - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE). - - See Also: - <gfx_blend_normal,gfx_blend_none> -*/ -void gfx_blend_additive(); - -/* - Function: gfx_blend_none - Disables blending - - Remarks: - This must be used before calling <gfx_quads_begin>. - - See Also: - <gfx_blend_normal,gfx_blend_additive> -*/ -void gfx_blend_none(); - - -/* - Function: gfx_setcolorvertex - Sets the color of a vertex. - - Arguments: - i - Index to the vertex. - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when <gfx_quads_begin> is called. -*/ -void gfx_setcolorvertex(int i, float r, float g, float b, float a); - -/* - Function: gfx_setcolor - Sets the color of all the vertices. - - Arguments: - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when <gfx_quads_begin> is called. -*/ -void gfx_setcolor(float r, float g, float b, float a); - -/* - Function: gfx_getscreen - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y); - -/* - Function: gfx_memory_usage - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int gfx_memory_usage(); - -/* - Function: gfx_screenshot - TODO - - Arguments: - filename - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_screenshot(); - -/* - Function: gfx_screenshot_direct - TODO - - Arguments: - filename - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_screenshot_direct(const char *filename); - -/* - Function: gfx_clip_enable - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_clip_enable(int x, int y, int w, int h); - -/* - Function: gfx_clip_disable - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void gfx_clip_disable(); - - -enum -{ - TEXTFLAG_RENDER=1, - TEXTFLAG_ALLOW_NEWLINE=2, - TEXTFLAG_STOP_AT_END=4 -}; - -typedef struct -{ - int flags; - int line_count; - int charcount; - - float start_x; - float start_y; - float line_width; - float x, y; - - struct FONT *font; - float font_size; -} TEXT_CURSOR; - -void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags); -void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length); - - -struct FONT *gfx_font_load(const char *filename); -void gfx_font_destroy(struct FONT *font); - -#endif diff --git a/src/engine/e_if_inp.h b/src/engine/e_if_inp.h deleted file mode 100644 index 919ac0eb..00000000 --- a/src/engine/e_if_inp.h +++ /dev/null @@ -1,244 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_INP_H -#define ENGINE_IF_INP_H - -/* - Section: Input -*/ - - -/* - Structure: INPUT_EVENT -*/ -enum -{ - INPFLAG_PRESS=1, - INPFLAG_RELEASE=2, - INPFLAG_REPEAT=4 -}; - -typedef struct -{ - int flags; - int unicode; - int key; -} INPUT_EVENT; - -/* - Function: inp_mouse_relative - Fetches the mouse movements. - - Arguments: - x - Pointer to the variable that should get the X movement. - y - Pointer to the variable that should get the Y movement. -*/ -void inp_mouse_relative(int *x, int *y); - -/* - Function: inp_mouse_scroll - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_mouse_scroll(); - -/* - Function: inp_key_pressed - Checks if a key is pressed. - - Arguments: - key - Index to the key to check - - Returns: - Returns 1 if the button is pressed, otherwise 0. - - Remarks: - Check keys.h for the keys. -*/ -int inp_key_pressed(int key); - - -/* input */ -/* - Function: inp_key_was_pressed - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_key_was_pressed(int key); - -/* - Function: inp_key_down - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_key_down(int key); - - -/* - Function: inp_num_events - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_num_events(); - -/* - Function: inp_get_event - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -INPUT_EVENT inp_get_event(int index); - -/* - Function: inp_clear_events - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void inp_clear_events(); - -/* - Function: inp_mouse_doubleclick - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_mouse_doubleclick(); - -/* - Function: inp_key_presses - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_key_presses(int key); - -/* - Function: inp_key_releases - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_key_releases(int key); - -/* - Function: inp_key_state - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_key_state(int key); - -/* - Function: inp_key_name - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *inp_key_name(int k); - -/* - Function: inp_key_code - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int inp_key_code(const char *key_name); - - - -/* - Function: inp_clear_key_states - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void inp_clear_key_states(); - -void inp_update(); -void inp_init(); -void inp_mouse_mode_absolute(); -void inp_mouse_mode_relative(); - -#endif diff --git a/src/engine/e_if_modc.h b/src/engine/e_if_modc.h deleted file mode 100644 index 8839d5f1..00000000 --- a/src/engine/e_if_modc.h +++ /dev/null @@ -1,145 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_MODC_H -#define ENGINE_IF_MODC_H - -/********************************************************************************** - Section: Client Hooks -*********************************************************************************/ -/* - Function: modc_console_init - TODO -*/ -void modc_console_init(); - -/* - Function: modc_rcon_line - TODO -*/ -void modc_rcon_line(const char *line); - -/* - Function: modc_save_config - TODO -*/ -void modc_save_config(); - -/* - Function: modc_init - Called when the client starts. - - Remarks: - The game should load resources that are used during the entire - time of the game. No map is loaded. -*/ -void modc_init(); - -/* - Function: modc_newsnapshot - Called when the client progressed to a new snapshot. - - Remarks: - The client can check for items in the snapshot and perform one time - events like playing sounds, spawning client side effects etc. -*/ -void modc_newsnapshot(); - -/* - Function: modc_entergame - Called when the client has successfully connect to a server and - loaded a map. - - Remarks: - The client can check for items in the map and load them. -*/ -void modc_entergame(); - -/* - Function: modc_shutdown - Called when the client closes down. -*/ -void modc_shutdown(); - -/* - Function: modc_render - Called every frame to let the game render it self. -*/ -void modc_render(); - -/* - Function: modc_statechange - Called every time client changes state. -*/ -void modc_statechange(int new_state, int old_state); - -/* undocumented callbacks */ -/* - Function: modc_connected - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void modc_connected(); - -/* - Function: modc_message - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void modc_message(int msg); - -/* - Function: modc_predict - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void modc_predict(); - -/* - Function: modc_snap_input - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int modc_snap_input(int *data); - -/* - Function: modc_net_version - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *modc_net_version(); - -#endif diff --git a/src/engine/e_if_mods.h b/src/engine/e_if_mods.h deleted file mode 100644 index 08d0ec37..00000000 --- a/src/engine/e_if_mods.h +++ /dev/null @@ -1,168 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_MOD_H -#define ENGINE_IF_MOD_H - -/********************************************************************************** - Section: Server Hooks -**********************************************************************************/ -/* - Function: mods_console_init - TODO -*/ -void mods_console_init(); - -/* - Function: mods_init - Called when the server is started. - - Remarks: - It's called after the map is loaded so all map items are available. -*/ -void mods_init(); - -/* - Function: mods_shutdown - Called when the server quits. - - Remarks: - Should be used to clean up all resources used. -*/ -void mods_shutdown(); - -/* - Function: mods_client_enter - Called when a client has joined the game. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - It's called when the client is finished loading and should enter gameplay. -*/ -void mods_client_enter(int cid); - -/* - Function: mods_client_drop - Called when a client drops from the server. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS -*/ -void mods_client_drop(int cid); - -/* - Function: mods_client_direct_input - Called when the server recives new input from a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - input - Pointer to the input data. - size - Size of the data. (NOT IMPLEMENTED YET) -*/ -void mods_client_direct_input(int cid, void *input); - -/* - Function: mods_client_predicted_input - Called when the server applys the predicted input on the client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - input - Pointer to the input data. - size - Size of the data. (NOT IMPLEMENTED YET) -*/ -void mods_client_predicted_input(int cid, void *input); - - -/* - Function: mods_tick - Called with a regular interval to progress the gameplay. - - Remarks: - The SERVER_TICK_SPEED tells the number of ticks per second. -*/ -void mods_tick(); - -/* - Function: mods_presnap - Called before the server starts to construct snapshots for the clients. -*/ -void mods_presnap(); - -/* - Function: mods_snap - Called to create the snapshot for a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - The game should make a series of calls to <snap_new_item> to construct - the snapshot for the client. -*/ -void mods_snap(int cid); - -/* - Function: mods_postsnap - Called after the server is done sending the snapshots. -*/ -void mods_postsnap(); - - -/* - Function: mods_connected - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void mods_connected(int client_id); - - -/* - Function: mods_net_version - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *mods_net_version(); - -/* - Function: mods_version - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *mods_version(); - -/* - Function: mods_message - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void mods_message(int msg, int client_id); - -#endif diff --git a/src/engine/e_if_msg.h b/src/engine/e_if_msg.h deleted file mode 100644 index d03a64a6..00000000 --- a/src/engine/e_if_msg.h +++ /dev/null @@ -1,151 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_MSG_H -#define ENGINE_IF_MSG_H - -/* - Section: Messaging -*/ - -void msg_pack_start_system(int msg, int flags); - -/* - Function: msg_pack_start - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void msg_pack_start(int msg, int flags); - -/* - Function: msg_pack_int - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void msg_pack_int(int i); - -/* - Function: msg_pack_string - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void msg_pack_string(const char *p, int limit); - -/* - Function: msg_pack_raw - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void msg_pack_raw(const void *data, int size); - -/* - Function: msg_pack_end - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void msg_pack_end(); - -typedef struct -{ - int msg; - int flags; - const unsigned char *data; - int size; -} MSG_INFO; - -const MSG_INFO *msg_get_info(); - -/* message unpacking */ -int msg_unpack_start(const void *data, int data_size, int *is_system); - -/* - Function: msg_unpack_int - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int msg_unpack_int(); - -/* - Function: msg_unpack_string - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *msg_unpack_string(); - -/* - Function: msg_unpack_raw - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const unsigned char *msg_unpack_raw(int size); - -/* - Function: msg_unpack_error - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int msg_unpack_error(); - - -#endif diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h deleted file mode 100644 index 3fbbd81e..00000000 --- a/src/engine/e_if_other.h +++ /dev/null @@ -1,385 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_OTHER_H -#define ENGINE_IF_OTHER_H - -/* - Title: Engine Interface -*/ - -#include <base/system.h> -#include "e_keys.h" - -enum -{ - SERVER_TICK_SPEED=50, - MAX_CLIENTS=16, - - SNAP_CURRENT=0, - SNAP_PREV=1, - - MASK_NONE=0, - MASK_SET, - MASK_ZERO, - - SNDFLAG_LOOP=1, - SNDFLAG_POS=2, - SNDFLAG_ALL=3, - - MAX_NAME_LENGTH=32 -}; - -enum -{ - SRVFLAG_PASSWORD = 0x1, -}; - -/* - Structure: SNAP_ITEM -*/ -typedef struct -{ - int type; - int id; - int datasize; -} SNAP_ITEM; - -/* - Structure: CLIENT_INFO -*/ -typedef struct -{ - const char *name; - int latency; -} CLIENT_INFO; - -typedef struct PERFORMACE_INFO_t -{ - const char *name; - struct PERFORMACE_INFO_t *parent; - struct PERFORMACE_INFO_t *first_child; - struct PERFORMACE_INFO_t *next_child; - int tick; - int64 start; - int64 total; - int64 biggest; - int64 last_delta; -} PERFORMACE_INFO; - -void perf_init(); -void perf_next(); -void perf_start(PERFORMACE_INFO *info); -void perf_end(); -void perf_dump(PERFORMACE_INFO *top); - -int gfx_init(); -void gfx_shutdown(); -void gfx_swap(); - -int gfx_window_active(); -int gfx_window_open(); - -void gfx_set_vsync(int val); - -int snd_init(); -int snd_shutdown(); -int snd_update(); - -int map_load(const char *mapname); -void map_unload(); - -void map_set(void *m); - -/* -#include "e_if_client.h" -#include "e_if_server.h" -#include "e_if_snd.h" -#include "e_if_gfx.h" -#include "e_if_inp.h" -#include "e_if_msg.h" -#include "e_if_mod.h"*/ - -/* - Section: Map -*/ - -/* - Function: map_is_loaded - Checks if a map is loaded. - - Returns: - Returns 1 if the button is pressed, otherwise 0. -*/ -int map_is_loaded(); - -/* - Function: map_num_items - Checks the number of items in the loaded map. - - Returns: - Returns the number of items. 0 if no map is loaded. -*/ -int map_num_items(); - -/* - Function: map_find_item - Searches the map for an item. - - Arguments: - type - Item type. - id - Item ID. - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_find_item(int type, int id); - -/* - Function: map_get_item - Gets an item from the loaded map from index. - - Arguments: - index - Item index. - type - Pointer that recives the item type (can be NULL). - id - Pointer that recives the item id (can be NULL). - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_get_item(int index, int *type, int *id); - -/* - Function: map_get_type - Gets the index range of an item type. - - Arguments: - type - Item type to search for. - start - Pointer that recives the starting index. - num - Pointer that recives the number of items. - - Returns: - If the item type is not in the map, start and num will be set to 0. -*/ -void map_get_type(int type, int *start, int *num); - -/* - Function: map_get_data - Fetches a pointer to a raw data chunk in the map. - - Arguments: - index - Index to the data to fetch. - - Returns: - A pointer to the raw data, otherwise 0. -*/ -void *map_get_data(int index); - -/* - Function: map_get_data_swapped - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void *map_get_data_swapped(int index); - -/* - Section: Network (Server) -*/ -/* - Function: snap_new_item - Creates a new item that should be sent. - - Arguments: - type - Type of the item. - id - ID of the item. - size - Size of the item. - - Returns: - A pointer to the item data, otherwise 0. - - Remarks: - The item data should only consist pf 4 byte integers as - they are subject to byte swapping. This means that the size - argument should be dividable by 4. -*/ -void *snap_new_item(int type, int id, int size); - -/* - Section:Section: Network (Client) -*/ -/* - Function: snap_num_items - Check the number of items in a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - - Returns: - The number of items in the snapshot. -*/ -int snap_num_items(int snapid); - -/* - Function: snap_get_item - Gets an item from a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - index - Index of the item. - item - Pointer that recives the item info. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -void *snap_get_item(int snapid, int index, SNAP_ITEM *item); - -/* - Function: snap_find_item - Searches a snapshot for an item. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - type - Type of the item. - id - ID of the item. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -void *snap_find_item(int snapid, int type, int id); - -/* - Function: snap_invalidate_item - Marks an item as invalid byt setting type and id to 0xffffffff. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - index - Index of the item. -*/ -void snap_invalidate_item(int snapid, int index); - -/* - Function: snap_input - Sets the input data to send to the server. - - Arguments: - data - Pointer to the data. - size - Size of the data. - - Remarks: - The data should only consist of 4 bytes integer as they are - subject to byte swapping. -*/ -void snap_input(void *data, int size); - -/* - Function: snap_set_staticsize - Tells the engine how big a specific item always will be. This - helps the engine to compress snapshots better. - - Arguments: - type - Item type - size - Size of the data. - - Remarks: - Size must be in a multiple of 4. -*/ -void snap_set_staticsize(int type, int size); - -/* message packing */ -enum -{ - MSGFLAG_VITAL=1, - MSGFLAG_FLUSH=2, - MSGFLAG_NORECORD=4, - MSGFLAG_RECORD=8, - MSGFLAG_NOSEND=16 -}; - -/* message sending */ -/* - Function: server_send_msg - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int server_send_msg(int client_id); /* client_id == -1 == broadcast */ - -/* - Function: client_send_msg - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int client_send_msg(); -/* undocumented graphics stuff */ - -/* server snap id */ -/* - Function: snap_new_id - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int snap_new_id(); - -/* - Function: snap_free_id - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void snap_free_id(int id); - -/* other */ -/* - Function: map_unload_data - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void map_unload_data(int index); - -#endif diff --git a/src/engine/e_if_server.h b/src/engine/e_if_server.h deleted file mode 100644 index 1acd184c..00000000 --- a/src/engine/e_if_server.h +++ /dev/null @@ -1,140 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_SERVER_H -#define ENGINE_IF_SERVER_H - -/* - Section: Server Interface -*/ - -/* server */ -/* - Function: server_getclientinfo - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int server_getclientinfo(int client_id, CLIENT_INFO *info); - -/* - Function: server_clientname - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -const char *server_clientname(int client_id); - -/* grabs the latest input for the client. not withholding anything */ - -/* - Function: server_latestinput - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int *server_latestinput(int client_id, int *size); - -/* - Function: server_setclientname - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void server_setclientname(int client_id, const char *name); - -/* - Function: server_setclientscore - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void server_setclientscore(int client_id, int score); - -/* - Function: server_setbrowseinfo - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void server_setbrowseinfo(const char *game_type, int progression); - -/* - Function: server_kick - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -void server_kick(int client_id, const char *reason); - -/* - Function: server_tick - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int server_tick(); - -/* - Function: server_tickspeed - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> -*/ -int server_tickspeed(); - -int server_ban_add(NETADDR addr, int seconds); -int server_ban_remove(NETADDR addr); -#endif diff --git a/src/engine/e_if_snd.h b/src/engine/e_if_snd.h deleted file mode 100644 index 48376bad..00000000 --- a/src/engine/e_if_snd.h +++ /dev/null @@ -1,91 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_IF_SND_H -#define ENGINE_IF_SND_H - -/* - Section: Sound -*/ - -/* - Function: snd_set_channel - Sets the parameters for a sound channel. - - Arguments: - cid - Channel ID - vol - Volume for the channel. 0.0 to 1.0. - pan - Panning for the channel. -1.0 is all left. 0.0 is equal distribution. 1.0 is all right. -*/ -void snd_set_channel(int cid, float vol, float pan); - -/* - Function: snd_load_wv - Loads a wavpack compressed sound. - - Arguments: - filename - Filename of the file to load - - Returns: - The id of the loaded sound. -1 on failure. -*/ -int snd_load_wv(const char *filename); - -/* - Function: snd_play_at - Plays a sound at a specified postition. - - Arguments: - cid - Channel id of the channel to use. - sid - Sound id of the sound to play. - flags - TODO - x - TODO - y - TODO - - Returns: - An id to the voice. -1 on failure. - - See Also: - <snd_play, snd_stop> -*/ -int snd_play_at(int cid, int sid, int flags, float x, float y); - -/* - Function: snd_play - Plays a sound. - - Arguments: - Arguments: - cid - Channel id of the channel to use. - sid - Sound id of the sound to play. - flags - TODO - - Returns: - An id to the voice. -1 on failure. - - See Also: - <snd_play_at, snd_stop> -*/ -int snd_play(int cid, int sid, int flags); - -/* - Function: snd_stop - Stops a currenly playing sound. - - Arguments: - id - The ID of the voice to stop. - - See Also: - <snd_play, snd_play_at> -*/ -void snd_stop(int id); - -/* - Function: snd_set_listener_pos - Sets the listener posititon. - - Arguments: - x - TODO - y - TODO -*/ -void snd_set_listener_pos(float x, float y); - -#endif diff --git a/src/engine/e_jobs.cpp b/src/engine/e_jobs.cpp deleted file mode 100644 index e87b395a..00000000 --- a/src/engine/e_jobs.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> -#include "e_jobs.h" - -void worker_thread(void *user) -{ - JOBPOOL *pool = (JOBPOOL *)user; - - while(1) - { - JOB *job = 0; - - /* fetch job from queue */ - lock_wait(pool->lock); - if(pool->first_job) - { - job = pool->first_job; - pool->first_job = pool->first_job->next; - if(pool->first_job) - pool->first_job->prev = 0; - else - pool->last_job = 0; - } - lock_release(pool->lock); - - /* do the job if we have one */ - if(job) - { - job->status = JOBSTATUS_RUNNING; - job->result = job->func(job->func_data); - job->status = JOBSTATUS_DONE; - } - else - thread_sleep(10); - } - -} - -int jobs_initpool(JOBPOOL *pool, int num_threads) -{ - int i; - - /* empty the pool */ - mem_zero(pool, sizeof(JOBPOOL)); - pool->lock = lock_create(); - - /* start threads */ - for(i = 0; i < num_threads; i++) - thread_create(worker_thread, pool); - return 0; -} - -int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data) -{ - mem_zero(job, sizeof(JOB)); - job->func = func; - job->func_data = data; - - lock_wait(pool->lock); - - /* add job to queue */ - job->prev = pool->last_job; - if(pool->last_job) - pool->last_job->next = job; - pool->last_job = job; - if(!pool->first_job) - pool->first_job = job; - - lock_release(pool->lock); - return 0; -} - -int jobs_status(JOB *job) -{ - return job->status; -} diff --git a/src/engine/e_jobs.h b/src/engine/e_jobs.h deleted file mode 100644 index 2b04a1e4..00000000 --- a/src/engine/e_jobs.h +++ /dev/null @@ -1,33 +0,0 @@ - -typedef int (*JOBFUNC)(void *data); - -typedef struct JOB -{ - struct JOBPOOL *pool; - struct JOB *prev; - struct JOB *next; - volatile int status; - volatile int result; - JOBFUNC func; - void *func_data; -} JOB; - -typedef struct JOBPOOL -{ - LOCK lock; - JOB *first_job; - JOB *last_job; -} JOBPOOL; - -enum -{ - JOBSTATUS_PENDING=0, - JOBSTATUS_RUNNING, - JOBSTATUS_DONE - /*JOBSTATUS_ABORTING,*/ - /*JOBSTATUS_ABORTED,*/ -}; - -int jobs_initpool(JOBPOOL *pool, int num_threads); -int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data); -int jobs_status(JOB *job); diff --git a/src/engine/e_linereader.cpp b/src/engine/e_linereader.cpp deleted file mode 100644 index 57ba9a85..00000000 --- a/src/engine/e_linereader.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "e_linereader.h" - -void linereader_init(LINEREADER *lr, IOHANDLE io) -{ - lr->buffer_max_size = 4*1024; - lr->buffer_size = 0; - lr->buffer_pos = 0; - lr->io = io; -} - -char *linereader_get(LINEREADER *lr) -{ - unsigned line_start = lr->buffer_pos; - - while(1) - { - if(lr->buffer_pos >= lr->buffer_size) - { - /* fetch more */ - - /* move the remaining part to the front */ - unsigned read; - unsigned left = lr->buffer_size - line_start; - - if(line_start > lr->buffer_size) - left = 0; - if(left) - mem_move(lr->buffer, &lr->buffer[line_start], left); - lr->buffer_pos = left; - - /* fill the buffer */ - read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos); - lr->buffer_size = left + read; - line_start = 0; - - if(!read) - { - if(left) - { - lr->buffer[left] = 0; /* return the last line */ - lr->buffer_pos = left; - lr->buffer_size = left; - return lr->buffer; - } - else - return 0x0; /* we are done! */ - } - } - else - { - if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r') - { - /* line found */ - lr->buffer[lr->buffer_pos] = 0; - lr->buffer_pos++; - return &lr->buffer[line_start]; - } - else - lr->buffer_pos++; - } - } -} diff --git a/src/engine/e_linereader.h b/src/engine/e_linereader.h deleted file mode 100644 index ca84960a..00000000 --- a/src/engine/e_linereader.h +++ /dev/null @@ -1,14 +0,0 @@ -#include <base/system.h> - -/* buffered stream for reading lines, should perhaps be something smaller */ -typedef struct -{ - char buffer[4*1024]; - unsigned buffer_pos; - unsigned buffer_size; - unsigned buffer_max_size; - IOHANDLE io; -} LINEREADER; - -void linereader_init(LINEREADER *lr, IOHANDLE io); -char *linereader_get(LINEREADER *lr); diff --git a/src/engine/e_map.cpp b/src/engine/e_map.cpp deleted file mode 100644 index a2048310..00000000 --- a/src/engine/e_map.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> -#include "e_datafile.h" - -static DATAFILE *map = 0; - -void *map_get_data(int index) -{ - return datafile_get_data(map, index); -} - -void *map_get_data_swapped(int index) -{ - return datafile_get_data_swapped(map, index); -} - -void map_unload_data(int index) -{ - datafile_unload_data(map, index); -} - -void *map_get_item(int index, int *type, int *id) -{ - return datafile_get_item(map, index, type, id); -} - -void map_get_type(int type, int *start, int *num) -{ - datafile_get_type(map, type, start, num); -} - -void *map_find_item(int type, int id) -{ - return datafile_find_item(map, type, id); -} - -int map_num_items() -{ - return datafile_num_items(map); -} - -void map_unload() -{ - datafile_unload(map); - map = 0x0; -} - -int map_is_loaded() -{ - return map != 0; -} - -int map_load(const char *mapname) -{ - char buf[512]; - str_format(buf, sizeof(buf), "maps/%s.map", mapname); - map = datafile_load(buf); - return map != 0; -} - -void map_set(void *m) -{ - if(map) - map_unload(); - map = (DATAFILE*)m; -} diff --git a/src/engine/e_memheap.cpp b/src/engine/e_memheap.cpp deleted file mode 100644 index fe157e86..00000000 --- a/src/engine/e_memheap.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> - -struct CHUNK -{ - char *memory; - char *current; - char *end; - CHUNK *next; -}; - -struct HEAP -{ - CHUNK *current; -}; - -/* how large each chunk should be */ -static const int chunksize = 1024*64; - -/* allocates a new chunk to be used */ -static CHUNK *memheap_newchunk() -{ - CHUNK *chunk; - char *mem; - - /* allocate memory */ - mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1); - if(!mem) - return 0x0; - - /* the chunk structure is located in the begining of the chunk */ - /* init it and return the chunk */ - chunk = (CHUNK*)mem; - chunk->memory = (char*)(chunk+1); - chunk->current = chunk->memory; - chunk->end = chunk->memory + chunksize; - chunk->next = (CHUNK *)0x0; - return chunk; -} - -/******************/ -static void *memheap_allocate_from_chunk(CHUNK *chunk, unsigned int size) -{ - char *mem; - - /* check if we need can fit the allocation */ - if(chunk->current + size > chunk->end) - return (void*)0x0; - - /* get memory and move the pointer forward */ - mem = chunk->current; - chunk->current += size; - return mem; -} - -/* creates a heap */ -HEAP *memheap_create() -{ - CHUNK *chunk; - HEAP *heap; - - /* allocate a chunk and allocate the heap structure on that chunk */ - chunk = memheap_newchunk(); - heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP)); - heap->current = chunk; - return heap; -} - -/* destroys the heap */ -void memheap_destroy(HEAP *heap) -{ - CHUNK *chunk = heap->current; - CHUNK *next; - - while(chunk) - { - next = chunk->next; - mem_free(chunk); - chunk = next; - } -} - -/* */ -void *memheap_allocate(HEAP *heap, unsigned int size) -{ - char *mem; - - /* try to allocate from current chunk */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - if(!mem) - { - /* allocate new chunk and add it to the heap */ - CHUNK *chunk = memheap_newchunk(); - chunk->next = heap->current; - heap->current = chunk; - - /* try to allocate again */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - } - - return mem; -} diff --git a/src/engine/e_memheap.h b/src/engine/e_memheap.h deleted file mode 100644 index b4391ec7..00000000 --- a/src/engine/e_memheap.h +++ /dev/null @@ -1,6 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -struct HEAP; -HEAP *memheap_create(); -void memheap_destroy(HEAP *heap); -void *memheap_allocate(HEAP *heap, unsigned int size); diff --git a/src/engine/e_msg.cpp b/src/engine/e_msg.cpp deleted file mode 100644 index 999a0ff0..00000000 --- a/src/engine/e_msg.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "e_common_interface.h" -#include "e_packer.h" - -/* message packing */ -static CPacker msg_packer; -static MSG_INFO pack_info; -static int packer_failed = 0; - -void msg_pack_int(int i) { msg_packer.AddInt(i); } -void msg_pack_string(const char *p, int limit) { msg_packer.AddString(p, limit); } -void msg_pack_raw(const void *data, int size) { msg_packer.AddRaw((const unsigned char *)data, size); } - -void msg_pack_start_system(int msg, int flags) -{ - msg_packer.Reset(); - pack_info.msg = (msg<<1)|1; - pack_info.flags = flags; - packer_failed = 0; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_start(int msg, int flags) -{ - msg_packer.Reset(); - pack_info.msg = msg<<1; - pack_info.flags = flags; - packer_failed = 0; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_end() -{ - if(msg_packer.Error()) - { - packer_failed = 1; - pack_info.size = 0; - pack_info.data = (unsigned char *)""; - } - else - { - pack_info.size = msg_packer.Size(); - pack_info.data = msg_packer.Data(); - } -} - -const MSG_INFO *msg_get_info() -{ - if(packer_failed) - return 0; - return &pack_info; -} - -/* message unpacking */ -static CUnpacker msg_unpacker; -int msg_unpack_start(const void *data, int data_size, int *system) -{ - int msg; - msg_unpacker.Reset((const unsigned char *)data, data_size); - msg = msg_unpack_int(); - *system = msg&1; - return msg>>1; -} - -int msg_unpack_int() { return msg_unpacker.GetInt(); } -const char *msg_unpack_string() { return msg_unpacker.GetString(); } -const unsigned char *msg_unpack_raw(int size) { return msg_unpacker.GetRaw(size); } -int msg_unpack_error() { return msg_unpacker.Error(); } diff --git a/src/engine/e_protocol.h b/src/engine/e_protocol.h deleted file mode 100644 index a1feb4cc..00000000 --- a/src/engine/e_protocol.h +++ /dev/null @@ -1,73 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> - -/* - Connection diagram - How the initilization works. - - Client -> INFO -> Server - Contains version info, name, and some other info. - - Client <- MAP <- Server - Contains current map. - - Client -> READY -> Server - The client has loaded the map and is ready to go, - but the mod needs to send it's information aswell. - modc_connected is called on the client and - mods_connected is called on the server. - The client should call client_entergame when the - mod has done it's initilization. - - Client -> ENTERGAME -> Server - Tells the server to start sending snapshots. - client_entergame and server_client_enter is called. -*/ - - -enum -{ - NETMSG_NULL=0, - - /* the first thing sent by the client - contains the version info for the client */ - NETMSG_INFO=1, - - /* sent by server */ - NETMSG_MAP_CHANGE, /* sent when client should switch map */ - NETMSG_MAP_DATA, /* map transfer, contains a chunk of the map file */ - NETMSG_SNAP, /* normal snapshot, multiple parts */ - NETMSG_SNAPEMPTY, /* empty snapshot */ - NETMSG_SNAPSINGLE, /* ? */ - NETMSG_SNAPSMALL, /* */ - NETMSG_INPUTTIMING, /* reports how off the input was */ - NETMSG_RCON_AUTH_STATUS,/* result of the authentication */ - NETMSG_RCON_LINE, /* line that should be printed to the remote console */ - - NETMSG_AUTH_CHALLANGE, /* */ - NETMSG_AUTH_RESULT, /* */ - - /* sent by client */ - NETMSG_READY, /* */ - NETMSG_ENTERGAME, - NETMSG_INPUT, /* contains the inputdata from the client */ - NETMSG_RCON_CMD, /* */ - NETMSG_RCON_AUTH, /* */ - NETMSG_REQUEST_MAP_DATA,/* */ - - NETMSG_AUTH_START, /* */ - NETMSG_AUTH_RESPONSE, /* */ - - /* sent by both */ - NETMSG_PING, - NETMSG_PING_REPLY, - NETMSG_ERROR -}; - - -/* this should be revised */ -enum -{ - MAX_CLANNAME_LENGTH=32, - MAX_INPUT_SIZE=128, - MAX_SNAPSHOT_PACKSIZE=900 -}; diff --git a/src/engine/e_server_interface.h b/src/engine/e_server_interface.h deleted file mode 100644 index c325b9a1..00000000 --- a/src/engine/e_server_interface.h +++ /dev/null @@ -1,12 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_SERVER_INTERFACE_H -#define ENGINE_SERVER_INTERFACE_H - -#include "e_if_other.h" -#include "e_if_server.h" -#include "e_if_msg.h" -#include "e_if_mods.h" - -#include "e_console.h" /* TODO: clean this up*/ - -#endif diff --git a/src/engine/e_snapshot.cpp b/src/engine/e_snapshot.cpp deleted file mode 100644 index 65487665..00000000 --- a/src/engine/e_snapshot.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdlib.h> -#include "e_snapshot.h" -#include "e_engine.h" -#include "e_compression.h" -#include "e_common_interface.h" - - -/* TODO: strange arbitrary number */ -static short item_sizes[64] = {0}; - -void snap_set_staticsize(int itemtype, int size) -{ - item_sizes[itemtype] = size; -} - -CSnapshotItem *CSnapshot::GetItem(int Index) -{ - return (CSnapshotItem *)(DataStart() + Offsets()[Index]); -} - -int CSnapshot::GetItemSize(int Index) -{ - if(Index == m_NumItems-1) - return (m_DataSize - Offsets()[Index]) - sizeof(CSnapshotItem); - return (Offsets()[Index+1] - Offsets()[Index]) - sizeof(CSnapshotItem); -} - -int CSnapshot::GetItemIndex(int Key) -{ - /* TODO: OPT: this should not be a linear search. very bad */ - for(int i = 0; i < m_NumItems; i++) - { - if(GetItem(i)->Key() == Key) - return i; - } - return -1; -} - -// TODO: clean up this -typedef struct -{ - int num; - int keys[64]; - int index[64]; -} ITEMLIST; - -static ITEMLIST sorted[256]; - -int CSnapshot::GenerateHash() -{ - for(int i = 0; i < 256; i++) - sorted[i].num = 0; - - for(int i = 0; i < m_NumItems; i++) - { - int Key = GetItem(i)->Key(); - int HashID = ((Key>>8)&0xf0) | (Key&0xf); - if(sorted[HashID].num != 64) - { - sorted[HashID].index[sorted[HashID].num] = i; - sorted[HashID].keys[sorted[HashID].num] = Key; - sorted[HashID].num++; - } - } - return 0; -} - -int CSnapshot::GetItemIndexHashed(int Key) -{ - int HashID = ((Key>>8)&0xf0) | (Key&0xf); - for(int i = 0; i < sorted[HashID].num; i++) - { - if(sorted[HashID].keys[i] == Key) - return sorted[HashID].index[i]; - } - - return -1; -} - - - -static const int MAX_ITEMS = 512; -static CSnapshotDelta empty = {0,0,0,{0}}; - -CSnapshotDelta *CSnapshot::EmptyDelta() -{ - return ∅ -} - -int CSnapshot::Crc() -{ - int crc = 0; - - for(int i = 0; i < m_NumItems; i++) - { - CSnapshotItem *pItem = GetItem(i); - int Size = GetItemSize(i); - - for(int b = 0; b < Size/4; b++) - crc += pItem->Data()[b]; - } - return crc; -} - -void CSnapshot::DebugDump() -{ - dbg_msg("snapshot", "data_size=%d num_items=%d", m_DataSize, m_NumItems); - for(int i = 0; i < m_NumItems; i++) - { - CSnapshotItem *pItem = GetItem(i); - int Size = GetItemSize(i); - dbg_msg("snapshot", "\ttype=%d id=%d", pItem->Type(), pItem->ID()); - for(int b = 0; b < Size/4; b++) - dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, pItem->Data()[b], pItem->Data()[b]); - } -} - - -// TODO: remove these -int snapshot_data_rate[0xffff] = {0}; -int snapshot_data_updates[0xffff] = {0}; -static int snapshot_current = 0; - -static int diff_item(int *past, int *current, int *out, int size) -{ - int needed = 0; - while(size) - { - *out = *current-*past; - needed |= *out; - out++; - past++; - current++; - size--; - } - - return needed; -} - -static void undiff_item(int *past, int *diff, int *out, int size) -{ - while(size) - { - *out = *past+*diff; - - if(*diff == 0) - snapshot_data_rate[snapshot_current] += 1; - else - { - unsigned char buf[16]; - unsigned char *end = vint_pack(buf, *diff); - snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; - } - - out++; - past++; - diff++; - size--; - } -} - - -/* TODO: OPT: this should be made much faster */ -int CSnapshot::CreateDelta(CSnapshot *from, CSnapshot *to, void *dstdata) -{ - static PERFORMACE_INFO hash_scope = {"hash", 0}; - CSnapshotDelta *delta = (CSnapshotDelta *)dstdata; - int *data = (int *)delta->m_pData; - int i, itemsize, pastindex; - CSnapshotItem *pFromItem; - CSnapshotItem *pCurItem; - CSnapshotItem *pPastItem; - int count = 0; - int size_count = 0; - - delta->m_NumDeletedItems = 0; - delta->m_NumUpdateItems = 0; - delta->m_NumTempItems = 0; - - perf_start(&hash_scope); - to->GenerateHash(); - perf_end(); - - /* pack deleted stuff */ - { - static PERFORMACE_INFO scope = {"delete", 0}; - perf_start(&scope); - - for(i = 0; i < from->m_NumItems; i++) - { - pFromItem = from->GetItem(i); - if(to->GetItemIndexHashed(pFromItem->Key()) == -1) - { - /* deleted */ - delta->m_NumDeletedItems++; - *data = pFromItem->Key(); - data++; - } - } - - perf_end(); - } - - perf_start(&hash_scope); - from->GenerateHash(); - perf_end(); - - /* pack updated stuff */ - { - static PERFORMACE_INFO scope = {"update", 0}; - int pastindecies[1024]; - perf_start(&scope); - - /* fetch previous indices */ - /* we do this as a separate pass because it helps the cache */ - { - static PERFORMACE_INFO scope = {"find", 0}; - perf_start(&scope); - for(i = 0; i < to->m_NumItems; i++) - { - pCurItem = to->GetItem(i); /* O(1) .. O(n) */ - pastindecies[i] = from->GetItemIndexHashed(pCurItem->Key()); /* O(n) .. O(n^n)*/ - } - perf_end(); - } - - for(i = 0; i < to->m_NumItems; i++) - { - /* do delta */ - itemsize = to->GetItemSize(i); /* O(1) .. O(n) */ - pCurItem = to->GetItem(i); /* O(1) .. O(n) */ - pastindex = pastindecies[i]; - - if(pastindex != -1) - { - static PERFORMACE_INFO scope = {"diff", 0}; - int *item_data_dst = data+3; - perf_start(&scope); - - pPastItem = from->GetItem(pastindex); - - if(item_sizes[pCurItem->Type()]) - item_data_dst = data+2; - - if(diff_item((int*)pPastItem->Data(), (int*)pCurItem->Data(), item_data_dst, itemsize/4)) - { - - *data++ = pCurItem->Type(); - *data++ = pCurItem->ID(); - if(!item_sizes[pCurItem->Type()]) - *data++ = itemsize/4; - data += itemsize/4; - delta->m_NumUpdateItems++; - } - perf_end(); - } - else - { - static PERFORMACE_INFO scope = {"copy", 0}; - perf_start(&scope); - - *data++ = pCurItem->Type(); - *data++ = pCurItem->ID(); - if(!item_sizes[pCurItem->Type()]) - *data++ = itemsize/4; - - mem_copy(data, pCurItem->Data(), itemsize); - size_count += itemsize; - data += itemsize/4; - delta->m_NumUpdateItems++; - count++; - - perf_end(); - } - } - - perf_end(); - } - - if(0) - { - dbg_msg("snapshot", "%d %d %d", - delta->m_NumDeletedItems, - delta->m_NumUpdateItems, - delta->m_NumTempItems); - } - - /* - // TODO: pack temp stuff - - // finish - //mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int)); - //mem_copy(delta->data_start(), data, data_size); - //delta->data_size = data_size; - * */ - - if(!delta->m_NumDeletedItems && !delta->m_NumUpdateItems && !delta->m_NumTempItems) - return 0; - - return (int)((char*)data-(char*)dstdata); -} - -static int range_check(void *end, void *ptr, int size) -{ - if((const char *)ptr + size > (const char *)end) - return -1; - return 0; -} - -int CSnapshot::UnpackDelta(CSnapshot *from, CSnapshot *to, void *srcdata, int data_size) -{ - CSnapshotBuilder builder; - CSnapshotDelta *delta = (CSnapshotDelta *)srcdata; - int *data = (int *)delta->m_pData; - int *end = (int *)(((char *)srcdata + data_size)); - - CSnapshotItem *fromitem; - int i, d, keep, itemsize; - int *deleted; - int id, type, key; - int fromindex; - int *newdata; - - builder.Init(); - - /* unpack deleted stuff */ - deleted = data; - data += delta->m_NumDeletedItems; - if(data > end) - return -1; - - /* copy all non deleted stuff */ - for(i = 0; i < from->m_NumItems; i++) - { - /* dbg_assert(0, "fail!"); */ - fromitem = from->GetItem(i); - itemsize = from->GetItemSize(i); - keep = 1; - for(d = 0; d < delta->m_NumDeletedItems; d++) - { - if(deleted[d] == fromitem->Key()) - { - keep = 0; - break; - } - } - - if(keep) - { - /* keep it */ - mem_copy( - builder.NewItem(fromitem->Type(), fromitem->ID(), itemsize), - fromitem->Data(), itemsize); - } - } - - /* unpack updated stuff */ - for(i = 0; i < delta->m_NumUpdateItems; i++) - { - if(data+2 > end) - return -1; - - type = *data++; - id = *data++; - if(item_sizes[type]) - itemsize = item_sizes[type]; - else - { - if(data+1 > end) - return -2; - itemsize = (*data++) * 4; - } - snapshot_current = type; - - if(range_check(end, data, itemsize) || itemsize < 0) return -3; - - key = (type<<16)|id; - - /* create the item if needed */ - newdata = builder.GetItemData(key); - if(!newdata) - newdata = (int *)builder.NewItem(key>>16, key&0xffff, itemsize); - - /*if(range_check(end, newdata, itemsize)) return -4;*/ - - fromindex = from->GetItemIndex(key); - if(fromindex != -1) - { - /* we got an update so we need to apply the diff */ - undiff_item((int *)from->GetItem(fromindex)->Data(), data, newdata, itemsize/4); - snapshot_data_updates[snapshot_current]++; - } - else /* no previous, just copy the data */ - { - mem_copy(newdata, data, itemsize); - snapshot_data_rate[snapshot_current] += itemsize*8; - snapshot_data_updates[snapshot_current]++; - } - - data += itemsize/4; - } - - /* finish up */ - return builder.Finish(to); -} - -/* CSnapshotStorage */ - -void CSnapshotStorage::Init() -{ - m_pFirst = 0; - m_pLast = 0; -} - -void CSnapshotStorage::PurgeAll() -{ - CHolder *pHolder = m_pFirst; - CHolder *pNext; - - while(pHolder) - { - pNext = pHolder->m_pNext; - mem_free(pHolder); - pHolder = pNext; - } - - /* no more snapshots in storage */ - m_pFirst = 0; - m_pLast = 0; -} - -void CSnapshotStorage::PurgeUntil(int Tick) -{ - CHolder *pHolder = m_pFirst; - CHolder *pNext; - - while(pHolder) - { - pNext = pHolder->m_pNext; - if(pHolder->m_Tick >= Tick) - return; /* no more to remove */ - mem_free(pHolder); - - /* did we come to the end of the list? */ - if (!pNext) - break; - - m_pFirst = pNext; - pNext->m_pPrev = 0x0; - - pHolder = pNext; - } - - /* no more snapshots in storage */ - m_pFirst = 0; - m_pLast = 0; -} - -void CSnapshotStorage::Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt) -{ - /* allocate memory for holder + snapshot_data */ - int TotalSize = sizeof(CHolder)+DataSize; - - if(CreateAlt) - TotalSize += DataSize; - - CHolder *pHolder = (CHolder *)mem_alloc(TotalSize, 1); - - /* set data */ - pHolder->m_Tick = Tick; - pHolder->m_Tagtime = Tagtime; - pHolder->m_SnapSize = DataSize; - pHolder->m_pSnap = (CSnapshot*)(pHolder+1); - mem_copy(pHolder->m_pSnap, pData, DataSize); - - if(CreateAlt) /* create alternative if wanted */ - { - pHolder->m_pAltSnap = (CSnapshot*)(((char *)pHolder->m_pSnap) + DataSize); - mem_copy(pHolder->m_pAltSnap, pData, DataSize); - } - else - pHolder->m_pAltSnap = 0; - - - /* link */ - pHolder->m_pNext = 0; - pHolder->m_pPrev = m_pLast; - if(m_pLast) - m_pLast->m_pNext = pHolder; - else - m_pFirst = pHolder; - m_pLast = pHolder; -} - -int CSnapshotStorage::Get(int Tick, int64 *pTagtime, CSnapshot **ppData, CSnapshot **ppAltData) -{ - CHolder *pHolder = m_pFirst; - - while(pHolder) - { - if(pHolder->m_Tick == Tick) - { - if(pTagtime) - *pTagtime = pHolder->m_Tagtime; - if(ppData) - *ppData = pHolder->m_pSnap; - if(ppAltData) - *ppData = pHolder->m_pAltSnap; - return pHolder->m_SnapSize; - } - - pHolder = pHolder->m_pNext; - } - - return -1; -} - -/* CSnapshotBuilder */ - -void CSnapshotBuilder::Init() -{ - m_DataSize = 0; - m_NumItems = 0; -} - -CSnapshotItem *CSnapshotBuilder::GetItem(int Index) -{ - return (CSnapshotItem *)&(m_aData[m_aOffsets[Index]]); -} - -int *CSnapshotBuilder::GetItemData(int key) -{ - int i; - for(i = 0; i < m_NumItems; i++) - { - if(GetItem(i)->Key() == key) - return (int *)GetItem(i)->Data(); - } - return 0; -} - -int CSnapshotBuilder::Finish(void *snapdata) -{ - /* flattern and make the snapshot */ - CSnapshot *pSnap = (CSnapshot *)snapdata; - int OffsetSize = sizeof(int)*m_NumItems; - pSnap->m_DataSize = m_DataSize; - pSnap->m_NumItems = m_NumItems; - mem_copy(pSnap->Offsets(), m_aOffsets, OffsetSize); - mem_copy(pSnap->DataStart(), m_aData, m_DataSize); - return sizeof(CSnapshot) + OffsetSize + m_DataSize; -} - -void *CSnapshotBuilder::NewItem(int Type, int ID, int Size) -{ - CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize); - - mem_zero(pObj, sizeof(CSnapshotItem) + Size); - pObj->m_TypeAndID = (Type<<16)|ID; - m_aOffsets[m_NumItems] = m_DataSize; - m_DataSize += sizeof(CSnapshotItem) + Size; - m_NumItems++; - - dbg_assert(m_DataSize < CSnapshot::MAX_SIZE, "too much data"); - dbg_assert(m_NumItems < MAX_ITEMS, "too many items"); - - return pObj->Data(); -} diff --git a/src/engine/editor.h b/src/engine/editor.h new file mode 100644 index 00000000..32a5cc93 --- /dev/null +++ b/src/engine/editor.h @@ -0,0 +1,16 @@ +#ifndef ENGINE_EDITOR_H +#define ENGINE_EDITOR_H +#include "kernel.h" + +class IEditor : public IInterface +{ + MACRO_INTERFACE("editor", 0) +public: + + virtual ~IEditor() {} + virtual void Init() = 0; + virtual void UpdateAndRender() = 0; +}; + +extern IEditor *CreateEditor(); +#endif diff --git a/src/engine/graphics.h b/src/engine/graphics.h new file mode 100644 index 00000000..4d3c0bc0 --- /dev/null +++ b/src/engine/graphics.h @@ -0,0 +1,151 @@ +#ifndef ENGINE_GRAPHICS_H +#define ENGINE_GRAPHICS_H + +#include "kernel.h" + +class CImageInfo +{ +public: + enum + { + FORMAT_AUTO=-1, + FORMAT_RGB=0, + FORMAT_RGBA=1, + FORMAT_ALPHA=2, + }; + + /* Variable: width + Contains the width of the image */ + int m_Width; + + /* Variable: height + Contains the height of the image */ + int m_Height; + + /* Variable: format + Contains the format of the image. See <Image Formats> for more information. */ + int m_Format; + + /* Variable: data + Pointer to the image data. */ + void *m_pData; +}; + +/* + Structure: CVideoMode +*/ +class CVideoMode +{ +public: + int m_Width, m_Height; + int m_Red, m_Green, m_Blue; +}; + +class IGraphics : public IInterface +{ + MACRO_INTERFACE("graphics", 0) +protected: + int m_ScreenWidth; + int m_ScreenHeight; +public: + /* Constants: Texture Loading Flags + TEXLOAD_NORESAMPLE - Prevents the texture from any resampling + */ + enum + { + TEXLOAD_NORESAMPLE=1, + }; + + int ScreenWidth() const { return m_ScreenWidth; } + int ScreenHeight() const { return m_ScreenHeight; } + float ScreenAspect() const { return (float)ScreenWidth()/(float)ScreenHeight(); } + + virtual void Clear(float r, float g, float b) = 0; + + virtual void ClipEnable(int x, int y, int w, int h) = 0; + virtual void ClipDisable() = 0; + + virtual void MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY) = 0; + virtual void GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY) = 0; + + // TODO: These should perhaps not be virtuals + virtual void BlendNone() = 0; + virtual void BlendNormal() = 0; + virtual void BlendAdditive() = 0; + virtual int MemoryUsage() const = 0; + + virtual int LoadPNG(CImageInfo *pImg, const char *pFilename) =0; + virtual int UnloadTexture(int Index) = 0; + virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) = 0; + virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) = 0; + virtual void TextureSet(int TextureID) = 0; + + struct CLineItem + { + float m_X0, m_Y0, m_X1, m_Y1; + CLineItem() {} + CLineItem(float x0, float y0, float x1, float y1) : m_X0(x0), m_Y0(y0), m_X1(x1), m_Y1(y1) {} + }; + virtual void LinesBegin() = 0; + virtual void LinesEnd() = 0; + virtual void LinesDraw(const CLineItem *pArray, int Num) = 0; + + virtual void QuadsBegin() = 0; + virtual void QuadsEnd() = 0; + virtual void QuadsSetRotation(float Angle) = 0; + virtual void QuadsSetSubset(float TopLeftY, float TopLeftV, float BottomRightU, float BottomRightV) = 0; + virtual void QuadsSetSubsetFree(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) = 0; + + struct CQuadItem + { + float m_X, m_Y, m_Width, m_Height; + CQuadItem() {} + CQuadItem(float x, float y, float w, float h) : m_X(x), m_Y(y), m_Width(w), m_Height(h) {} + }; + virtual void QuadsDraw(CQuadItem *pArray, int Num) = 0; + virtual void QuadsDrawTL(const CQuadItem *pArray, int Num) = 0; + + struct CFreeformItem + { + float m_X0, m_Y0, m_X1, m_Y1, m_X2, m_Y2, m_X3, m_Y3; + CFreeformItem() {} + CFreeformItem(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) + : m_X0(x0), m_Y0(y0), m_X1(x1), m_Y1(y1), m_X2(x2), m_Y2(y2), m_X3(x3), m_Y3(y3) {} + }; + virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num) = 0; + virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) = 0; + + struct CColorVertex + { + int m_Index; + float m_R, m_G, m_B, m_A; + CColorVertex() {} + CColorVertex(int i, float r, float g, float b, float a) : m_Index(i), m_R(r), m_G(g), m_B(b), m_A(a) {} + }; + virtual void SetColorVertex(const CColorVertex *pArray, int Num) = 0; + virtual void SetColor(float r, float g, float b, float a) = 0; + + virtual void TakeScreenshot() = 0; + virtual int GetVideoModes(CVideoMode *pModes, int MaxModes) = 0; + + virtual void Swap() = 0; +}; + +class IEngineGraphics : public IGraphics +{ + MACRO_INTERFACE("enginegraphics", 0) +public: + virtual bool Init() = 0; + virtual void Shutdown() = 0; + + virtual void Minimize() = 0; + virtual void Maximize() = 0; + + virtual int WindowActive() = 0; + virtual int WindowOpen() = 0; + +}; + +extern IEngineGraphics *CreateEngineGraphics(); + +#endif diff --git a/src/engine/input.h b/src/engine/input.h new file mode 100644 index 00000000..168614c8 --- /dev/null +++ b/src/engine/input.h @@ -0,0 +1,89 @@ +#ifndef ENGINE_INPUT_H +#define ENGINE_INPUT_H + +#include "kernel.h" + +extern const char g_aaKeyStrings[512][16]; + +class IInput : public IInterface +{ + MACRO_INTERFACE("input", 0) +public: + class CEvent + { + public: + int m_Flags; + int m_Unicode; + int m_Key; + }; + +protected: + enum + { + INPUT_BUFFER_SIZE=32 + }; + + // quick access to events + int m_NumEvents; + IInput::CEvent m_aInputEvents[INPUT_BUFFER_SIZE]; + + //quick access to input + struct + { + unsigned char m_Presses; + unsigned char m_Releases; + } m_aInputCount[2][1024]; + + unsigned char m_aInputState[2][1024]; + int m_InputCurrent; + + int KeyWasPressed(int Key) { return m_aInputState[m_InputCurrent^1][Key]; } + +public: + enum + { + FLAG_PRESS=1, + FLAG_RELEASE=2, + FLAG_REPEAT=4 + }; + + // events + int NumEvents() const { return m_NumEvents; } + void ClearEvents() { m_NumEvents = 0; } + CEvent GetEvent(int Index) const + { + if(Index < 0 || Index >= m_NumEvents) + { + IInput::CEvent e = {0,0}; + return e; + } + return m_aInputEvents[Index]; + } + + // keys + int KeyPressed(int Key) { return m_aInputState[m_InputCurrent][Key]; } + int KeyReleases(int Key) { return m_aInputCount[m_InputCurrent][Key].m_Releases; } + int KeyPresses(int Key) { return m_aInputCount[m_InputCurrent][Key].m_Presses; } + int KeyDown(int Key) { return KeyPressed(Key)&&!KeyWasPressed(Key); } + const char *KeyName(int Key) { return (Key >= 0 && Key < 512) ? g_aaKeyStrings[Key] : g_aaKeyStrings[0]; } + + // + virtual void MouseModeRelative() = 0; + virtual void MouseModeAbsolute() = 0; + virtual int MouseDoubleClick() = 0; + + virtual void MouseRelative(int *x, int *y) = 0; +}; + + +class IEngineInput : public IInput +{ + MACRO_INTERFACE("engineinput", 0) +public: + virtual void Init() = 0; + virtual void Update() = 0; +}; + +extern IEngineInput *CreateEngineInput(); + +#endif diff --git a/src/engine/kernel.h b/src/engine/kernel.h new file mode 100644 index 00000000..6a72690f --- /dev/null +++ b/src/engine/kernel.h @@ -0,0 +1,66 @@ +#ifndef ENGINE_KERNEL_H +#define ENGINE_KERNEL_H + +#include <base/system.h> + +class IKernel; +class IInterface; + +class IInterface +{ + // friend with the kernel implementation + friend class CKernel; + IKernel *m_pKernel; +protected: + IKernel *Kernel() { return m_pKernel; } +public: + IInterface() : m_pKernel(0) {} + virtual ~IInterface() {} + + //virtual unsigned InterfaceID() = 0; + //virtual const char *InterfaceName() = 0; +}; + +#define MACRO_INTERFACE(Name, ver) \ + public: \ + static const char *InterfaceName() { return Name; } \ + private: + + //virtual unsigned InterfaceID() { return INTERFACE_ID; } + //virtual const char *InterfaceName() { return name; } + + +// This kernel thingie makes the structure very flat and basiclly singletons. +// I'm not sure if this is a good idea but it works for now. +class IKernel +{ + // hide the implementation + virtual bool RegisterInterfaceImpl(const char *InterfaceName, IInterface *pInterface) = 0; + virtual bool ReregisterInterfaceImpl(const char *InterfaceName, IInterface *pInterface) = 0; + virtual IInterface *RequestInterfaceImpl(const char *InterfaceName) = 0; +public: + static IKernel *Create(); + virtual ~IKernel() {} + + // templated access to handle pointer convertions and interface names + template<class TINTERFACE> + bool RegisterInterface(TINTERFACE *pInterface) + { + return RegisterInterfaceImpl(TINTERFACE::InterfaceName(), pInterface); + } + template<class TINTERFACE> + bool ReregisterInterface(TINTERFACE *pInterface) + { + return ReregisterInterfaceImpl(TINTERFACE::InterfaceName(), pInterface); + } + + // Usage example: + // IMyInterface *pMyHandle = Kernel()->RequestInterface<IMyInterface>() + template<class TINTERFACE> + TINTERFACE *RequestInterface() + { + return reinterpret_cast<TINTERFACE *>(RequestInterfaceImpl(TINTERFACE::InterfaceName())); + } +}; + +#endif diff --git a/src/engine/e_keys.h b/src/engine/keys.h index cb4ca371..cb4ca371 100644 --- a/src/engine/e_keys.h +++ b/src/engine/keys.h diff --git a/src/engine/map.h b/src/engine/map.h new file mode 100644 index 00000000..2c285f26 --- /dev/null +++ b/src/engine/map.h @@ -0,0 +1,32 @@ +#ifndef ENGINE_MAP_H +#define ENGINE_MAP_H + +#include "kernel.h" + +class IMap : public IInterface +{ + MACRO_INTERFACE("map", 0) +public: + virtual void *GetData(int Index) = 0; + virtual void *GetDataSwapped(int Index) = 0; + virtual void UnloadData(int Index) = 0; + virtual void *GetItem(int Index, int *Type, int *pId) = 0; + virtual void GetType(int Type, int *pStart, int *pNum) = 0; + virtual void *FindItem(int Type, int Id) = 0; + virtual int NumItems() = 0; +}; + + +class IEngineMap : public IMap +{ + MACRO_INTERFACE("enginemap", 0) +public: + virtual bool Load(const char *pMapName) = 0; + virtual bool IsLoaded() = 0; + virtual void Unload() = 0; + virtual unsigned Crc() = 0; +}; + +extern IEngineMap *CreateEngineMap(); + +#endif diff --git a/src/engine/masterserver.h b/src/engine/masterserver.h new file mode 100644 index 00000000..9d16e85f --- /dev/null +++ b/src/engine/masterserver.h @@ -0,0 +1,40 @@ +#ifndef ENGINE_MASTERSERVER_H +#define ENGINE_MASTERSERVER_H + +#include "kernel.h" + +class IMasterServer : public IInterface +{ + MACRO_INTERFACE("masterserver", 0) +public: + + enum + { + MAX_MASTERSERVERS=4 + }; + + virtual void Init(class CEngine *pEngine) = 0; + virtual void SetDefault() = 0; + virtual int Load() = 0; + virtual int Save() = 0; + + virtual int RefreshAddresses() = 0; + virtual void Update() = 0; + virtual int IsRefreshing() = 0; + virtual void DumpServers() = 0; + virtual NETADDR GetAddr(int Index) = 0; + virtual const char *GetName(int Index) = 0; +}; + +class IEngineMasterServer : public IMasterServer +{ + MACRO_INTERFACE("enginemasterserver", 0) +public: +}; + +extern IEngineMasterServer *CreateEngineMasterServer(); + +#endif + + + diff --git a/src/engine/message.h b/src/engine/message.h index 6de407ed..3ffc488f 100644 --- a/src/engine/message.h +++ b/src/engine/message.h @@ -1,8 +1,16 @@ +#ifndef ENGINE_MESSAGE_H +#define ENGINE_MESSAGE_H +#include <engine/shared/packer.h> -class CMessage +class CMsgPacker : public CPacker { public: - virtual bool Pack(void *pData, unsigned MaxDataSize); - virtual bool Unpack(const void *pData, unsigned DataSize); + CMsgPacker(int Type) + { + Reset(); + AddInt(Type); + } }; + +#endif diff --git a/src/engine/server.h b/src/engine/server.h new file mode 100644 index 00000000..52e6ec6a --- /dev/null +++ b/src/engine/server.h @@ -0,0 +1,81 @@ +#ifndef ENGINE_SERVER_H +#define ENGINE_SERVER_H +#include "kernel.h" +#include "message.h" + +class IServer : public IInterface +{ + MACRO_INTERFACE("server", 0) +protected: + int m_CurrentGameTick; + int m_TickSpeed; + +public: + /* + Structure: CClientInfo + */ + struct CClientInfo + { + const char *m_pName; + int m_Latency; + }; + + int Tick() const { return m_CurrentGameTick; } + int TickSpeed() const { return m_TickSpeed; } + + virtual const char *ClientName(int ClientID) = 0; + virtual bool ClientIngame(int ClientID) = 0; + virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) = 0; + virtual void GetClientIP(int ClientID, char *pIPString, int Size) = 0; + virtual int *LatestInput(int ClientID, int *pSize) = 0; + + virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0; + + template<class T> + int SendPackMsg(T *pMsg, int Flags, int ClientID) + { + CMsgPacker Packer(pMsg->MsgID()); + if(pMsg->Pack(&Packer)) + return -1; + return SendMsg(&Packer, Flags, ClientID); + } + + virtual void SetBrowseInfo(char const *pGameType, int Progression) = 0; + virtual void SetClientName(int ClientID, char const *pName) = 0; + virtual void SetClientScore(int ClientID, int Score) = 0; + + virtual int SnapNewID() = 0; + virtual void SnapFreeID(int ID) = 0; + virtual void *SnapNewItem(int Type, int Id, int Size) = 0; + + virtual void SnapSetStaticsize(int ItemType, int Size) = 0; +}; + +class IGameServer : public IInterface +{ + MACRO_INTERFACE("gameserver", 0) +protected: +public: + virtual void OnInit() = 0; + virtual void OnConsoleInit() = 0; + virtual void OnShutdown() = 0; + + virtual void OnTick() = 0; + virtual void OnPreSnap() = 0; + virtual void OnSnap(int ClientID) = 0; + virtual void OnPostSnap() = 0; + + virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientID) = 0; + + virtual void OnClientConnected(int ClientID) = 0; + virtual void OnClientEnter(int ClientID) = 0; + virtual void OnClientDrop(int ClientID) = 0; + virtual void OnClientDirectInput(int ClientID, void *pInput) = 0; + virtual void OnClientPredictedInput(int ClientID, void *pInput) = 0; + + virtual const char *Version() = 0; + virtual const char *NetVersion() = 0; +}; + +extern IGameServer *CreateGameServer(); +#endif diff --git a/src/engine/server/es_register.cpp b/src/engine/server/es_register.cpp deleted file mode 100644 index 0eb5dba5..00000000 --- a/src/engine/server/es_register.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include <string.h> -#include <base/system.h> -#include <engine/e_network.h> -#include <engine/e_config.h> -#include <engine/e_engine.h> - -#include <mastersrv/mastersrv.h> - -extern CNetServer *m_pNetServer; - -enum -{ - REGISTERSTATE_START=0, - REGISTERSTATE_UPDATE_ADDRS, - REGISTERSTATE_QUERY_COUNT, - REGISTERSTATE_HEARTBEAT, - REGISTERSTATE_REGISTERED, - REGISTERSTATE_ERROR -}; - -static int register_state = REGISTERSTATE_START; -static int64 register_state_start = 0; -static int register_first = 1; -static int register_count = 0; - -static void register_new_state(int state) -{ - register_state = state; - register_state_start = time_get(); -} - -static void register_send_fwcheckresponse(NETADDR *pAddr) -{ - CNetChunk Packet; - Packet.m_ClientID = -1; - Packet.m_Address = *pAddr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE); - Packet.m_pData = SERVERBROWSE_FWRESPONSE; - m_pNetServer->Send(&Packet); -} - -static void register_send_heartbeat(NETADDR addr) -{ - static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; - unsigned short port = config.sv_port; - CNetChunk Packet; - - mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); - - Packet.m_ClientID = -1; - Packet.m_Address = addr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2; - Packet.m_pData = &data; - - /* supply the set port that the master can use if it has problems */ - if(config.sv_external_port) - port = config.sv_external_port; - data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; - data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; - m_pNetServer->Send(&Packet); -} - -static void register_send_count_request(NETADDR Addr) -{ - CNetChunk Packet; - Packet.m_ClientID = -1; - Packet.m_Address = Addr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT); - Packet.m_pData = SERVERBROWSE_GETCOUNT; - m_pNetServer->Send(&Packet); -} - -typedef struct -{ - NETADDR addr; - int count; - int valid; - int64 last_send; -} MASTERSERVER_INFO; - -static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}}; -static int register_registered_server = -1; - -void register_update() -{ - int64 now = time_get(); - int64 freq = time_freq(); - - if(!config.sv_register) - return; - - mastersrv_update(); - - if(register_state == REGISTERSTATE_START) - { - register_count = 0; - register_first = 1; - register_new_state(REGISTERSTATE_UPDATE_ADDRS); - mastersrv_refresh_addresses(); - dbg_msg("register", "refreshing ip addresses"); - } - else if(register_state == REGISTERSTATE_UPDATE_ADDRS) - { - register_registered_server = -1; - - if(!mastersrv_refreshing()) - { - int i; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - NETADDR addr = mastersrv_get(i); - masterserver_info[i].addr = addr; - masterserver_info[i].count = 0; - - if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) - masterserver_info[i].valid = 0; - else - { - masterserver_info[i].valid = 1; - masterserver_info[i].count = -1; - masterserver_info[i].last_send = 0; - } - } - - dbg_msg("register", "fetching server counts"); - register_new_state(REGISTERSTATE_QUERY_COUNT); - } - } - else if(register_state == REGISTERSTATE_QUERY_COUNT) - { - int i; - int left = 0; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - if(!masterserver_info[i].valid) - continue; - - if(masterserver_info[i].count == -1) - { - left++; - if(masterserver_info[i].last_send+freq < now) - { - masterserver_info[i].last_send = now; - register_send_count_request(masterserver_info[i].addr); - } - } - } - - /* check if we are done or timed out */ - if(left == 0 || now > register_state_start+freq*3) - { - /* choose server */ - int best = -1; - int i; - for(i = 0; i < MAX_MASTERSERVERS; i++) - { - if(!masterserver_info[i].valid || masterserver_info[i].count == -1) - continue; - - if(best == -1 || masterserver_info[i].count < masterserver_info[best].count) - best = i; - } - - /* server chosen */ - register_registered_server = best; - if(register_registered_server == -1) - { - dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds"); - register_new_state(REGISTERSTATE_ERROR); - } - else - { - dbg_msg("register", "choosen '%s' as master, sending heartbeats", mastersrv_name(register_registered_server)); - masterserver_info[register_registered_server].last_send = 0; - register_new_state(REGISTERSTATE_HEARTBEAT); - } - } - } - else if(register_state == REGISTERSTATE_HEARTBEAT) - { - /* check if we should send heartbeat */ - if(now > masterserver_info[register_registered_server].last_send+freq*15) - { - masterserver_info[register_registered_server].last_send = now; - register_send_heartbeat(masterserver_info[register_registered_server].addr); - } - - if(now > register_state_start+freq*60) - { - dbg_msg("register", "WARNING: Master server is not responding, switching master"); - register_new_state(REGISTERSTATE_START); - } - } - else if(register_state == REGISTERSTATE_REGISTERED) - { - if(register_first) - dbg_msg("register", "server registered"); - - register_first = 0; - - /* check if we should send new heartbeat again */ - if(now > register_state_start+freq) - { - if(register_count == 120) /* redo the whole process after 60 minutes to balance out the master servers */ - register_new_state(REGISTERSTATE_START); - else - { - register_count++; - register_new_state(REGISTERSTATE_HEARTBEAT); - } - } - } - else if(register_state == REGISTERSTATE_ERROR) - { - /* check for restart */ - if(now > register_state_start+freq*60) - register_new_state(REGISTERSTATE_START); - } -} - -static void register_got_count(CNetChunk *pChunk) -{ - unsigned char *pData = (unsigned char *)pChunk->m_pData; - int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1]; - - for(int i = 0; i < MAX_MASTERSERVERS; i++) - { - if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0) - { - masterserver_info[i].count = Count; - break; - } - } -} - -int register_process_packet(CNetChunk *pPacket) -{ - if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) && - memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) - { - register_send_fwcheckresponse(&pPacket->m_Address); - return 1; - } - else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) && - memcmp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) - { - if(register_first) - dbg_msg("register", "no firewall/nat problems detected"); - register_new_state(REGISTERSTATE_REGISTERED); - return 1; - } - else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) && - memcmp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) - { - dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server."); - dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port); - register_new_state(REGISTERSTATE_ERROR); - return 1; - } - else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 && - memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) - { - register_got_count(pPacket); - return 1; - } - - return 0; -} diff --git a/src/engine/server/es_server.cpp b/src/engine/server/es_server.cpp deleted file mode 100644 index cc0e6e0f..00000000 --- a/src/engine/server/es_server.cpp +++ /dev/null @@ -1,1969 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#include <base/system.h> - -#include <engine/e_config.h> -#include <engine/e_engine.h> -#include <engine/e_server_interface.h> - -#include <engine/e_protocol.h> -#include <engine/e_snapshot.h> - -#include <engine/e_compression.h> - -#include <engine/e_network.h> -#include <engine/e_config.h> -#include <engine/e_packer.h> -#include <engine/e_datafile.h> -#include <engine/e_demorec.h> - -#include <mastersrv/mastersrv.h> - -#if defined(CONF_FAMILY_WINDOWS) - #define _WIN32_WINNT 0x0500 - #include <windows.h> -#endif - - -extern int register_process_packet(CNetChunk *pPacket); -extern int register_update(); - -static const char *str_ltrim(const char *str) -{ - while(*str && *str <= 32) - str++; - return str; -} - -static void str_rtrim(char *str) -{ - int i = str_length(str); - while(i >= 0) - { - if(str[i] > 32) - break; - str[i] = 0; - i--; - } -} - - -class CSnapIDPool -{ - enum - { - MAX_IDS = 16*1024, - }; - - class CID - { - public: - short m_Next; - short m_State; /* 0 = free, 1 = alloced, 2 = timed */ - int m_Timeout; - }; - - CID m_aIDs[MAX_IDS]; - - int m_FirstFree; - int m_FirstTimed; - int m_LastTimed; - int m_Usage; - int m_InUsage; - -public: - - CSnapIDPool() - { - Reset(); - } - - void Reset() - { - for(int i = 0; i < MAX_IDS; i++) - { - m_aIDs[i].m_Next = i+1; - m_aIDs[i].m_State = 0; - } - - m_aIDs[MAX_IDS-1].m_Next = -1; - m_FirstFree = 0; - m_FirstTimed = -1; - m_LastTimed = -1; - m_Usage = 0; - m_InUsage = 0; - } - - - void RemoveFirstTimeout() - { - int NextTimed = m_aIDs[m_FirstTimed].m_Next; - - /* add it to the free list */ - m_aIDs[m_FirstTimed].m_Next = m_FirstFree; - m_aIDs[m_FirstTimed].m_State = 0; - m_FirstFree = m_FirstTimed; - - /* remove it from the timed list */ - m_FirstTimed = NextTimed; - if(m_FirstTimed == -1) - m_LastTimed = -1; - - m_Usage--; - } - - int NewID() - { - int64 now = time_get(); - - /* process timed ids */ - while(m_FirstTimed != -1 && m_aIDs[m_FirstTimed].m_Timeout < now) - RemoveFirstTimeout(); - - int id = m_FirstFree; - dbg_assert(id != -1, "id error"); - m_FirstFree = m_aIDs[m_FirstFree].m_Next; - m_aIDs[id].m_State = 1; - m_Usage++; - m_InUsage++; - return id; - } - - void TimeoutIDs() - { - /* process timed ids */ - while(m_FirstTimed != -1) - RemoveFirstTimeout(); - } - - void FreeID(int id) - { - dbg_assert(m_aIDs[id].m_State == 1, "id is not alloced"); - - m_InUsage--; - m_aIDs[id].m_State = 2; - m_aIDs[id].m_Timeout = time_get()+time_freq()*5; - m_aIDs[id].m_Next = -1; - - if(m_LastTimed != -1) - { - m_aIDs[m_LastTimed].m_Next = id; - m_LastTimed = id; - } - else - { - m_FirstTimed = id; - m_LastTimed = id; - } - } -}; - -#if 0 -class IGameServer -{ -public: - /* - Function: mods_console_init - TODO - */ - virtual void ConsoleInit(); - - /* - Function: Init - Called when the server is started. - - Remarks: - It's called after the map is loaded so all map items are available. - */ - void Init() = 0; - - /* - Function: Shutdown - Called when the server quits. - - Remarks: - Should be used to clean up all resources used. - */ - void Shutdown() = 0; - - /* - Function: ClientEnter - Called when a client has joined the game. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - It's called when the client is finished loading and should enter gameplay. - */ - void ClientEnter(int cid) = 0; - - /* - Function: ClientDrop - Called when a client drops from the server. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS - */ - void ClientDrop(int cid) = 0; - - /* - Function: ClientDirectInput - Called when the server recives new input from a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - input - Pointer to the input data. - size - Size of the data. (NOT IMPLEMENTED YET) - */ - void ClientDirectInput(int cid, void *input) = 0; - - /* - Function: ClientPredictedInput - Called when the server applys the predicted input on the client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - input - Pointer to the input data. - size - Size of the data. (NOT IMPLEMENTED YET) - */ - void ClientPredictedInput(int cid, void *input) = 0; - - - /* - Function: Tick - Called with a regular interval to progress the gameplay. - - Remarks: - The SERVER_TICK_SPEED tells the number of ticks per second. - */ - void Tick() = 0; - - /* - Function: Presnap - Called before the server starts to construct snapshots for the clients. - */ - void Presnap() = 0; - - /* - Function: Snap - Called to create the snapshot for a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - The game should make a series of calls to <snap_new_item> to construct - the snapshot for the client. - */ - void Snap(int cid) = 0; - - /* - Function: PostSnap - Called after the server is done sending the snapshots. - */ - void PostSnap() = 0; - - /* - Function: ClientConnected - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> - */ - void ClientConnected(int client_id) = 0; - - - /* - Function: NetVersion - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> - */ - const char *NetVersion() = 0; - - /* - Function: Version - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> - */ - const char *Version() = 0; - - /* - Function: Message - TODO - - Arguments: - arg1 - desc - - Returns: - - See Also: - <other_func> - */ - void Message(int msg, int client_id) = 0; -}; - -#endif -/* -class IServer -{ -public: - BanAdd - BanRemove - TickSpeed - Tick - Kick - SetBrowseInfo - SetClientScore - SetClientName - GetClientInfo - LatestInput - ClientName - - SendMessage() - - Map - - virtual int NewSnapID() = 0; - virtual int FreeSnapID(int i) = 0; -};*/ - -class CServer -{ -public: - /* */ - class CClient - { - public: - - enum - { - STATE_EMPTY = 0, - STATE_AUTH, - STATE_CONNECTING, - STATE_READY, - STATE_INGAME, - - SNAPRATE_INIT=0, - SNAPRATE_FULL, - SNAPRATE_RECOVER - }; - - class CInput - { - public: - int m_aData[MAX_INPUT_SIZE]; - int m_GameTick; /* the tick that was chosen for the input */ - }; - - /* connection state info */ - int m_State; - int m_Latency; - int m_SnapRate; - - int m_LastAckedSnapshot; - int m_LastInputTick; - CSnapshotStorage m_Snapshots; - - CInput m_LatestInput; - CInput m_aInputs[200]; /* TODO: handle input better */ - int m_CurrentInput; - - char m_aName[MAX_NAME_LENGTH]; - char m_aClan[MAX_CLANNAME_LENGTH]; - int m_Score; - int m_Authed; - - void Reset() - { - /* reset input */ - for(int i = 0; i < 200; i++) - m_aInputs[i].m_GameTick = -1; - m_CurrentInput = 0; - mem_zero(&m_LatestInput, sizeof(m_LatestInput)); - - m_Snapshots.PurgeAll(); - m_LastAckedSnapshot = -1; - m_LastInputTick = -1; - m_SnapRate = CClient::SNAPRATE_INIT; - m_Score = 0; - } - }; - - CClient m_aClients[MAX_CLIENTS]; - - CSnapshotBuilder m_SnapshotBuilder; - CSnapIDPool m_IDPool; - CNetServer m_NetServer; - - int64 m_GameStartTime; - int m_CurrentTick; - int m_RunServer; - - char m_aBrowseinfoGametype[16]; - int m_BrowseinfoProgression; - - int64 m_Lastheartbeat; - /*static NETADDR4 master_server;*/ - - char m_aCurrentMap[64]; - int m_CurrentMapCrc; - unsigned char *m_pCurrentMapData; - int m_CurrentMapSize; - - CServer() - { - m_CurrentTick = 0; - m_RunServer = 1; - - mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype)); - m_BrowseinfoProgression = -1; - - m_pCurrentMapData = 0; - m_CurrentMapSize = 0; - } - - - int TrySetClientName(int ClientID, const char *pName) - { - int i; - char aTrimmedName[64]; - - /* trim the name */ - str_copy(aTrimmedName, str_ltrim(pName), sizeof(aTrimmedName)); - str_rtrim(aTrimmedName); - dbg_msg("", "'%s' -> '%s'", pName, aTrimmedName); - pName = aTrimmedName; - - - /* check for empty names */ - if(!pName[0]) - return -1; - - /* make sure that two clients doesn't have the same name */ - for(i = 0; i < MAX_CLIENTS; i++) - if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY) - { - if(strcmp(pName, m_aClients[i].m_aName) == 0) - return -1; - } - - /* set the client name */ - str_copy(m_aClients[ClientID].m_aName, pName, MAX_NAME_LENGTH); - return 0; - } - - - - void SetClientName(int ClientID, const char *pName) - { - if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) - return; - - if(!pName) - return; - - char aNameTry[MAX_NAME_LENGTH]; - str_copy(aNameTry, pName, MAX_NAME_LENGTH); - if(TrySetClientName(ClientID, aNameTry)) - { - /* auto rename */ - for(int i = 1;; i++) - { - str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, pName); - if(TrySetClientName(ClientID, aNameTry) == 0) - break; - } - } - } - - void SetClientScore(int ClientID, int Score) - { - if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) - return; - m_aClients[ClientID].m_Score = Score; - } - - void SetBrowseInfo(const char *pGameType, int Progression) - { - str_copy(m_aBrowseinfoGametype, pGameType, sizeof(m_aBrowseinfoGametype)); - m_BrowseinfoProgression = Progression; - if(m_BrowseinfoProgression > 100) - m_BrowseinfoProgression = 100; - if(m_BrowseinfoProgression < -1) - m_BrowseinfoProgression = -1; - } - - void Kick(int ClientID, const char *pReason) - { - if(ClientID < 0 || ClientID > MAX_CLIENTS) - return; - - if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY) - m_NetServer.Drop(ClientID, pReason); - } - - int Tick() - { - return m_CurrentTick; - } - - int64 TickStartTime(int Tick) - { - return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED; - } - - int TickSpeed() - { - return SERVER_TICK_SPEED; - } - - int Init() - { - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - m_aClients[i].m_State = CClient::STATE_EMPTY; - m_aClients[i].m_aName[0] = 0; - m_aClients[i].m_aClan[0] = 0; - m_aClients[i].m_Snapshots.Init(); - } - - m_CurrentTick = 0; - - return 0; - } - - int GetClientInfo(int ClientID, CLIENT_INFO *pInfo) - { - dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); - dbg_assert(pInfo != 0, "info can not be null"); - - if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) - { - pInfo->name = m_aClients[ClientID].m_aName; - pInfo->latency = m_aClients[ClientID].m_Latency; - return 1; - } - return 0; - } - - int SendMsg(int ClientID) - { - const MSG_INFO *pInfo = msg_get_info(); - CNetChunk Packet; - if(!pInfo) - return -1; - - mem_zero(&Packet, sizeof(CNetChunk)); - - Packet.m_ClientID = ClientID; - Packet.m_pData = pInfo->data; - Packet.m_DataSize = pInfo->size; - - if(pInfo->flags&MSGFLAG_VITAL) - Packet.m_Flags |= NETSENDFLAG_VITAL; - if(pInfo->flags&MSGFLAG_FLUSH) - Packet.m_Flags |= NETSENDFLAG_FLUSH; - - /* write message to demo recorder */ - if(!(pInfo->flags&MSGFLAG_NORECORD)) - demorec_record_message(pInfo->data, pInfo->size); - - if(!(pInfo->flags&MSGFLAG_NOSEND)) - { - if(ClientID == -1) - { - /* broadcast */ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - if(m_aClients[i].m_State == CClient::STATE_INGAME) - { - Packet.m_ClientID = i; - m_NetServer.Send(&Packet); - } - } - else - m_NetServer.Send(&Packet); - } - return 0; - } - - void DoSnapshot() - { - int i; - - { - static PERFORMACE_INFO scope = {"presnap", 0}; - perf_start(&scope); - mods_presnap(); - perf_end(); - } - - /* create snapshot for demo recording */ - if(demorec_isrecording()) - { - char data[CSnapshot::MAX_SIZE]; - int snapshot_size; - - /* build snap and possibly add some messages */ - m_SnapshotBuilder.Init(); - mods_snap(-1); - snapshot_size = m_SnapshotBuilder.Finish(data); - - /* write snapshot */ - demorec_record_snapshot(Tick(), data, snapshot_size); - } - - /* create snapshots for all clients */ - for(i = 0; i < MAX_CLIENTS; i++) - { - /* client must be ingame to recive snapshots */ - if(m_aClients[i].m_State != CClient::STATE_INGAME) - continue; - - /* this client is trying to recover, don't spam snapshots */ - if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0) - continue; - - /* this client is trying to recover, don't spam snapshots */ - if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0) - continue; - - { - char data[CSnapshot::MAX_SIZE]; - char deltadata[CSnapshot::MAX_SIZE]; - char compdata[CSnapshot::MAX_SIZE]; - int snapshot_size; - int crc; - static CSnapshot emptysnap; - CSnapshot *deltashot = &emptysnap; - int deltashot_size; - int delta_tick = -1; - int deltasize; - static PERFORMACE_INFO scope = {"build", 0}; - perf_start(&scope); - - m_SnapshotBuilder.Init(); - - { - static PERFORMACE_INFO scope = {"modsnap", 0}; - perf_start(&scope); - mods_snap(i); - perf_end(); - } - - /* finish snapshot */ - snapshot_size = m_SnapshotBuilder.Finish(data); - crc = ((CSnapshot*)data)->Crc(); - - /* remove old snapshos */ - /* keep 3 seconds worth of snapshots */ - m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentTick-SERVER_TICK_SPEED*3); - - /* save it the snapshot */ - m_aClients[i].m_Snapshots.Add(m_CurrentTick, time_get(), snapshot_size, data, 0); - - /* find snapshot that we can preform delta against */ - emptysnap.m_DataSize = 0; - emptysnap.m_NumItems = 0; - - { - deltashot_size = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &deltashot, 0); - if(deltashot_size >= 0) - delta_tick = m_aClients[i].m_LastAckedSnapshot; - else - { - /* no acked package found, force client to recover rate */ - if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_FULL) - m_aClients[i].m_SnapRate = CClient::SNAPRATE_RECOVER; - } - } - - /* create delta */ - { - static PERFORMACE_INFO scope = {"delta", 0}; - perf_start(&scope); - deltasize = CSnapshot::CreateDelta(deltashot, (CSnapshot*)data, deltadata); - perf_end(); - } - - - if(deltasize) - { - /* compress it */ - int snapshot_size; - const int max_size = MAX_SNAPSHOT_PACKSIZE; - int numpackets; - int n, left; - - { - static PERFORMACE_INFO scope = {"compress", 0}; - /*char buffer[512];*/ - perf_start(&scope); - snapshot_size = intpack_compress(deltadata, deltasize, compdata); - - /*str_hex(buffer, sizeof(buffer), compdata, snapshot_size); - dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/ - - perf_end(); - } - - numpackets = (snapshot_size+max_size-1)/max_size; - - for(n = 0, left = snapshot_size; left; n++) - { - int chunk = left < max_size ? left : max_size; - left -= chunk; - - if(numpackets == 1) - msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH); - else - msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH); - - msg_pack_int(m_CurrentTick); - msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */ - - if(numpackets != 1) - { - msg_pack_int(numpackets); - msg_pack_int(n); - } - - msg_pack_int(crc); - msg_pack_int(chunk); - msg_pack_raw(&compdata[n*max_size], chunk); - msg_pack_end(); - SendMsg(i); - } - } - else - { - msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH); - msg_pack_int(m_CurrentTick); - msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */ - msg_pack_end(); - SendMsg(i); - } - - perf_end(); - } - } - - mods_postsnap(); - } - - - static int NewClientCallback(int cid, void *pUser) - { - CServer *pThis = (CServer *)pUser; - pThis->m_aClients[cid].m_State = CClient::STATE_AUTH; - pThis->m_aClients[cid].m_aName[0] = 0; - pThis->m_aClients[cid].m_aClan[0] = 0; - pThis->m_aClients[cid].m_Authed = 0; - pThis->m_aClients[cid].Reset(); - return 0; - } - - static int DelClientCallback(int cid, void *pUser) - { - CServer *pThis = (CServer *)pUser; - - /* notify the mod about the drop */ - if(pThis->m_aClients[cid].m_State >= CClient::STATE_READY) - mods_client_drop(cid); - - pThis->m_aClients[cid].m_State = CClient::STATE_EMPTY; - pThis->m_aClients[cid].m_aName[0] = 0; - pThis->m_aClients[cid].m_aClan[0] = 0; - pThis->m_aClients[cid].m_Authed = 0; - pThis->m_aClients[cid].m_Snapshots.PurgeAll(); - return 0; - } - - void SendMap(int cid) - { - msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_string(config.sv_map, 0); - msg_pack_int(m_CurrentMapCrc); - msg_pack_end(); - server_send_msg(cid); - } - - void SendRconLine(int cid, const char *pLine) - { - msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); - msg_pack_string(pLine, 512); - msg_pack_end(); - server_send_msg(cid); - } - - static void SendRconLineAuthed(const char *pLine, void *pUser) - { - CServer *pThis = (CServer *)pUser; - static volatile int reentry_guard = 0; - int i; - - if(reentry_guard) return; - reentry_guard++; - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed) - pThis->SendRconLine(i, pLine); - } - - reentry_guard--; - } - - void ProcessClientPacket(CNetChunk *pPacket) - { - int cid = pPacket->m_ClientID; - NETADDR addr; - - int sys; - int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys); - - if(m_aClients[cid].m_State == CClient::STATE_AUTH) - { - if(sys && msg == NETMSG_INFO) - { - char version[64]; - const char *password; - str_copy(version, msg_unpack_string(), 64); - if(strcmp(version, mods_net_version()) != 0) - { - /* OH FUCK! wrong version, drop him */ - char reason[256]; - str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version); - m_NetServer.Drop(cid, reason); - return; - } - - str_copy(m_aClients[cid].m_aName, msg_unpack_string(), MAX_NAME_LENGTH); - str_copy(m_aClients[cid].m_aClan, msg_unpack_string(), MAX_CLANNAME_LENGTH); - password = msg_unpack_string(); - - if(config.password[0] != 0 && strcmp(config.password, password) != 0) - { - /* wrong password */ - m_NetServer.Drop(cid, "wrong password"); - return; - } - - m_aClients[cid].m_State = CClient::STATE_CONNECTING; - SendMap(cid); - } - } - else - { - if(sys) - { - /* system message */ - if(msg == NETMSG_REQUEST_MAP_DATA) - { - int chunk = msg_unpack_int(); - int chunk_size = 1024-128; - int offset = chunk * chunk_size; - int last = 0; - - /* drop faulty map data requests */ - if(chunk < 0 || offset > m_CurrentMapSize) - return; - - if(offset+chunk_size >= m_CurrentMapSize) - { - chunk_size = m_CurrentMapSize-offset; - if(chunk_size < 0) - chunk_size = 0; - last = 1; - } - - msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_int(last); - msg_pack_int(m_CurrentMapSize); - msg_pack_int(chunk_size); - msg_pack_raw(&m_pCurrentMapData[offset], chunk_size); - msg_pack_end(); - server_send_msg(cid); - - if(config.debug) - dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); - } - else if(msg == NETMSG_READY) - { - if(m_aClients[cid].m_State == CClient::STATE_CONNECTING) - { - addr = m_NetServer.ClientAddr(cid); - - dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d", - cid, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] - ); - m_aClients[cid].m_State = CClient::STATE_READY; - mods_connected(cid); - } - } - else if(msg == NETMSG_ENTERGAME) - { - if(m_aClients[cid].m_State == CClient::STATE_READY) - { - addr = m_NetServer.ClientAddr(cid); - - dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d", - cid, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] - ); - m_aClients[cid].m_State = CClient::STATE_INGAME; - mods_client_enter(cid); - } - } - else if(msg == NETMSG_INPUT) - { - int tick, size, i; - CClient::CInput *pInput; - int64 tagtime; - - m_aClients[cid].m_LastAckedSnapshot = msg_unpack_int(); - tick = msg_unpack_int(); - size = msg_unpack_int(); - - /* check for errors */ - if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) - return; - - if(m_aClients[cid].m_LastAckedSnapshot > 0) - m_aClients[cid].m_SnapRate = CClient::SNAPRATE_FULL; - - if(m_aClients[cid].m_Snapshots.Get(m_aClients[cid].m_LastAckedSnapshot, &tagtime, 0, 0) >= 0) - m_aClients[cid].m_Latency = (int)(((time_get()-tagtime)*1000)/time_freq()); - - /* add message to report the input timing */ - /* skip packets that are old */ - if(tick > m_aClients[cid].m_LastInputTick) - { - int time_left = ((TickStartTime(tick)-time_get())*1000) / time_freq(); - msg_pack_start_system(NETMSG_INPUTTIMING, 0); - msg_pack_int(tick); - msg_pack_int(time_left); - msg_pack_end(); - server_send_msg(cid); - } - - m_aClients[cid].m_LastInputTick = tick; - - pInput = &m_aClients[cid].m_aInputs[m_aClients[cid].m_CurrentInput]; - - if(tick <= server_tick()) - tick = server_tick()+1; - - pInput->m_GameTick = tick; - - for(i = 0; i < size/4; i++) - pInput->m_aData[i] = msg_unpack_int(); - - mem_copy(m_aClients[cid].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int)); - - m_aClients[cid].m_CurrentInput++; - m_aClients[cid].m_CurrentInput %= 200; - - /* call the mod with the fresh input data */ - if(m_aClients[cid].m_State == CClient::STATE_INGAME) - mods_client_direct_input(cid, m_aClients[cid].m_LatestInput.m_aData); - } - else if(msg == NETMSG_RCON_CMD) - { - const char *cmd = msg_unpack_string(); - - if(msg_unpack_error() == 0 && m_aClients[cid].m_Authed) - { - dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); - console_execute_line(cmd); - } - } - else if(msg == NETMSG_RCON_AUTH) - { - const char *pw; - msg_unpack_string(); /* login name, not used */ - pw = msg_unpack_string(); - - if(msg_unpack_error() == 0) - { - if(config.sv_rcon_password[0] == 0) - { - SendRconLine(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); - } - else if(strcmp(pw, config.sv_rcon_password) == 0) - { - msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL); - msg_pack_int(1); - msg_pack_end(); - server_send_msg(cid); - - m_aClients[cid].m_Authed = 1; - SendRconLine(cid, "Authentication successful. Remote console access granted."); - dbg_msg("server", "cid=%d authed", cid); - } - else - { - SendRconLine(cid, "Wrong password."); - } - } - } - else if(msg == NETMSG_PING) - { - msg_pack_start_system(NETMSG_PING_REPLY, 0); - msg_pack_end(); - server_send_msg(cid); - } - else - { - char hex[] = "0123456789ABCDEF"; - char buf[512]; - int b; - - for(b = 0; b < pPacket->m_DataSize && b < 32; b++) - { - buf[b*3] = hex[((const unsigned char *)pPacket->m_pData)[b]>>4]; - buf[b*3+1] = hex[((const unsigned char *)pPacket->m_pData)[b]&0xf]; - buf[b*3+2] = ' '; - buf[b*3+3] = 0; - } - - dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, pPacket->m_DataSize); - dbg_msg("server", "%s", buf); - - } - } - else - { - /* game message */ - if(m_aClients[cid].m_State >= CClient::STATE_READY) - mods_message(msg, cid); - } - } - } - - void SendServerInfo(NETADDR *addr, int token) - { - CNetChunk Packet; - CPacker p; - char buf[128]; - - /* count the players */ - int player_count = 0; - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(m_aClients[i].m_State != CClient::STATE_EMPTY) - player_count++; - } - - p.Reset(); - - if(token >= 0) - { - /* new token based format */ - p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); - str_format(buf, sizeof(buf), "%d", token); - p.AddString(buf, 6); - } - else - { - /* old format */ - p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)); - } - - p.AddString(mods_version(), 32); - p.AddString(config.sv_name, 64); - p.AddString(config.sv_map, 32); - - /* gametype */ - p.AddString(m_aBrowseinfoGametype, 16); - - /* flags */ - i = 0; - if(config.password[0]) /* password set */ - i |= SRVFLAG_PASSWORD; - str_format(buf, sizeof(buf), "%d", i); - p.AddString(buf, 2); - - /* progression */ - str_format(buf, sizeof(buf), "%d", m_BrowseinfoProgression); - p.AddString(buf, 4); - - str_format(buf, sizeof(buf), "%d", player_count); p.AddString(buf, 3); /* num players */ - str_format(buf, sizeof(buf), "%d", m_NetServer.MaxClients()); p.AddString(buf, 3); /* max players */ - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(m_aClients[i].m_State != CClient::STATE_EMPTY) - { - p.AddString(m_aClients[i].m_aName, 48); /* player name */ - str_format(buf, sizeof(buf), "%d", m_aClients[i].m_Score); p.AddString(buf, 6); /* player score */ - } - } - - - Packet.m_ClientID = -1; - Packet.m_Address = *addr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_DataSize = p.Size(); - Packet.m_pData = p.Data(); - m_NetServer.Send(&Packet); - } - - int BanAdd(NETADDR Addr, int Seconds) - { - return m_NetServer.BanAdd(Addr, Seconds); - } - - int BanRemove(NETADDR Addr) - { - return m_NetServer.BanRemove(Addr); - } - - - void PumpNetwork() - { - CNetChunk Packet; - - m_NetServer.Update(); - - /* process packets */ - while(m_NetServer.Recv(&Packet)) - { - if(Packet.m_ClientID == -1) - { - /* stateless */ - if(!register_process_packet(&Packet)) - { - if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && - memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) - { - SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); - } - - - if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && - memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) - { - SendServerInfo(&Packet.m_Address, -1); - } - } - } - else - ProcessClientPacket(&Packet); - } - } - - int LoadMap(const char *pMapName) - { - DATAFILE *df; - char buf[512]; - str_format(buf, sizeof(buf), "maps/%s.map", pMapName); - df = datafile_load(buf); - if(!df) - return 0; - - /* stop recording when we change map */ - demorec_record_stop(); - - /* reinit snapshot ids */ - m_IDPool.TimeoutIDs(); - - /* get the crc of the map */ - m_CurrentMapCrc = datafile_crc(buf); - dbg_msg("server", "%s crc is %08x", buf, m_CurrentMapCrc); - - str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap)); - map_set(df); - - /* load compelate map into memory for download */ - { - IOHANDLE file = engine_openfile(buf, IOFLAG_READ); - m_CurrentMapSize = (int)io_length(file); - if(m_pCurrentMapData) - mem_free(m_pCurrentMapData); - m_pCurrentMapData = (unsigned char *)mem_alloc(m_CurrentMapSize, 1); - io_read(file, m_pCurrentMapData, m_CurrentMapSize); - io_close(file); - } - return 1; - } - - int Run() - { - NETADDR bindaddr; - - //snap_init_id(); - net_init(); - - /* */ - console_register_print_callback(SendRconLineAuthed, this); - - /* load map */ - if(!LoadMap(config.sv_map)) - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - return -1; - } - - /* start server */ - /* TODO: IPv6 support */ - if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) - { - /* sweet! */ - bindaddr.port = config.sv_port; - } - else - { - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = config.sv_port; - } - - - if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0)) - { - dbg_msg("server", "couldn't open socket. port might already be in use"); - return -1; - } - - m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); - - dbg_msg("server", "server name is '%s'", config.sv_name); - - mods_init(); - dbg_msg("server", "version %s", mods_net_version()); - - /* start game */ - { - int64 reporttime = time_get(); - int reportinterval = 3; - - m_Lastheartbeat = 0; - m_GameStartTime = time_get(); - - if(config.debug) - dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); - - while(m_RunServer) - { - static PERFORMACE_INFO rootscope = {"root", 0}; - int64 t = time_get(); - int new_ticks = 0; - - perf_start(&rootscope); - - /* load new map TODO: don't poll this */ - if(strcmp(config.sv_map, m_aCurrentMap) != 0 || config.sv_map_reload) - { - config.sv_map_reload = 0; - - /* load map */ - if(LoadMap(config.sv_map)) - { - int c; - - /* new map loaded */ - mods_shutdown(); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(m_aClients[c].m_State == CClient::STATE_EMPTY) - continue; - - SendMap(c); - m_aClients[c].Reset(); - m_aClients[c].m_State = CClient::STATE_CONNECTING; - } - - m_GameStartTime = time_get(); - m_CurrentTick = 0; - mods_init(); - } - else - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - config_set_sv_map(&config, m_aCurrentMap); - } - } - - while(t > TickStartTime(m_CurrentTick+1)) - { - m_CurrentTick++; - new_ticks++; - - /* apply new input */ - { - static PERFORMACE_INFO scope = {"input", 0}; - int c, i; - - perf_start(&scope); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(m_aClients[c].m_State == CClient::STATE_EMPTY) - continue; - for(i = 0; i < 200; i++) - { - if(m_aClients[c].m_aInputs[i].m_GameTick == server_tick()) - { - if(m_aClients[c].m_State == CClient::STATE_INGAME) - mods_client_predicted_input(c, m_aClients[c].m_aInputs[i].m_aData); - break; - } - } - } - - perf_end(); - } - - /* progress game */ - { - static PERFORMACE_INFO scope = {"tick", 0}; - perf_start(&scope); - mods_tick(); - perf_end(); - } - } - - /* snap game */ - if(new_ticks) - { - if(config.sv_high_bandwidth || (m_CurrentTick%2) == 0) - { - static PERFORMACE_INFO scope = {"snap", 0}; - perf_start(&scope); - DoSnapshot(); - perf_end(); - } - } - - /* master server stuff */ - register_update(); - - - { - static PERFORMACE_INFO scope = {"net", 0}; - perf_start(&scope); - PumpNetwork(); - perf_end(); - } - - perf_end(); - - if(reporttime < time_get()) - { - if(config.debug) - { - /* - static NETSTATS prev_stats; - NETSTATS stats; - netserver_stats(net, &stats); - - perf_next(); - - if(config.dbg_pref) - perf_dump(&rootscope); - - dbg_msg("server", "send=%8d recv=%8d", - (stats.send_bytes - prev_stats.send_bytes)/reportinterval, - (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); - - prev_stats = stats; - */ - } - - reporttime += time_freq()*reportinterval; - } - - /* wait for incomming data */ - net_socket_read_wait(m_NetServer.Socket(), 5); - } - } - - mods_shutdown(); - map_unload(); - - if(m_pCurrentMapData) - mem_free(m_pCurrentMapData); - return 0; - } - - static void ConKick(void *pResult, void *pUser) - { - ((CServer *)pUser)->Kick(console_arg_int(pResult, 0), "kicked by console"); - } - - static int str_allnum(const char *str) - { - while(*str) - { - if(!(*str >= '0' && *str <= '9')) - return 0; - str++; - } - return 1; - } - - static void ConBan(void *pResult, void *pUser) - { - NETADDR addr; - char addrstr[128]; - const char *str = console_arg_string(pResult, 0); - int minutes = 30; - - if(console_arg_num(pResult) > 1) - minutes = console_arg_int(pResult, 1); - - if(net_addr_from_str(&addr, str) == 0) - ((CServer *)pUser)->BanAdd(addr, minutes*60); - else if(str_allnum(str)) - { - NETADDR addr; - int cid = atoi(str); - - if(cid < 0 || cid > MAX_CLIENTS || ((CServer *)pUser)->m_aClients[cid].m_State == CClient::STATE_EMPTY) - { - dbg_msg("server", "invalid client id"); - return; - } - - addr = ((CServer *)pUser)->m_NetServer.ClientAddr(cid); - ((CServer *)pUser)->BanAdd(addr, minutes*60); - } - - addr.port = 0; - net_addr_str(&addr, addrstr, sizeof(addrstr)); - - if(minutes) - dbg_msg("server", "banned %s for %d minutes", addrstr, minutes); - else - dbg_msg("server", "banned %s for life", addrstr); - } - - static void ConUnban(void *result, void *pUser) - { - NETADDR addr; - const char *str = console_arg_string(result, 0); - - if(net_addr_from_str(&addr, str) == 0) - ((CServer *)pUser)->BanRemove(addr); - else - dbg_msg("server", "invalid network address"); - } - - static void ConBans(void *pResult, void *pUser) - { - unsigned now = time_timestamp(); - - int num = ((CServer *)pUser)->m_NetServer.BanNum(); - for(int i = 0; i < num; i++) - { - CNetServer::CBanInfo Info; - ((CServer *)pUser)->m_NetServer.BanGet(i, &Info); - NETADDR Addr = Info.m_Addr; - - if(Info.m_Expires == -1) - { - dbg_msg("server", "#%d %d.%d.%d.%d for life", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); - } - else - { - unsigned t = Info.m_Expires - now; - dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], t/60, t%60); - } - } - dbg_msg("server", "%d ban(s)", num); - } - - static void ConStatus(void *pResult, void *pUser) - { - int i; - NETADDR addr; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(((CServer *)pUser)->m_aClients[i].m_State == CClient::STATE_INGAME) - { - addr = ((CServer *)pUser)->m_NetServer.ClientAddr(i); - dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d", - i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port, - ((CServer *)pUser)->m_aClients[i].m_aName, ((CServer *)pUser)->m_aClients[i].m_Score); - } - } - } - - static void ConShutdown(void *pResult, void *pUser) - { - ((CServer *)pUser)->m_RunServer = 0; - } - - static void ConRecord(void *pResult, void *pUser) - { - char filename[512]; - str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(pResult, 0)); - demorec_record_start(filename, mods_net_version(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server"); - } - - static void ConStopRecord(void *pResult, void *pUser) - { - demorec_record_stop(); - } - - - void RegisterCommands() - { - MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, ConKick, this, ""); - MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, ConBan, this, ""); - MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, ConUnban, this, ""); - MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, ConBans, this, ""); - MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, ConStatus, this, ""); - MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, ""); - - MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, ConRecord, this, ""); - MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, ""); - } - -}; - - -// UGLY UGLY HACK for now -CServer g_Server; -CNetServer *m_pNetServer; - -int server_tick() { return g_Server.Tick(); } -int server_tickspeed() { return g_Server.TickSpeed(); } -int snap_new_id() { return g_Server.m_IDPool.NewID(); } -void snap_free_id(int id) { return g_Server.m_IDPool.FreeID(id); } -int server_send_msg(int client_id) { return g_Server.SendMsg(client_id); } -void server_setbrowseinfo(const char *game_type, int progression) { g_Server.SetBrowseInfo(game_type, progression); } -void server_setclientname(int client_id, const char *name) { g_Server.SetClientName(client_id, name); } -void server_setclientscore(int client_id, int score) { g_Server.SetClientScore(client_id, score); } - -int server_getclientinfo(int client_id, CLIENT_INFO *info) { return g_Server.GetClientInfo(client_id, info); } - -void *snap_new_item(int type, int id, int size) -{ - dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); - dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); - return g_Server.m_SnapshotBuilder.NewItem(type, id, size); -} - -int *server_latestinput(int client_id, int *size) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY) - return 0; - return g_Server.m_aClients[client_id].m_LatestInput.m_aData; -} - -const char *server_clientname(int client_id) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY) - return "(invalid client)"; - return g_Server.m_aClients[client_id].m_aName; -} - -#if 0 - - -static void reset_client(int cid) -{ - /* reset input */ - int i; - for(i = 0; i < 200; i++) - m_aClients[cid].m_aInputs[i].m_GameTick = -1; - m_aClients[cid].m_CurrentInput = 0; - mem_zero(&m_aClients[cid].m_Latestinput, sizeof(m_aClients[cid].m_Latestinput)); - - m_aClients[cid].m_Snapshots.PurgeAll(); - m_aClients[cid].m_LastAckedSnapshot = -1; - m_aClients[cid].m_LastInputTick = -1; - m_aClients[cid].m_SnapRate = CClient::SNAPRATE_INIT; - m_aClients[cid].m_Score = 0; - -} - - -static int new_client_callback(int cid, void *user) -{ - m_aClients[cid].state = CClient::STATE_AUTH; - m_aClients[cid].name[0] = 0; - m_aClients[cid].clan[0] = 0; - m_aClients[cid].authed = 0; - reset_client(cid); - return 0; -} - -static int del_client_callback(int cid, void *user) -{ - /* notify the mod about the drop */ - if(m_aClients[cid].state >= CClient::STATE_READY) - mods_client_drop(cid); - - m_aClients[cid].state = CClient::STATE_EMPTY; - m_aClients[cid].name[0] = 0; - m_aClients[cid].clan[0] = 0; - m_aClients[cid].authed = 0; - m_aClients[cid].snapshots.PurgeAll(); - return 0; -} -static void server_send_map(int cid) -{ - msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_string(config.sv_map, 0); - msg_pack_int(current_map_crc); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_rcon_line(int cid, const char *line) -{ - msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); - msg_pack_string(line, 512); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_rcon_line_authed(const char *line, void *user_data) -{ - static volatile int reentry_guard = 0; - int i; - - if(reentry_guard) return; - reentry_guard++; - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(m_aClients[i].state != CClient::STATE_EMPTY && m_aClients[i].authed) - server_send_rcon_line(i, line); - } - - reentry_guard--; -} - -static void server_pump_network() -{ - CNetChunk Packet; - - m_NetServer.Update(); - - /* process packets */ - while(m_NetServer.Recv(&Packet)) - { - if(Packet.m_ClientID == -1) - { - /* stateless */ - if(!register_process_packet(&Packet)) - { - if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && - memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) - { - server_send_serverinfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); - } - - - if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && - memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) - { - server_send_serverinfo(&Packet.m_Address, -1); - } - } - } - else - server_process_client_packet(&Packet); - } -} - -static int server_load_map(const char *mapname) -{ - DATAFILE *df; - char buf[512]; - str_format(buf, sizeof(buf), "maps/%s.map", mapname); - df = datafile_load(buf); - if(!df) - return 0; - - /* stop recording when we change map */ - demorec_record_stop(); - - /* reinit snapshot ids */ - snap_timeout_ids(); - - /* get the crc of the map */ - current_map_crc = datafile_crc(buf); - dbg_msg("server", "%s crc is %08x", buf, current_map_crc); - - str_copy(current_map, mapname, sizeof(current_map)); - map_set(df); - - /* load compelate map into memory for download */ - { - IOHANDLE file = engine_openfile(buf, IOFLAG_READ); - current_map_size = (int)io_length(file); - if(current_map_data) - mem_free(current_map_data); - current_map_data = (unsigned char *)mem_alloc(current_map_size, 1); - io_read(file, current_map_data, current_map_size); - io_close(file); - } - return 1; -} - -static int server_run() -{ - NETADDR bindaddr; - - snap_init_id(); - net_init(); - - /* */ - console_register_print_callback(server_send_rcon_line_authed, 0); - - /* load map */ - if(!server_load_map(config.sv_map)) - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - return -1; - } - - /* start server */ - /* TODO: IPv6 support */ - if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) - { - /* sweet! */ - bindaddr.port = config.sv_port; - } - else - { - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = config.sv_port; - } - - - if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0)) - { - dbg_msg("server", "couldn't open socket. port might already be in use"); - return -1; - } - - m_NetServer.SetCallbacks(new_client_callback, del_client_callback, this); - - dbg_msg("server", "server name is '%s'", config.sv_name); - - mods_init(); - dbg_msg("server", "version %s", mods_net_version()); - - /* start game */ - { - int64 reporttime = time_get(); - int reportinterval = 3; - - lastheartbeat = 0; - game_start_time = time_get(); - - if(config.debug) - dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); - - while(run_server) - { - static PERFORMACE_INFO rootscope = {"root", 0}; - int64 t = time_get(); - int new_ticks = 0; - - perf_start(&rootscope); - - /* load new map TODO: don't poll this */ - if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) - { - config.sv_map_reload = 0; - - /* load map */ - if(server_load_map(config.sv_map)) - { - int c; - - /* new map loaded */ - mods_shutdown(); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(m_aClients[c].state == CClient::STATE_EMPTY) - continue; - - server_send_map(c); - reset_client(c); - m_aClients[c].state = CClient::STATE_CONNECTING; - } - - game_start_time = time_get(); - current_tick = 0; - mods_init(); - } - else - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - config_set_sv_map(&config, current_map); - } - } - - while(t > server_tick_start_time(current_tick+1)) - { - current_tick++; - new_ticks++; - - /* apply new input */ - { - static PERFORMACE_INFO scope = {"input", 0}; - int c, i; - - perf_start(&scope); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(m_aClients[c].state == CClient::STATE_EMPTY) - continue; - for(i = 0; i < 200; i++) - { - if(m_aClients[c].inputs[i].game_tick == server_tick()) - { - if(m_aClients[c].state == CClient::STATE_INGAME) - mods_client_predicted_input(c, m_aClients[c].inputs[i].data); - break; - } - } - } - - perf_end(); - } - - /* progress game */ - { - static PERFORMACE_INFO scope = {"tick", 0}; - perf_start(&scope); - mods_tick(); - perf_end(); - } - } - - /* snap game */ - if(new_ticks) - { - if(config.sv_high_bandwidth || (current_tick%2) == 0) - { - static PERFORMACE_INFO scope = {"snap", 0}; - perf_start(&scope); - server_do_snap(); - perf_end(); - } - } - - /* master server stuff */ - register_update(); - - - { - static PERFORMACE_INFO scope = {"net", 0}; - perf_start(&scope); - server_pump_network(); - perf_end(); - } - - perf_end(); - - if(reporttime < time_get()) - { - if(config.debug) - { - /* - static NETSTATS prev_stats; - NETSTATS stats; - netserver_stats(net, &stats); - - perf_next(); - - if(config.dbg_pref) - perf_dump(&rootscope); - - dbg_msg("server", "send=%8d recv=%8d", - (stats.send_bytes - prev_stats.send_bytes)/reportinterval, - (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); - - prev_stats = stats; - */ - } - - reporttime += time_freq()*reportinterval; - } - - /* wait for incomming data */ - net_socket_read_wait(m_NetServer.Socket(), 5); - } - } - - mods_shutdown(); - map_unload(); - - if(current_map_data) - mem_free(current_map_data); - return 0; -} - - -#endif - -int main(int argc, char **argv) -{ - - m_pNetServer = &g_Server.m_NetServer; -#if defined(CONF_FAMILY_WINDOWS) - int i; - for(i = 1; i < argc; i++) - { - if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0) - { - ShowWindow(GetConsoleWindow(), SW_HIDE); - break; - } - } -#endif - - /* init the engine */ - dbg_msg("server", "starting..."); - engine_init("Teeworlds"); - - /* register all console commands */ - - g_Server.RegisterCommands(); - mods_console_init(); - - /* parse the command line arguments */ - engine_parse_arguments(argc, argv); - - /* run the server */ - g_Server.Run(); - return 0; -} - diff --git a/src/engine/server/register.cpp b/src/engine/server/register.cpp new file mode 100644 index 00000000..959b9288 --- /dev/null +++ b/src/engine/server/register.cpp @@ -0,0 +1,264 @@ +#include <base/system.h> +#include <engine/shared/network.h> +#include <engine/shared/config.h> +#include <engine/shared/engine.h> +#include <engine/masterserver.h> + +#include <mastersrv/mastersrv.h> + +#include "register.h" + +CRegister::CRegister() +{ + m_pNetServer = 0; + m_pMasterServer = 0; + + m_RegisterState = REGISTERSTATE_START; + m_RegisterStateStart = 0; + m_RegisterFirst = 1; + m_RegisterCount = 0; + + mem_zero(m_aMasterserverInfo, sizeof(m_aMasterserverInfo)); + m_RegisterRegisteredServer = -1; +} + +void CRegister::RegisterNewState(int State) +{ + m_RegisterState = State; + m_RegisterStateStart = time_get(); +} + +void CRegister::RegisterSendFwcheckresponse(NETADDR *pAddr) +{ + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = *pAddr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE); + Packet.m_pData = SERVERBROWSE_FWRESPONSE; + m_pNetServer->Send(&Packet); +} + +void CRegister::RegisterSendHeartbeat(NETADDR Addr) +{ + static unsigned char aData[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; + unsigned short Port = g_Config.m_SvPort; + CNetChunk Packet; + + mem_copy(aData, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); + + Packet.m_ClientID = -1; + Packet.m_Address = Addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2; + Packet.m_pData = &aData; + + // supply the set port that the master can use if it has problems + if(g_Config.m_SvExternalPort) + Port = g_Config.m_SvExternalPort; + aData[sizeof(SERVERBROWSE_HEARTBEAT)] = Port >> 8; + aData[sizeof(SERVERBROWSE_HEARTBEAT)+1] = Port&0xff; + m_pNetServer->Send(&Packet); +} + +void CRegister::RegisterSendCountRequest(NETADDR Addr) +{ + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = Addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT); + Packet.m_pData = SERVERBROWSE_GETCOUNT; + m_pNetServer->Send(&Packet); +} + +void CRegister::RegisterGotCount(CNetChunk *pChunk) +{ + unsigned char *pData = (unsigned char *)pChunk->m_pData; + int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1]; + + for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + if(net_addr_comp(&m_aMasterserverInfo[i].m_Addr, &pChunk->m_Address) == 0) + { + m_aMasterserverInfo[i].m_Count = Count; + break; + } + } +} + +void CRegister::Init(CNetServer *pNetServer, IEngineMasterServer *pMasterServer) +{ + m_pNetServer = pNetServer; + m_pMasterServer = pMasterServer; +} + +void CRegister::RegisterUpdate() +{ + int64 Now = time_get(); + int64 Freq = time_freq(); + + if(!g_Config.m_SvRegister) + return; + + m_pMasterServer->Update(); + + if(m_RegisterState == REGISTERSTATE_START) + { + m_RegisterCount = 0; + m_RegisterFirst = 1; + RegisterNewState(REGISTERSTATE_UPDATE_ADDRS); + m_pMasterServer->RefreshAddresses(); + dbg_msg("register", "refreshing ip addresses"); + } + else if(m_RegisterState == REGISTERSTATE_UPDATE_ADDRS) + { + m_RegisterRegisteredServer = -1; + + if(!m_pMasterServer->IsRefreshing()) + { + int i; + for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + NETADDR addr = m_pMasterServer->GetAddr(i); + m_aMasterserverInfo[i].m_Addr = addr; + m_aMasterserverInfo[i].m_Count = 0; + + if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) + m_aMasterserverInfo[i].m_Valid = 0; + else + { + m_aMasterserverInfo[i].m_Valid = 1; + m_aMasterserverInfo[i].m_Count = -1; + m_aMasterserverInfo[i].m_LastSend = 0; + } + } + + dbg_msg("register", "fetching server counts"); + RegisterNewState(REGISTERSTATE_QUERY_COUNT); + } + } + else if(m_RegisterState == REGISTERSTATE_QUERY_COUNT) + { + int Left = 0; + for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + if(!m_aMasterserverInfo[i].m_Valid) + continue; + + if(m_aMasterserverInfo[i].m_Count == -1) + { + Left++; + if(m_aMasterserverInfo[i].m_LastSend+Freq < Now) + { + m_aMasterserverInfo[i].m_LastSend = Now; + RegisterSendCountRequest(m_aMasterserverInfo[i].m_Addr); + } + } + } + + // check if we are done or timed out + if(Left == 0 || Now > m_RegisterStateStart+Freq*3) + { + // choose server + int Best = -1; + int i; + for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++) + { + if(!m_aMasterserverInfo[i].m_Valid || m_aMasterserverInfo[i].m_Count == -1) + continue; + + if(Best == -1 || m_aMasterserverInfo[i].m_Count < m_aMasterserverInfo[Best].m_Count) + Best = i; + } + + // server chosen + m_RegisterRegisteredServer = Best; + if(m_RegisterRegisteredServer == -1) + { + dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds"); + RegisterNewState(REGISTERSTATE_ERROR); + } + else + { + dbg_msg("register", "choosen '%s' as master, sending heartbeats", m_pMasterServer->GetName(m_RegisterRegisteredServer)); + m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = 0; + RegisterNewState(REGISTERSTATE_HEARTBEAT); + } + } + } + else if(m_RegisterState == REGISTERSTATE_HEARTBEAT) + { + // check if we should send heartbeat + if(Now > m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend+Freq*15) + { + m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = Now; + RegisterSendHeartbeat(m_aMasterserverInfo[m_RegisterRegisteredServer].m_Addr); + } + + if(Now > m_RegisterStateStart+Freq*60) + { + dbg_msg("register", "WARNING: Master server is not responding, switching master"); + RegisterNewState(REGISTERSTATE_START); + } + } + else if(m_RegisterState == REGISTERSTATE_REGISTERED) + { + if(m_RegisterFirst) + dbg_msg("register", "server registered"); + + m_RegisterFirst = 0; + + // check if we should send new heartbeat again + if(Now > m_RegisterStateStart+Freq) + { + if(m_RegisterCount == 120) // redo the whole process after 60 minutes to balance out the master servers + RegisterNewState(REGISTERSTATE_START); + else + { + m_RegisterCount++; + RegisterNewState(REGISTERSTATE_HEARTBEAT); + } + } + } + else if(m_RegisterState == REGISTERSTATE_ERROR) + { + // check for restart + if(Now > m_RegisterStateStart+Freq*60) + RegisterNewState(REGISTERSTATE_START); + } +} + +int CRegister::RegisterProcessPacket(CNetChunk *pPacket) +{ + if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) && + mem_comp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) + { + RegisterSendFwcheckresponse(&pPacket->m_Address); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) && + mem_comp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) + { + if(m_RegisterFirst) + dbg_msg("register", "no firewall/nat problems detected"); + RegisterNewState(REGISTERSTATE_REGISTERED); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) && + mem_comp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) + { + dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server."); + dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", g_Config.m_SvPort); + RegisterNewState(REGISTERSTATE_ERROR); + return 1; + } + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 && + mem_comp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) + { + RegisterGotCount(pPacket); + return 1; + } + + return 0; +} diff --git a/src/engine/server/register.h b/src/engine/server/register.h new file mode 100644 index 00000000..a800ec1e --- /dev/null +++ b/src/engine/server/register.h @@ -0,0 +1,48 @@ +#ifndef ENGINE_SERVER_REGISTER_H +#define ENGINE_SERVER_REGISTER_H + +class CRegister +{ + enum + { + REGISTERSTATE_START=0, + REGISTERSTATE_UPDATE_ADDRS, + REGISTERSTATE_QUERY_COUNT, + REGISTERSTATE_HEARTBEAT, + REGISTERSTATE_REGISTERED, + REGISTERSTATE_ERROR + }; + + struct CMasterserverInfo + { + NETADDR m_Addr; + int m_Count; + int m_Valid; + int64 m_LastSend; + }; + + class CNetServer *m_pNetServer; + class IEngineMasterServer *m_pMasterServer; + + int m_RegisterState; + int64 m_RegisterStateStart; + int m_RegisterFirst; + int m_RegisterCount; + + class CMasterserverInfo m_aMasterserverInfo[IMasterServer::MAX_MASTERSERVERS]; + int m_RegisterRegisteredServer; + + void RegisterNewState(int State); + void RegisterSendFwcheckresponse(NETADDR *pAddr); + void RegisterSendHeartbeat(NETADDR Addr); + void RegisterSendCountRequest(NETADDR Addr); + void RegisterGotCount(class CNetChunk *pChunk); + +public: + CRegister(); + void Init(class CNetServer *pNetServer, class IEngineMasterServer *pMasterServer); + void RegisterUpdate(); + int RegisterProcessPacket(class CNetChunk *pPacket); +}; + +#endif diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp new file mode 100644 index 00000000..45cec1e4 --- /dev/null +++ b/src/engine/server/server.cpp @@ -0,0 +1,1400 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info + +#include <base/system.h> + +#include <engine/shared/config.h> +#include <engine/shared/engine.h> + +#include <engine/shared/protocol.h> +#include <engine/shared/snapshot.h> + +#include <engine/shared/compression.h> + +#include <engine/shared/network.h> +#include <engine/shared/config.h> +#include <engine/shared/packer.h> +#include <engine/shared/datafile.h> +#include <engine/shared/demorec.h> + +#include <engine/server.h> +#include <engine/map.h> +#include <engine/console.h> +#include <engine/storage.h> +#include <engine/masterserver.h> +#include <engine/config.h> + +#include <mastersrv/mastersrv.h> + +#include "register.h" +#include "server.h" + +#if defined(CONF_FAMILY_WINDOWS) + #define _WIN32_WINNT 0x0500 + #define NOGDI + #include <windows.h> +#endif + +static const char *StrLtrim(const char *pStr) +{ + while(*pStr && *pStr <= 32) + pStr++; + return pStr; +} + +static void StrRtrim(char *pStr) +{ + int i = str_length(pStr); + while(i >= 0) + { + if(pStr[i] > 32) + break; + pStr[i] = 0; + i--; + } +} + + +static int StrAllnum(const char *pStr) +{ + while(*pStr) + { + if(!(*pStr >= '0' && *pStr <= '9')) + return 0; + pStr++; + } + return 1; +} + +CSnapIDPool::CSnapIDPool() +{ + Reset(); +} + +void CSnapIDPool::Reset() +{ + for(int i = 0; i < MAX_IDS; i++) + { + m_aIDs[i].m_Next = i+1; + m_aIDs[i].m_State = 0; + } + + m_aIDs[MAX_IDS-1].m_Next = -1; + m_FirstFree = 0; + m_FirstTimed = -1; + m_LastTimed = -1; + m_Usage = 0; + m_InUsage = 0; +} + + +void CSnapIDPool::RemoveFirstTimeout() +{ + int NextTimed = m_aIDs[m_FirstTimed].m_Next; + + // add it to the free list + m_aIDs[m_FirstTimed].m_Next = m_FirstFree; + m_aIDs[m_FirstTimed].m_State = 0; + m_FirstFree = m_FirstTimed; + + // remove it from the timed list + m_FirstTimed = NextTimed; + if(m_FirstTimed == -1) + m_LastTimed = -1; + + m_Usage--; +} + +int CSnapIDPool::NewID() +{ + int64 Now = time_get(); + + // process timed ids + while(m_FirstTimed != -1 && m_aIDs[m_FirstTimed].m_Timeout < Now) + RemoveFirstTimeout(); + + int Id = m_FirstFree; + dbg_assert(Id != -1, "id error"); + m_FirstFree = m_aIDs[m_FirstFree].m_Next; + m_aIDs[Id].m_State = 1; + m_Usage++; + m_InUsage++; + return Id; +} + +void CSnapIDPool::TimeoutIDs() +{ + // process timed ids + while(m_FirstTimed != -1) + RemoveFirstTimeout(); +} + +void CSnapIDPool::FreeID(int Id) +{ + dbg_assert(m_aIDs[Id].m_State == 1, "id is not alloced"); + + m_InUsage--; + m_aIDs[Id].m_State = 2; + m_aIDs[Id].m_Timeout = time_get()+time_freq()*5; + m_aIDs[Id].m_Next = -1; + + if(m_LastTimed != -1) + { + m_aIDs[m_LastTimed].m_Next = Id; + m_LastTimed = Id; + } + else + { + m_FirstTimed = Id; + m_LastTimed = Id; + } +} + +void CServer::CClient::Reset() +{ + // reset input + for(int i = 0; i < 200; i++) + m_aInputs[i].m_GameTick = -1; + m_CurrentInput = 0; + mem_zero(&m_LatestInput, sizeof(m_LatestInput)); + + m_Snapshots.PurgeAll(); + m_LastAckedSnapshot = -1; + m_LastInputTick = -1; + m_SnapRate = CClient::SNAPRATE_INIT; + m_Score = 0; +} + +CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta) +{ + m_TickSpeed = SERVER_TICK_SPEED; + + m_pGameServer = 0; + + m_CurrentGameTick = 0; + m_RunServer = 1; + + mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype)); + m_BrowseinfoProgression = -1; + + m_pCurrentMapData = 0; + m_CurrentMapSize = 0; + + Init(); +} + + +int CServer::TrySetClientName(int ClientID, const char *pName) +{ + char aTrimmedName[64]; + + // trim the name + str_copy(aTrimmedName, StrLtrim(pName), sizeof(aTrimmedName)); + StrRtrim(aTrimmedName); + dbg_msg("", "'%s' -> '%s'", pName, aTrimmedName); + pName = aTrimmedName; + + + // check for empty names + if(!pName[0]) + return -1; + + // make sure that two clients doesn't have the same name + for(int i = 0; i < MAX_CLIENTS; i++) + if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY) + { + if(str_comp(pName, m_aClients[i].m_aName) == 0) + return -1; + } + + // set the client name + str_copy(m_aClients[ClientID].m_aName, pName, MAX_NAME_LENGTH); + return 0; +} + + + +void CServer::SetClientName(int ClientID, const char *pName) +{ + if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) + return; + + if(!pName) + return; + + char aNameTry[MAX_NAME_LENGTH]; + str_copy(aNameTry, pName, MAX_NAME_LENGTH); + if(TrySetClientName(ClientID, aNameTry)) + { + // auto rename + for(int i = 1;; i++) + { + str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, pName); + if(TrySetClientName(ClientID, aNameTry) == 0) + break; + } + } +} + +void CServer::SetClientScore(int ClientID, int Score) +{ + if(ClientID < 0 || ClientID >= MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) + return; + m_aClients[ClientID].m_Score = Score; +} + +void CServer::SetBrowseInfo(const char *pGameType, int Progression) +{ + str_copy(m_aBrowseinfoGametype, pGameType, sizeof(m_aBrowseinfoGametype)); + m_BrowseinfoProgression = Progression; + if(m_BrowseinfoProgression > 100) + m_BrowseinfoProgression = 100; + if(m_BrowseinfoProgression < -1) + m_BrowseinfoProgression = -1; +} + +void CServer::Kick(int ClientID, const char *pReason) +{ + if(ClientID < 0 || ClientID >= MAX_CLIENTS) + return; + + if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY) + m_NetServer.Drop(ClientID, pReason); +} + +/*int CServer::Tick() +{ + return m_CurrentGameTick; +}*/ + +int64 CServer::TickStartTime(int Tick) +{ + return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED; +} + +/*int CServer::TickSpeed() +{ + return SERVER_TICK_SPEED; +}*/ + +int CServer::Init() +{ + for(int i = 0; i < MAX_CLIENTS; i++) + { + m_aClients[i].m_State = CClient::STATE_EMPTY; + m_aClients[i].m_aName[0] = 0; + m_aClients[i].m_aClan[0] = 0; + m_aClients[i].m_Snapshots.Init(); + } + + m_CurrentGameTick = 0; + + return 0; +} + +int CServer::GetClientInfo(int ClientID, CClientInfo *pInfo) +{ + dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); + dbg_assert(pInfo != 0, "info can not be null"); + + if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) + { + pInfo->m_pName = m_aClients[ClientID].m_aName; + pInfo->m_Latency = m_aClients[ClientID].m_Latency; + return 1; + } + return 0; +} + +void CServer::GetClientIP(int ClientID, char *pIPString, int Size) +{ + if(ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_INGAME) + { + NETADDR Addr = m_NetServer.ClientAddr(ClientID); + str_format(pIPString, Size, "%d.%d.%d.%d", Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); + } +} + + +int *CServer::LatestInput(int ClientId, int *size) +{ + if(ClientId < 0 || ClientId >= MAX_CLIENTS || m_aClients[ClientId].m_State < CServer::CClient::STATE_READY) + return 0; + return m_aClients[ClientId].m_LatestInput.m_aData; +} + +const char *CServer::ClientName(int ClientId) +{ + if(ClientId < 0 || ClientId >= MAX_CLIENTS || m_aClients[ClientId].m_State < CServer::CClient::STATE_READY) + return "(invalid client)"; + return m_aClients[ClientId].m_aName; +} + +bool CServer::ClientIngame(int ClientID) +{ + return ClientID >= 0 && ClientID < MAX_CLIENTS && m_aClients[ClientID].m_State == CServer::CClient::STATE_INGAME; +} + +int CServer::SendMsg(CMsgPacker *pMsg, int Flags, int ClientId) +{ + return SendMsgEx(pMsg, Flags, ClientId, false); +} + +int CServer::SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System) +{ + CNetChunk Packet; + if(!pMsg) + return -1; + + mem_zero(&Packet, sizeof(CNetChunk)); + + Packet.m_ClientID = ClientID; + Packet.m_pData = pMsg->Data(); + Packet.m_DataSize = pMsg->Size(); + + // HACK: modify the message id in the packet and store the system flag + *((unsigned char*)Packet.m_pData) <<= 1; + if(System) + *((unsigned char*)Packet.m_pData) |= 1; + + if(Flags&MSGFLAG_VITAL) + Packet.m_Flags |= NETSENDFLAG_VITAL; + if(Flags&MSGFLAG_FLUSH) + Packet.m_Flags |= NETSENDFLAG_FLUSH; + + // write message to demo recorder + if(!(Flags&MSGFLAG_NORECORD)) + m_DemoRecorder.RecordMessage(pMsg->Data(), pMsg->Size()); + + if(!(Flags&MSGFLAG_NOSEND)) + { + if(ClientID == -1) + { + // broadcast + int i; + for(i = 0; i < MAX_CLIENTS; i++) + if(m_aClients[i].m_State == CClient::STATE_INGAME) + { + Packet.m_ClientID = i; + m_NetServer.Send(&Packet); + } + } + else + m_NetServer.Send(&Packet); + } + return 0; +} + +void CServer::DoSnapshot() +{ + GameServer()->OnPreSnap(); + + // create snapshot for demo recording + if(m_DemoRecorder.IsRecording()) + { + char aData[CSnapshot::MAX_SIZE]; + int SnapshotSize; + + // build snap and possibly add some messages + m_SnapshotBuilder.Init(); + GameServer()->OnSnap(-1); + SnapshotSize = m_SnapshotBuilder.Finish(aData); + + // write snapshot + m_DemoRecorder.RecordSnapshot(Tick(), aData, SnapshotSize); + } + + // create snapshots for all clients + for(int i = 0; i < MAX_CLIENTS; i++) + { + // client must be ingame to recive snapshots + if(m_aClients[i].m_State != CClient::STATE_INGAME) + continue; + + // this client is trying to recover, don't spam snapshots + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0) + continue; + + // this client is trying to recover, don't spam snapshots + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0) + continue; + + { + char aData[CSnapshot::MAX_SIZE]; + char aDeltaData[CSnapshot::MAX_SIZE]; + char aCompData[CSnapshot::MAX_SIZE]; + int SnapshotSize; + int Crc; + static CSnapshot EmptySnap; + CSnapshot *pDeltashot = &EmptySnap; + int DeltashotSize; + int DeltaTick = -1; + int DeltaSize; + + m_SnapshotBuilder.Init(); + + GameServer()->OnSnap(i); + + // finish snapshot + SnapshotSize = m_SnapshotBuilder.Finish(aData); + Crc = ((CSnapshot*)aData)->Crc(); + + // remove old snapshos + // keep 3 seconds worth of snapshots + m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentGameTick-SERVER_TICK_SPEED*3); + + // save it the snapshot + m_aClients[i].m_Snapshots.Add(m_CurrentGameTick, time_get(), SnapshotSize, aData, 0); + + // find snapshot that we can preform delta against + EmptySnap.Clear(); + + { + DeltashotSize = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &pDeltashot, 0); + if(DeltashotSize >= 0) + DeltaTick = m_aClients[i].m_LastAckedSnapshot; + else + { + // no acked package found, force client to recover rate + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_FULL) + m_aClients[i].m_SnapRate = CClient::SNAPRATE_RECOVER; + } + } + + // create delta + DeltaSize = m_SnapshotDelta.CreateDelta(pDeltashot, (CSnapshot*)aData, aDeltaData); + + if(DeltaSize) + { + // compress it + int SnapshotSize; + const int MaxSize = MAX_SNAPSHOT_PACKSIZE; + int NumPackets; + + SnapshotSize = CVariableInt::Compress(aDeltaData, DeltaSize, aCompData); + NumPackets = (SnapshotSize+MaxSize-1)/MaxSize; + + for(int n = 0, Left = SnapshotSize; Left; n++) + { + int Chunk = Left < MaxSize ? Left : MaxSize; + Left -= Chunk; + + if(NumPackets == 1) + { + CMsgPacker Msg(NETMSG_SNAPSINGLE); + Msg.AddInt(m_CurrentGameTick); + Msg.AddInt(m_CurrentGameTick-DeltaTick); + Msg.AddInt(Crc); + Msg.AddInt(Chunk); + Msg.AddRaw(&aCompData[n*MaxSize], Chunk); + SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true); + } + else + { + CMsgPacker Msg(NETMSG_SNAP); + Msg.AddInt(m_CurrentGameTick); + Msg.AddInt(m_CurrentGameTick-DeltaTick); + Msg.AddInt(NumPackets); + Msg.AddInt(n); + Msg.AddInt(Crc); + Msg.AddInt(Chunk); + Msg.AddRaw(&aCompData[n*MaxSize], Chunk); + SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true); + } + } + } + else + { + CMsgPacker Msg(NETMSG_SNAPEMPTY); + Msg.AddInt(m_CurrentGameTick); + Msg.AddInt(m_CurrentGameTick-DeltaTick); + SendMsgEx(&Msg, MSGFLAG_FLUSH, i, true); + } + } + } + + GameServer()->OnPostSnap(); +} + + +int CServer::NewClientCallback(int ClientId, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + pThis->m_aClients[ClientId].m_State = CClient::STATE_AUTH; + pThis->m_aClients[ClientId].m_aName[0] = 0; + pThis->m_aClients[ClientId].m_aClan[0] = 0; + pThis->m_aClients[ClientId].m_Authed = 0; + pThis->m_aClients[ClientId].Reset(); + return 0; +} + +int CServer::DelClientCallback(int ClientId, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + + // notify the mod about the drop + if(pThis->m_aClients[ClientId].m_State >= CClient::STATE_READY) + pThis->GameServer()->OnClientDrop(ClientId); + + pThis->m_aClients[ClientId].m_State = CClient::STATE_EMPTY; + pThis->m_aClients[ClientId].m_aName[0] = 0; + pThis->m_aClients[ClientId].m_aClan[0] = 0; + pThis->m_aClients[ClientId].m_Authed = 0; + pThis->m_aClients[ClientId].m_Snapshots.PurgeAll(); + return 0; +} + +void CServer::SendMap(int ClientId) +{ + CMsgPacker Msg(NETMSG_MAP_CHANGE); + Msg.AddString(g_Config.m_SvMap, 0); + Msg.AddInt(m_CurrentMapCrc); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId, true); +} + +void CServer::SendRconLine(int ClientId, const char *pLine) +{ + CMsgPacker Msg(NETMSG_RCON_LINE); + Msg.AddString(pLine, 512); + SendMsgEx(&Msg, MSGFLAG_VITAL, ClientId, true); +} + +void CServer::SendRconLineAuthed(const char *pLine, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + static volatile int ReentryGuard = 0; + int i; + + if(ReentryGuard) return; + ReentryGuard++; + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed) + pThis->SendRconLine(i, pLine); + } + + ReentryGuard--; +} + +void CServer::ProcessClientPacket(CNetChunk *pPacket) +{ + int ClientId = pPacket->m_ClientID; + NETADDR Addr; + CUnpacker Unpacker; + Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize); + + // unpack msgid and system flag + int Msg = Unpacker.GetInt(); + int Sys = Msg&1; + Msg >>= 1; + + if(Unpacker.Error()) + return; + + if(m_aClients[ClientId].m_State == CClient::STATE_AUTH) + { + if(Sys && Msg == NETMSG_INFO) + { + char aVersion[64]; + const char *pPassword; + str_copy(aVersion, Unpacker.GetString(), 64); + if(str_comp(aVersion, GameServer()->NetVersion()) != 0) + { + // OH FUCK! wrong version, drop him + char aReason[256]; + str_format(aReason, sizeof(aReason), "wrong version. server is running '%s' and client '%s'.", GameServer()->NetVersion(), aVersion); + m_NetServer.Drop(ClientId, aReason); + return; + } + + str_copy(m_aClients[ClientId].m_aName, Unpacker.GetString(), MAX_NAME_LENGTH); + str_copy(m_aClients[ClientId].m_aClan, Unpacker.GetString(), MAX_CLANNAME_LENGTH); + pPassword = Unpacker.GetString(); + + if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0) + { + // wrong password + m_NetServer.Drop(ClientId, "wrong password"); + return; + } + + m_aClients[ClientId].m_State = CClient::STATE_CONNECTING; + SendMap(ClientId); + } + } + else + { + if(Sys) + { + // system message + if(Msg == NETMSG_REQUEST_MAP_DATA) + { + int Chunk = Unpacker.GetInt(); + int ChunkSize = 1024-128; + int Offset = Chunk * ChunkSize; + int Last = 0; + + // drop faulty map data requests + if(Chunk < 0 || Offset > m_CurrentMapSize) + return; + + if(Offset+ChunkSize >= m_CurrentMapSize) + { + ChunkSize = m_CurrentMapSize-Offset; + if(ChunkSize < 0) + ChunkSize = 0; + Last = 1; + } + + CMsgPacker Msg(NETMSG_MAP_DATA); + Msg.AddInt(Last); + Msg.AddInt(m_CurrentMapSize); + Msg.AddInt(ChunkSize); + Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize); + SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId, true); + + if(g_Config.m_Debug) + dbg_msg("server", "sending chunk %d with size %d", Chunk, ChunkSize); + } + else if(Msg == NETMSG_READY) + { + if(m_aClients[ClientId].m_State == CClient::STATE_CONNECTING) + { + Addr = m_NetServer.ClientAddr(ClientId); + + dbg_msg("server", "player is ready. ClientId=%x ip=%d.%d.%d.%d", + ClientId, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); + m_aClients[ClientId].m_State = CClient::STATE_READY; + GameServer()->OnClientConnected(ClientId); + } + } + else if(Msg == NETMSG_ENTERGAME) + { + if(m_aClients[ClientId].m_State == CClient::STATE_READY) + { + Addr = m_NetServer.ClientAddr(ClientId); + + dbg_msg("server", "player has entered the game. ClientId=%x ip=%d.%d.%d.%d", + ClientId, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); + m_aClients[ClientId].m_State = CClient::STATE_INGAME; + GameServer()->OnClientEnter(ClientId); + } + } + else if(Msg == NETMSG_INPUT) + { + CClient::CInput *pInput; + int64 TagTime; + + m_aClients[ClientId].m_LastAckedSnapshot = Unpacker.GetInt(); + int IntendedTick = Unpacker.GetInt(); + int Size = Unpacker.GetInt(); + + // check for errors + if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE) + return; + + if(m_aClients[ClientId].m_LastAckedSnapshot > 0) + m_aClients[ClientId].m_SnapRate = CClient::SNAPRATE_FULL; + + if(m_aClients[ClientId].m_Snapshots.Get(m_aClients[ClientId].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0) + m_aClients[ClientId].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq()); + + // add message to report the input timing + // skip packets that are old + if(IntendedTick > m_aClients[ClientId].m_LastInputTick) + { + int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq(); + + CMsgPacker Msg(NETMSG_INPUTTIMING); + Msg.AddInt(IntendedTick); + Msg.AddInt(TimeLeft); + SendMsgEx(&Msg, 0, ClientId, true); + } + + m_aClients[ClientId].m_LastInputTick = IntendedTick; + + pInput = &m_aClients[ClientId].m_aInputs[m_aClients[ClientId].m_CurrentInput]; + + if(IntendedTick <= Tick()) + IntendedTick = Tick()+1; + + pInput->m_GameTick = IntendedTick; + + for(int i = 0; i < Size/4; i++) + pInput->m_aData[i] = Unpacker.GetInt(); + + mem_copy(m_aClients[ClientId].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int)); + + m_aClients[ClientId].m_CurrentInput++; + m_aClients[ClientId].m_CurrentInput %= 200; + + // call the mod with the fresh input data + if(m_aClients[ClientId].m_State == CClient::STATE_INGAME) + GameServer()->OnClientDirectInput(ClientId, m_aClients[ClientId].m_LatestInput.m_aData); + } + else if(Msg == NETMSG_RCON_CMD) + { + const char *pCmd = Unpacker.GetString(); + + if(Unpacker.Error() == 0 && m_aClients[ClientId].m_Authed) + { + dbg_msg("server", "ClientId=%d rcon='%s'", ClientId, pCmd); + Console()->ExecuteLine(pCmd); + } + } + else if(Msg == NETMSG_RCON_AUTH) + { + const char *pPw; + Unpacker.GetString(); // login name, not used + pPw = Unpacker.GetString(); + + if(Unpacker.Error() == 0) + { + if(g_Config.m_SvRconPassword[0] == 0) + { + SendRconLine(ClientId, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); + } + else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0) + { + CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); + Msg.AddInt(1); + SendMsgEx(&Msg, MSGFLAG_VITAL, ClientId, true); + + m_aClients[ClientId].m_Authed = 1; + SendRconLine(ClientId, "Authentication successful. Remote console access granted."); + dbg_msg("server", "ClientId=%d authed", ClientId); + } + else + { + SendRconLine(ClientId, "Wrong password."); + } + } + } + else if(Msg == NETMSG_PING) + { + CMsgPacker Msg(NETMSG_PING_REPLY); + SendMsgEx(&Msg, 0, ClientId, true); + } + else + { + char aHex[] = "0123456789ABCDEF"; + char aBuf[512]; + + for(int b = 0; b < pPacket->m_DataSize && b < 32; b++) + { + aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4]; + aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf]; + aBuf[b*3+2] = ' '; + aBuf[b*3+3] = 0; + } + + dbg_msg("server", "strange message ClientId=%d msg=%d data_size=%d", ClientId, Msg, pPacket->m_DataSize); + dbg_msg("server", "%s", aBuf); + + } + } + else + { + // game message + if(m_aClients[ClientId].m_State >= CClient::STATE_READY) + GameServer()->OnMessage(Msg, &Unpacker, ClientId); + } + } +} + +void CServer::SendServerInfo(NETADDR *pAddr, int Token) +{ + CNetChunk Packet; + CPacker p; + char aBuf[128]; + + // count the players + int PlayerCount = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + PlayerCount++; + } + + p.Reset(); + + if(Token >= 0) + { + // new token based format + p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + str_format(aBuf, sizeof(aBuf), "%d", Token); + p.AddString(aBuf, 6); + } + else + { + // old format + p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)); + } + + p.AddString(GameServer()->Version(), 32); + p.AddString(g_Config.m_SvName, 64); + p.AddString(g_Config.m_SvMap, 32); + + // gametype + p.AddString(m_aBrowseinfoGametype, 16); + + // flags + int i = 0; + if(g_Config.m_Password[0]) // password set + i |= SERVER_FLAG_PASSWORD; + str_format(aBuf, sizeof(aBuf), "%d", i); + p.AddString(aBuf, 2); + + // progression + str_format(aBuf, sizeof(aBuf), "%d", m_BrowseinfoProgression); + p.AddString(aBuf, 4); + + str_format(aBuf, sizeof(aBuf), "%d", PlayerCount); p.AddString(aBuf, 3); // num players + str_format(aBuf, sizeof(aBuf), "%d", m_NetServer.MaxClients()); p.AddString(aBuf, 3); // max players + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + { + p.AddString(m_aClients[i].m_aName, 48); // player name + str_format(aBuf, sizeof(aBuf), "%d", m_aClients[i].m_Score); p.AddString(aBuf, 6); // player score + } + } + + + Packet.m_ClientID = -1; + Packet.m_Address = *pAddr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = p.Size(); + Packet.m_pData = p.Data(); + m_NetServer.Send(&Packet); +} + +void CServer::UpdateServerInfo() +{ + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + { + NETADDR Addr = m_NetServer.ClientAddr(i); + SendServerInfo(&Addr, -1); // SERVERBROWSE_OLD_INFO + } + } +} + +int CServer::BanAdd(NETADDR Addr, int Seconds) +{ + return m_NetServer.BanAdd(Addr, Seconds); +} + +int CServer::BanRemove(NETADDR Addr) +{ + return m_NetServer.BanRemove(Addr); +} + + +void CServer::PumpNetwork() +{ + CNetChunk Packet; + + m_NetServer.Update(); + + // process packets + while(m_NetServer.Recv(&Packet)) + { + if(Packet.m_ClientID == -1) + { + // stateless + if(!m_Register.RegisterProcessPacket(&Packet)) + { + if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && + mem_comp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); + } + + + if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && + mem_comp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) + { + SendServerInfo(&Packet.m_Address, -1); + } + } + } + else + ProcessClientPacket(&Packet); + } +} + +int CServer::LoadMap(const char *pMapName) +{ + //DATAFILE *df; + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName); + + /*df = datafile_load(buf); + if(!df) + return 0;*/ + + if(!m_pMap->Load(aBuf)) + return 0; + + // stop recording when we change map + m_DemoRecorder.Stop(); + + // reinit snapshot ids + m_IDPool.TimeoutIDs(); + + // get the crc of the map + m_CurrentMapCrc = m_pMap->Crc(); + dbg_msg("server", "%s crc is %08x", aBuf, m_CurrentMapCrc); + + str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap)); + //map_set(df); + + // load compelate map into memory for download + { + IOHANDLE File = Storage()->OpenFile(aBuf, IOFLAG_READ); + m_CurrentMapSize = (int)io_length(File); + if(m_pCurrentMapData) + mem_free(m_pCurrentMapData); + m_pCurrentMapData = (unsigned char *)mem_alloc(m_CurrentMapSize, 1); + io_read(File, m_pCurrentMapData, m_CurrentMapSize); + io_close(File); + } + return 1; +} + +void CServer::InitEngine(const char *pAppname) +{ + m_Engine.Init(pAppname); +} + +void CServer::InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer) +{ + m_Register.Init(pNetServer, pMasterServer); +} + +int CServer::Run() +{ + m_pGameServer = Kernel()->RequestInterface<IGameServer>(); + m_pMap = Kernel()->RequestInterface<IEngineMap>(); + m_pStorage = Kernel()->RequestInterface<IStorage>(); + + //snap_init_id(); + net_init(); + + // + Console()->RegisterPrintCallback(SendRconLineAuthed, this); + + // load map + if(!LoadMap(g_Config.m_SvMap)) + { + dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap); + return -1; + } + + // start server + // TODO: IPv6 support + NETADDR BindAddr; + if(g_Config.m_SvBindaddr[0] && net_host_lookup(g_Config.m_SvBindaddr, &BindAddr, NETTYPE_IPV4) == 0) + { + // sweet! + BindAddr.port = g_Config.m_SvPort; + } + else + { + mem_zero(&BindAddr, sizeof(BindAddr)); + BindAddr.port = g_Config.m_SvPort; + } + + + if(!m_NetServer.Open(BindAddr, g_Config.m_SvMaxClients, 0)) + { + dbg_msg("server", "couldn't open socket. port might already be in use"); + return -1; + } + + m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); + + dbg_msg("server", "server name is '%s'", g_Config.m_SvName); + + GameServer()->OnInit(); + dbg_msg("server", "version %s", GameServer()->NetVersion()); + + // start game + { + int64 ReportTime = time_get(); + int ReportInterval = 3; + + m_Lastheartbeat = 0; + m_GameStartTime = time_get(); + + if(g_Config.m_Debug) + dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); + + while(m_RunServer) + { + int64 t = time_get(); + int NewTicks = 0; + + // load new map TODO: don't poll this + if(str_comp(g_Config.m_SvMap, m_aCurrentMap) != 0 || g_Config.m_SvMapReload) + { + g_Config.m_SvMapReload = 0; + + // load map + if(LoadMap(g_Config.m_SvMap)) + { + // new map loaded + GameServer()->OnShutdown(); + + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].m_State == CClient::STATE_EMPTY) + continue; + + SendMap(c); + m_aClients[c].Reset(); + m_aClients[c].m_State = CClient::STATE_CONNECTING; + } + + m_GameStartTime = time_get(); + m_CurrentGameTick = 0; + Kernel()->ReregisterInterface(GameServer()); + GameServer()->OnInit(); + } + else + { + dbg_msg("server", "failed to load map. mapname='%s'", g_Config.m_SvMap); + str_copy(g_Config.m_SvMap, m_aCurrentMap, sizeof(g_Config.m_SvMap)); + } + } + + while(t > TickStartTime(m_CurrentGameTick+1)) + { + m_CurrentGameTick++; + NewTicks++; + + // apply new input + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].m_State == CClient::STATE_EMPTY) + continue; + for(int i = 0; i < 200; i++) + { + if(m_aClients[c].m_aInputs[i].m_GameTick == Tick()) + { + if(m_aClients[c].m_State == CClient::STATE_INGAME) + GameServer()->OnClientPredictedInput(c, m_aClients[c].m_aInputs[i].m_aData); + break; + } + } + } + + GameServer()->OnTick(); + } + + // snap game + if(NewTicks) + { + if(g_Config.m_SvHighBandwidth || (m_CurrentGameTick%2) == 0) + DoSnapshot(); + } + + // master server stuff + m_Register.RegisterUpdate(); + + PumpNetwork(); + + if(ReportTime < time_get()) + { + if(g_Config.m_Debug) + { + /* + static NETSTATS prev_stats; + NETSTATS stats; + netserver_stats(net, &stats); + + perf_next(); + + if(config.dbg_pref) + perf_dump(&rootscope); + + dbg_msg("server", "send=%8d recv=%8d", + (stats.send_bytes - prev_stats.send_bytes)/reportinterval, + (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); + + prev_stats = stats; + */ + } + + ReportTime += time_freq()*ReportInterval; + } + + // wait for incomming data + net_socket_read_wait(m_NetServer.Socket(), 5); + } + } + // disconnect all clients on shutdown + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + m_NetServer.Drop(i, "server shutdown"); + } + + GameServer()->OnShutdown(); + m_pMap->Unload(); + + if(m_pCurrentMapData) + mem_free(m_pCurrentMapData); + return 0; +} + +void CServer::ConKick(IConsole::IResult *pResult, void *pUser) +{ + ((CServer *)pUser)->Kick(pResult->GetInteger(0), "kicked by console"); +} + +void CServer::ConBan(IConsole::IResult *pResult, void *pUser) +{ + NETADDR Addr; + char aAddrStr[128]; + const char *pStr = pResult->GetString(0); + int Minutes = 30; + + if(pResult->NumArguments() > 1) + Minutes = pResult->GetInteger(1); + + if(net_addr_from_str(&Addr, pStr) == 0) + ((CServer *)pUser)->BanAdd(Addr, Minutes*60); + else if(StrAllnum(pStr)) + { + int ClientId = str_toint(pStr); + + if(ClientId < 0 || ClientId >= MAX_CLIENTS || ((CServer *)pUser)->m_aClients[ClientId].m_State == CClient::STATE_EMPTY) + { + dbg_msg("server", "invalid client id"); + return; + } + + NETADDR Addr = ((CServer *)pUser)->m_NetServer.ClientAddr(ClientId); + ((CServer *)pUser)->BanAdd(Addr, Minutes*60); + } + + Addr.port = 0; + net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr)); + + if(Minutes) + dbg_msg("server", "banned %s for %d minutes", aAddrStr, Minutes); + else + dbg_msg("server", "banned %s for life", aAddrStr); +} + +void CServer::ConUnban(IConsole::IResult *pResult, void *pUser) +{ + NETADDR Addr; + const char *pStr = pResult->GetString(0); + + if(net_addr_from_str(&Addr, pStr) == 0) + ((CServer *)pUser)->BanRemove(Addr); + else + dbg_msg("server", "invalid network address"); +} + +void CServer::ConBans(IConsole::IResult *pResult, void *pUser) +{ + unsigned Now = time_timestamp(); + char aBuf[1024]; + CServer* pServer = (CServer *)pUser; + + int Num = pServer->m_NetServer.BanNum(); + for(int i = 0; i < Num; i++) + { + CNetServer::CBanInfo Info; + pServer->m_NetServer.BanGet(i, &Info); + NETADDR Addr = Info.m_Addr; + + if(Info.m_Expires == -1) + { + str_format(aBuf, sizeof(aBuf), "#%d %d.%d.%d.%d for life", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); + } + else + { + unsigned t = Info.m_Expires - Now; + str_format(aBuf, sizeof(aBuf), "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], t/60, t%60); + } + pServer->Console()->Print(aBuf); + dbg_msg("server", "%s", aBuf); + } + str_format(aBuf, sizeof(aBuf), "%d ban(s)", Num); + pServer->Console()->Print(aBuf); + dbg_msg("server", "%s", aBuf); +} + +void CServer::ConStatus(IConsole::IResult *pResult, void *pUser) +{ + int i; + NETADDR Addr; + char aBuf[1024]; + CServer* pServer = (CServer *)pUser; + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(pServer->m_aClients[i].m_State == CClient::STATE_INGAME) + { + Addr = pServer->m_NetServer.ClientAddr(i); + str_format(aBuf, sizeof(aBuf), "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d", + i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], Addr.port, + pServer->m_aClients[i].m_aName, pServer->m_aClients[i].m_Score); + pServer->Console()->Print(aBuf); + dbg_msg("server", "%s", aBuf); + } + } +} + +void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser) +{ + ((CServer *)pUser)->m_RunServer = 0; +} + +void CServer::ConRecord(IConsole::IResult *pResult, void *pUser) +{ + char aFilename[512]; + str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pResult->GetString(0)); + ((CServer *)pUser)->m_DemoRecorder.Start(((CServer *)pUser)->Storage(), aFilename, ((CServer *)pUser)->GameServer()->NetVersion(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server"); +} + +void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser) +{ + ((CServer *)pUser)->m_DemoRecorder.Stop(); +} + +void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments()) + ((CServer *)pUserData)->UpdateServerInfo(); +} + +void CServer::RegisterCommands() +{ + m_pConsole = Kernel()->RequestInterface<IConsole>(); + + Console()->Register("kick", "i", CFGFLAG_SERVER, ConKick, this, ""); + Console()->Register("ban", "s?i", CFGFLAG_SERVER, ConBan, this, ""); + Console()->Register("unban", "s", CFGFLAG_SERVER, ConUnban, this, ""); + Console()->Register("bans", "", CFGFLAG_SERVER, ConBans, this, ""); + Console()->Register("status", "", CFGFLAG_SERVER, ConStatus, this, ""); + Console()->Register("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, ""); + + Console()->Register("record", "s", CFGFLAG_SERVER, ConRecord, this, ""); + Console()->Register("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, ""); + + Console()->Chain("sv_name", ConchainSpecialInfoupdate, this); + Console()->Chain("password", ConchainSpecialInfoupdate, this); +} + + +int CServer::SnapNewID() +{ + return m_IDPool.NewID(); +} + +void CServer::SnapFreeID(int ID) +{ + m_IDPool.FreeID(ID); +} + + +void *CServer::SnapNewItem(int Type, int Id, int Size) +{ + dbg_assert(Type >= 0 && Type <=0xffff, "incorrect type"); + dbg_assert(Id >= 0 && Id <=0xffff, "incorrect id"); + return m_SnapshotBuilder.NewItem(Type, Id, Size); +} + +void CServer::SnapSetStaticsize(int ItemType, int Size) +{ + m_SnapshotDelta.SetStaticsize(ItemType, Size); +} + +static CServer *CreateServer() { return new CServer(); } + +int main(int argc, const char **argv) // ignore_convention +{ +#if defined(CONF_FAMILY_WINDOWS) + for(int i = 1; i < argc; i++) // ignore_convention + { + if(str_comp("-s", argv[i]) == 0 || str_comp("--silent", argv[i]) == 0) // ignore_convention + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + break; + } + } +#endif + + // init the engine + dbg_msg("server", "starting..."); + CServer *pServer = CreateServer(); + pServer->InitEngine("Teeworlds"); + + IKernel *pKernel = IKernel::Create(); + + // create the components + IEngineMap *pEngineMap = CreateEngineMap(); + IGameServer *pGameServer = CreateGameServer(); + IConsole *pConsole = CreateConsole(); + IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer(); + IStorage *pStorage = CreateStorage("Teeworlds", argv[0]); // ignore_convention + IConfig *pConfig = CreateConfig(); + + pServer->InitRegister(&pServer->m_NetServer, pEngineMasterServer); + + { + bool RegisterFail = false; + + RegisterFail = RegisterFail || !pKernel->RegisterInterface(pServer); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMap*>(pEngineMap)); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMap*>(pEngineMap)); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(pGameServer); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConsole); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(pConfig); + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMasterServer*>(pEngineMasterServer)); // register as both + RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMasterServer*>(pEngineMasterServer)); + + if(RegisterFail) + return -1; + } + + pConfig->Init(); + pEngineMasterServer->Init(pServer->Engine()); + pEngineMasterServer->Load(); + + // register all console commands + pServer->RegisterCommands(); + pGameServer->OnConsoleInit(); + + // execute autoexec file + pConsole->ExecuteFile("autoexec.cfg"); + + // parse the command line arguments + if(argc > 1) // ignore_convention + pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention + + // run the server + pServer->Run(); + + // free + delete pServer; + delete pKernel; + delete pEngineMap; + delete pGameServer; + delete pConsole; + delete pEngineMasterServer; + delete pStorage; + delete pConfig; + return 0; +} + diff --git a/src/engine/server/server.h b/src/engine/server/server.h new file mode 100644 index 00000000..6904085a --- /dev/null +++ b/src/engine/server/server.h @@ -0,0 +1,195 @@ +#ifndef ENGINE_SERVER_SERVER_H +#define ENGINE_SERVER_SERVER_H + +#include <engine/server.h> + +class CSnapIDPool +{ + enum + { + MAX_IDS = 16*1024, + }; + + class CID + { + public: + short m_Next; + short m_State; // 0 = free, 1 = alloced, 2 = timed + int m_Timeout; + }; + + CID m_aIDs[MAX_IDS]; + + int m_FirstFree; + int m_FirstTimed; + int m_LastTimed; + int m_Usage; + int m_InUsage; + +public: + + CSnapIDPool(); + + void Reset(); + void RemoveFirstTimeout(); + int NewID(); + void TimeoutIDs(); + void FreeID(int Id); +}; + +class CServer : public IServer +{ + class IGameServer *m_pGameServer; + class IConsole *m_pConsole; + class IStorage *m_pStorage; +public: + class IGameServer *GameServer() { return m_pGameServer; } + class IConsole *Console() { return m_pConsole; } + class IStorage *Storage() { return m_pStorage; } + class CEngine *Engine() { return &m_Engine; } + + class CClient + { + public: + + enum + { + STATE_EMPTY = 0, + STATE_AUTH, + STATE_CONNECTING, + STATE_READY, + STATE_INGAME, + + SNAPRATE_INIT=0, + SNAPRATE_FULL, + SNAPRATE_RECOVER + }; + + class CInput + { + public: + int m_aData[MAX_INPUT_SIZE]; + int m_GameTick; // the tick that was chosen for the input + }; + + // connection state info + int m_State; + int m_Latency; + int m_SnapRate; + + int m_LastAckedSnapshot; + int m_LastInputTick; + CSnapshotStorage m_Snapshots; + + CInput m_LatestInput; + CInput m_aInputs[200]; // TODO: handle input better + int m_CurrentInput; + + char m_aName[MAX_NAME_LENGTH]; + char m_aClan[MAX_CLANNAME_LENGTH]; + int m_Score; + int m_Authed; + + void Reset(); + }; + + CClient m_aClients[MAX_CLIENTS]; + + CSnapshotDelta m_SnapshotDelta; + CSnapshotBuilder m_SnapshotBuilder; + CSnapIDPool m_IDPool; + CNetServer m_NetServer; + + IEngineMap *m_pMap; + + int64 m_GameStartTime; + //int m_CurrentGameTick; + int m_RunServer; + + char m_aBrowseinfoGametype[16]; + int m_BrowseinfoProgression; + + int64 m_Lastheartbeat; + //static NETADDR4 master_server; + + char m_aCurrentMap[64]; + int m_CurrentMapCrc; + unsigned char *m_pCurrentMapData; + int m_CurrentMapSize; + + CDemoRecorder m_DemoRecorder; + CEngine m_Engine; + CRegister m_Register; + + CServer(); + + int TrySetClientName(int ClientID, const char *pName); + + virtual void SetClientName(int ClientID, const char *pName); + virtual void SetClientScore(int ClientID, int Score); + virtual void SetBrowseInfo(const char *pGameType, int Progression); + + void Kick(int ClientID, const char *pReason); + + //int Tick() + int64 TickStartTime(int Tick); + //int TickSpeed() + + int Init(); + + int GetClientInfo(int ClientID, CClientInfo *pInfo); + void GetClientIP(int ClientID, char *pIPString, int Size); + const char *ClientName(int ClientId); + bool ClientIngame(int ClientID); + + int *LatestInput(int ClientId, int *size); + + virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientId); + int SendMsgEx(CMsgPacker *pMsg, int Flags, int ClientID, bool System); + + void DoSnapshot(); + + static int NewClientCallback(int ClientId, void *pUser); + static int DelClientCallback(int ClientId, void *pUser); + + void SendMap(int ClientId); + void SendRconLine(int ClientId, const char *pLine); + static void SendRconLineAuthed(const char *pLine, void *pUser); + + void ProcessClientPacket(CNetChunk *pPacket); + + void SendServerInfo(NETADDR *pAddr, int Token); + void UpdateServerInfo(); + + int BanAdd(NETADDR Addr, int Seconds); + int BanRemove(NETADDR Addr); + + + void PumpNetwork(); + + int LoadMap(const char *pMapName); + + void InitEngine(const char *pAppname); + void InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterServer); + int Run(); + + static void ConKick(IConsole::IResult *pResult, void *pUser); + static void ConBan(IConsole::IResult *pResult, void *pUser); + static void ConUnban(IConsole::IResult *pResult, void *pUser); + static void ConBans(IConsole::IResult *pResult, void *pUser); + static void ConStatus(IConsole::IResult *pResult, void *pUser); + static void ConShutdown(IConsole::IResult *pResult, void *pUser); + static void ConRecord(IConsole::IResult *pResult, void *pUser); + static void ConStopRecord(IConsole::IResult *pResult, void *pUser); + static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + + void RegisterCommands(); + + + virtual int SnapNewID(); + virtual void SnapFreeID(int ID); + virtual void *SnapNewItem(int Type, int Id, int Size); + void SnapSetStaticsize(int ItemType, int Size); +}; + +#endif diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h new file mode 100644 index 00000000..43732f13 --- /dev/null +++ b/src/engine/serverbrowser.h @@ -0,0 +1,93 @@ +#ifndef ENGINE_SERVERBROWSER_H +#define ENGINE_SERVERBROWSER_H + +#include "kernel.h" + +/* + Structure: CServerInfo +*/ +class CServerInfo +{ +public: + /* + Structure: CInfoPlayer + */ + class CPlayer + { + public: + char m_aName[48]; + int m_Score; + } ; + + int m_SortedIndex; + int m_ServerIndex; + + NETADDR m_NetAddr; + + int m_QuickSearchHit; + + int m_Progression; + int m_MaxPlayers; + int m_NumPlayers; + int m_Flags; + int m_Favorite; + int m_Latency; // in ms + char m_aGameType[16]; + char m_aName[64]; + char m_aMap[32]; + char m_aVersion[32]; + char m_aAddress[24]; + CPlayer m_aPlayers[16]; +}; + +class IServerBrowser : public IInterface +{ + MACRO_INTERFACE("serverbrowser", 0) +public: + + /* Constants: Server Browser Sorting + SORT_NAME - Sort by name. + SORT_PING - Sort by ping. + SORT_MAP - Sort by map + SORT_GAMETYPE - Sort by game type. DM, TDM etc. + SORT_PROGRESSION - Sort by progression. + SORT_NUMPLAYERS - Sort after how many players there are on the server. + */ + enum{ + SORT_NAME = 0, + SORT_PING, + SORT_MAP, + SORT_GAMETYPE, + SORT_PROGRESSION, + SORT_NUMPLAYERS, + + QUICK_SERVERNAME=1, + QUICK_PLAYERNAME=2, + QUICK_MAPNAME=4, + + TYPE_INTERNET = 0, + TYPE_LAN = 1, + TYPE_FAVORITES = 2, + + // TODO: clean this up + SET_MASTER_ADD=1, + SET_FAV_ADD, + SET_TOKEN, + SET_OLD_INTERNET, + SET_OLD_LAN + }; + + virtual void Refresh(int Type) = 0; + virtual bool IsRefreshingMasters() const = 0; + + virtual int NumServers() const = 0; + + virtual int NumSortedServers() const = 0; + virtual const CServerInfo *SortedGet(int Index) const = 0; + + virtual bool IsFavorite(const NETADDR &Addr) const = 0; + virtual void AddFavorite(const NETADDR &Addr) = 0; + virtual void RemoveFavorite(const NETADDR &Addr) = 0; +}; + +#endif diff --git a/src/engine/shared/compression.cpp b/src/engine/shared/compression.cpp new file mode 100644 index 00000000..63e44699 --- /dev/null +++ b/src/engine/shared/compression.cpp @@ -0,0 +1,88 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> + +#include "compression.h" + +// Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign +unsigned char *CVariableInt::Pack(unsigned char *pDst, int i) +{ + *pDst = (i>>25)&0x40; // set sign bit if i<0 + i = i^(i>>31); // if(i<0) i = ~i + + *pDst |= i&0x3F; // pack 6bit into dst + i >>= 6; // discard 6 bits + if(i) + { + *pDst |= 0x80; // set extend bit + while(1) + { + pDst++; + *pDst = i&(0x7F); // pack 7bit + i >>= 7; // discard 7 bits + *pDst |= (i!=0)<<7; // set extend bit (may branch) + if(!i) + break; + } + } + + pDst++; + return pDst; +} + +const unsigned char *CVariableInt::Unpack(const unsigned char *pSrc, int *pInOut) +{ + int Sign = (*pSrc>>6)&1; + *pInOut = *pSrc&0x3F; + + do + { + if(!(*pSrc&0x80)) break; + pSrc++; + *pInOut |= (*pSrc&(0x7F))<<(6); + + if(!(*pSrc&0x80)) break; + pSrc++; + *pInOut |= (*pSrc&(0x7F))<<(6+7); + + if(!(*pSrc&0x80)) break; + pSrc++; + *pInOut |= (*pSrc&(0x7F))<<(6+7+7); + + if(!(*pSrc&0x80)) break; + pSrc++; + *pInOut |= (*pSrc&(0x7F))<<(6+7+7+7); + } while(0); + + pSrc++; + *pInOut ^= -Sign; // if(sign) *i = ~(*i) + return pSrc; +} + + +long CVariableInt::Decompress(const void *pSrc_, int Size, void *pDst_) +{ + const unsigned char *pSrc = (unsigned char *)pSrc_; + const unsigned char *pEnd = pSrc + Size; + int *pDst = (int *)pDst_; + while(pSrc < pEnd) + { + pSrc = CVariableInt::Unpack(pSrc, pDst); + pDst++; + } + return (long)((unsigned char *)pDst-(unsigned char *)pDst_); +} + +long CVariableInt::Compress(const void *pSrc_, int Size, void *pDst_) +{ + int *pSrc = (int *)pSrc_; + unsigned char *pDst = (unsigned char *)pDst_; + Size /= 4; + while(Size) + { + pDst = CVariableInt::Pack(pDst, *pSrc); + Size--; + pSrc++; + } + return (long)(pDst-(unsigned char *)pDst_); +} + diff --git a/src/engine/shared/compression.h b/src/engine/shared/compression.h new file mode 100644 index 00000000..9bd9e61a --- /dev/null +++ b/src/engine/shared/compression.h @@ -0,0 +1,12 @@ +#ifndef ENGINE_SHARED_COMPRESSION_H +#define ENGINE_SHARED_COMPRESSION_H +// variable int packing +class CVariableInt +{ +public: + static unsigned char *Pack(unsigned char *pDst, int i); + static const unsigned char *Unpack(const unsigned char *pSrc, int *pInOut); + static long Compress(const void *pSrc, int Size, void *pDst); + static long Decompress(const void *pSrc, int Size, void *pDst); +}; +#endif diff --git a/src/engine/shared/config.cpp b/src/engine/shared/config.cpp new file mode 100644 index 00000000..ca12e8b7 --- /dev/null +++ b/src/engine/shared/config.cpp @@ -0,0 +1,111 @@ +#include <engine/config.h> +#include <engine/storage.h> +#include <engine/shared/config.h> + +CConfiguration g_Config; + +class CConfig : public IConfig +{ + IStorage *m_pStorage; + IOHANDLE m_ConfigFile; + + struct CCallback + { + SAVECALLBACKFUNC m_pfnFunc; + void *m_pUserData; + }; + + enum + { + MAX_CALLBACKS = 16 + }; + + CCallback m_aCallbacks[MAX_CALLBACKS]; + int m_NumCallbacks; + + void EscapeParam(char *pDst, const char *pSrc, int size) + { + for(int i = 0; *pSrc && i < size - 1; ++i) + { + if(*pSrc == '"' || *pSrc == '\\') // escape \ and " + *pDst++ = '\\'; + *pDst++ = *pSrc++; + } + *pDst = 0; + } + +public: + + CConfig() + { + m_ConfigFile = 0; + m_NumCallbacks = 0; + } + + virtual void Init() + { + m_pStorage = Kernel()->RequestInterface<IStorage>(); + Reset(); + } + + virtual void Reset() + { + #define MACRO_CONFIG_INT(Name,ScriptName,def,min,max,flags,desc) g_Config.m_##Name = def; + #define MACRO_CONFIG_STR(Name,ScriptName,len,def,flags,desc) str_copy(g_Config.m_##Name, def, len); + + #include "config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR + } + + virtual void Save() + { + if(!m_pStorage) + return; + m_ConfigFile = m_pStorage->OpenFile("settings.cfg", IOFLAG_WRITE); + + if(!m_ConfigFile) + return; + + char aLineBuf[1024*2]; + char aEscapeBuf[1024*2]; + + #define MACRO_CONFIG_INT(Name,ScriptName,def,min,max,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(aLineBuf, sizeof(aLineBuf), "%s %i", #ScriptName, g_Config.m_##Name); WriteLine(aLineBuf); } + #define MACRO_CONFIG_STR(Name,ScriptName,len,def,flags,desc) if((flags)&CFGFLAG_SAVE){ EscapeParam(aEscapeBuf, g_Config.m_##Name, sizeof(aEscapeBuf)); str_format(aLineBuf, sizeof(aLineBuf), "%s \"%s\"", #ScriptName, aEscapeBuf); WriteLine(aLineBuf); } + + #include "config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR + + for(int i = 0; i < m_NumCallbacks; i++) + m_aCallbacks[i].m_pfnFunc(this, m_aCallbacks[i].m_pUserData); + + io_close(m_ConfigFile); + m_ConfigFile = 0; + } + + virtual void RegisterCallback(SAVECALLBACKFUNC pfnFunc, void *pUserData) + { + dbg_assert(m_NumCallbacks < MAX_CALLBACKS, "too many config callbacks"); + m_aCallbacks[m_NumCallbacks].m_pfnFunc = pfnFunc; + m_aCallbacks[m_NumCallbacks].m_pUserData = pUserData; + m_NumCallbacks++; + } + + virtual void WriteLine(const char *pLine) + { + if(!m_ConfigFile) + return; +#if defined(CONF_FAMILY_WINDOWS) + static const char Newline[] = "\r\n"; +#else + static const char Newline[] = "\n"; +#endif + io_write(m_ConfigFile, pLine, str_length(pLine)); + io_write(m_ConfigFile, Newline, sizeof(Newline)-1); + } +}; + +IConfig *CreateConfig() { return new CConfig; } diff --git a/src/engine/shared/config.h b/src/engine/shared/config.h new file mode 100644 index 00000000..10a54004 --- /dev/null +++ b/src/engine/shared/config.h @@ -0,0 +1,22 @@ +#ifndef ENGINE_SHARED_E_CONFIG_H +#define ENGINE_SHARED_E_CONFIG_H + +struct CConfiguration +{ + #define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Save,Desc) int m_##Name; + #define MACRO_CONFIG_STR(Name,ScriptName,Len,Def,Save,Desc) char m_##Name[Len]; // Flawfinder: ignore + #include "config_variables.h" + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +}; + +extern CConfiguration g_Config; + +enum +{ + CFGFLAG_SAVE=1, + CFGFLAG_CLIENT=2, + CFGFLAG_SERVER=4 +}; + +#endif diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h new file mode 100644 index 00000000..e5541911 --- /dev/null +++ b/src/engine/shared/config_variables.h @@ -0,0 +1,80 @@ +#ifndef ENGINE_SHARED_E_CONFIG_VARIABLES_H +#define ENGINE_SHARED_E_CONFIG_VARIABLES_H +#undef ENGINE_SHARED_E_CONFIG_VARIABLES_H // this file will be included several times + +// TODO: remove this +#include "././game/variables.h" + + +MACRO_CONFIG_STR(PlayerName, player_name, 24, "nameless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the player") +MACRO_CONFIG_STR(ClanName, clan_name, 32, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "(not used)") +MACRO_CONFIG_STR(Password, password, 32, "", CFGFLAG_CLIENT|CFGFLAG_SERVER, "Password to the server") +MACRO_CONFIG_STR(Logfile, logfile, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filename to log all output to") + +MACRO_CONFIG_INT(ClCpuThrottle, cl_cpu_throttle, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(ClEditor, cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "") + +MACRO_CONFIG_INT(ClEventthread, cl_eventthread, 0, 0, 1, CFGFLAG_CLIENT, "Enables the usage of a thread to pump the events") + +MACRO_CONFIG_INT(InpGrab, inp_grab, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use forceful input grabbing method") + +MACRO_CONFIG_STR(BrFilterString, br_filter_string, 25, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server browser filtering string") + +MACRO_CONFIG_INT(BrFilterFull, br_filter_full, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out full server in browser") +MACRO_CONFIG_INT(BrFilterEmpty, br_filter_empty, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser") +MACRO_CONFIG_INT(BrFilterPw, br_filter_pw, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out password protected servers in browser") +MACRO_CONFIG_INT(BrFilterPing, br_filter_ping, 999, 0, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ping to filter by in the server browser") +MACRO_CONFIG_STR(BrFilterGametype, br_filter_gametype, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Game types to filter") +MACRO_CONFIG_INT(BrFilterPure, br_filter_pure, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard servers in browser") +MACRO_CONFIG_INT(BrFilterPureMap, br_filter_pure_map, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser") +MACRO_CONFIG_INT(BrFilterCompatversion, br_filter_compatversion, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-compatible servers in browser") + +MACRO_CONFIG_INT(BrSort, br_sort, 0, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(BrSortOrder, br_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(BrMaxRequests, br_max_requests, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser") + +MACRO_CONFIG_INT(SndBufferSize, snd_buffer_size, 512, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size") +MACRO_CONFIG_INT(SndRate, snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate") +MACRO_CONFIG_INT(SndEnable, snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable") +MACRO_CONFIG_INT(SndVolume, snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume") +MACRO_CONFIG_INT(SndDevice, snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use") + +MACRO_CONFIG_INT(SndNonactiveMute, snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") + +MACRO_CONFIG_INT(GfxScreenWidth, gfx_screen_width, 800, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution width") +MACRO_CONFIG_INT(GfxScreenHeight, gfx_screen_height, 600, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution height") +MACRO_CONFIG_INT(GfxFullscreen, gfx_fullscreen, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fullscreen") +MACRO_CONFIG_INT(GfxAlphabits, gfx_alphabits, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Alpha bits for framebuffer (fullscreen only)") +MACRO_CONFIG_INT(GfxColorDepth, gfx_color_depth, 24, 16, 24, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Colors bits for framebuffer (fullscreen only)") +MACRO_CONFIG_INT(GfxClear, gfx_clear, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clear screen before rendering") +MACRO_CONFIG_INT(GfxVsync, gfx_vsync, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Vertical sync") +MACRO_CONFIG_INT(GfxDisplayAllModes, gfx_display_all_modes, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(GfxTextureCompression, gfx_texture_compression, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use texture compression") +MACRO_CONFIG_INT(GfxHighDetail, gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail") +MACRO_CONFIG_INT(GfxTextureQuality, gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(GfxFsaaSamples, gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples") +MACRO_CONFIG_INT(GfxRefreshRate, gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate") +MACRO_CONFIG_INT(GfxFinish, gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") + +MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity") + +MACRO_CONFIG_STR(SvName, sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server name") +MACRO_CONFIG_STR(SvBindaddr, sv_bindaddr, 128, "", CFGFLAG_SERVER, "Address to bind the server to") +MACRO_CONFIG_INT(SvPort, sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server") +MACRO_CONFIG_INT(SvExternalPort, sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers") +MACRO_CONFIG_STR(SvMap, sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server") +MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server") +MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only") +MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing") +MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password") +MACRO_CONFIG_INT(SvMapReload, sv_map_reload, 0, 0, 1, CFGFLAG_SERVER, "Reload the current map") + +MACRO_CONFIG_INT(Debug, debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode") +MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems") +MACRO_CONFIG_INT(DbgStressNetwork, dbg_stress_network, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress network") +MACRO_CONFIG_INT(DbgPref, dbg_pref, 0, 0, 1, CFGFLAG_SERVER, "Performance outputs") +MACRO_CONFIG_INT(DbgGraphs, dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs") +MACRO_CONFIG_INT(DbgHitch, dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings") +MACRO_CONFIG_STR(DbgStressServer, dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress") +MACRO_CONFIG_INT(DbgResizable, dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables window resizing") +#endif diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp new file mode 100644 index 00000000..c545b7db --- /dev/null +++ b/src/engine/shared/console.cpp @@ -0,0 +1,489 @@ +#include <base/system.h> +#include <engine/shared/protocol.h> +#include <engine/storage.h> +#include "console.h" +#include "config.h" +#include "engine.h" +#include "linereader.h" + +const char *CConsole::CResult::GetString(unsigned Index) +{ + if (Index < 0 || Index >= m_NumArgs) + return ""; + return m_apArgs[Index]; +} + +int CConsole::CResult::GetInteger(unsigned Index) +{ + if (Index < 0 || Index >= m_NumArgs) + return 0; + return str_toint(m_apArgs[Index]); +} + +float CConsole::CResult::GetFloat(unsigned Index) +{ + if (Index < 0 || Index >= m_NumArgs) + return 0.0f; + return str_tofloat(m_apArgs[Index]); +} + +// the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces +static char *SkipBlanks(char *pStr) +{ + while(*pStr && (*pStr == ' ' || *pStr == '\t' || *pStr == '\n')) + pStr++; + return pStr; +} + +static char *SkipToBlank(char *pStr) +{ + while(*pStr && (*pStr != ' ' && *pStr != '\t' && *pStr != '\n')) + pStr++; + return pStr; +} + + +int CConsole::ParseStart(CResult *pResult, const char *pString, int Length) +{ + char *pStr; + int Len = sizeof(pResult->m_aStringStorage); + if(Length < Len) + Len = Length; + + str_copy(pResult->m_aStringStorage, pString, Length); + pStr = pResult->m_aStringStorage; + + // get command + pStr = SkipBlanks(pStr); + pResult->m_pCommand = pStr; + pStr = SkipToBlank(pStr); + + if(*pStr) + { + pStr[0] = 0; + pStr++; + } + + pResult->m_pArgsStart = pStr; + return 0; +} + +int CConsole::ParseArgs(CResult *pResult, const char *pFormat) +{ + char Command; + char *pStr; + int Optional = 0; + int Error = 0; + + pStr = pResult->m_pArgsStart; + + while(1) + { + // fetch command + Command = *pFormat; + pFormat++; + + if(!Command) + break; + + if(Command == '?') + Optional = 1; + else + { + pStr = SkipBlanks(pStr); + + if(!(*pStr)) // error, non optional command needs value + { + if(!Optional) + Error = 1; + break; + } + + // add token + if(*pStr == '"') + { + char *pDst; + pStr++; + pResult->AddArgument(pStr); + + pDst = pStr; // we might have to process escape data + while(1) + { + if(pStr[0] == '"') + break; + else if(pStr[0] == '\\') + { + if(pStr[1] == '\\') + pStr++; // skip due to escape + else if(pStr[1] == '"') + pStr++; // skip due to escape + } + else if(pStr[0] == 0) + return 1; // return error + + *pDst = *pStr; + pDst++; + pStr++; + } + + // write null termination + *pDst = 0; + + + pStr++; + } + else + { + pResult->AddArgument(pStr); + + if(Command == 'r') // rest of the string + break; + else if(Command == 'i') // validate int + pStr = SkipToBlank(pStr); + else if(Command == 'f') // validate float + pStr = SkipToBlank(pStr); + else if(Command == 's') // validate string + pStr = SkipToBlank(pStr); + + if(pStr[0] != 0) // check for end of string + { + pStr[0] = 0; + pStr++; + } + } + } + } + + return Error; +} + +void CConsole::RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) +{ + m_pfnPrintCallback = pfnPrintCallback; + m_pPrintCallbackUserdata = pUserData; +} + +void CConsole::Print(const char *pStr) +{ + dbg_msg("console" ,"%s", pStr); + if (m_pfnPrintCallback) + m_pfnPrintCallback(pStr, m_pPrintCallbackUserdata); +} + +void CConsole::ExecuteLineStroked(int Stroke, const char *pStr) +{ + CResult Result; + + char aStrokeStr[2] = {'0', 0}; + if(Stroke) + aStrokeStr[0] = '1'; + + while(pStr && *pStr) + { + const char *pEnd = pStr; + const char *pNextPart = 0; + int InString = 0; + + while(*pEnd) + { + if(*pEnd == '"') + InString ^= 1; + else if(*pEnd == '\\') // escape sequences + { + if(pEnd[1] == '"') + pEnd++; + } + else if(!InString) + { + if(*pEnd == ';') // command separator + { + pNextPart = pEnd+1; + break; + } + else if(*pEnd == '#') // comment, no need to do anything more + break; + } + + pEnd++; + } + + if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0) + return; + + CCommand *pCommand = FindCommand(Result.m_pCommand); + + if(pCommand) + { + int IsStrokeCommand = 0; + if(Result.m_pCommand[0] == '+') + { + // insert the stroke direction token + Result.AddArgument(aStrokeStr); + IsStrokeCommand = 1; + } + + if(Stroke || IsStrokeCommand) + { + if(ParseArgs(&Result, pCommand->m_pParams)) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams); + Print(aBuf); + } + else + pCommand->m_pfnCallback(&Result, pCommand->m_pUserData); + } + } + else + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "No such command: %s.", Result.m_pCommand); + Print(aBuf); + } + + pStr = pNextPart; + } +} + +void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) +{ + CCommand *pCommand; + for(pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) + { + if(pCommand->m_Flags&FlagMask) + { + if(str_find_nocase(pCommand->m_pName, pStr)) + pfnCallback(pCommand->m_pName, pUser); + } + } +} + +// TODO: this should regard the commands flag +CConsole::CCommand *CConsole::FindCommand(const char *pName) +{ + CCommand *pCommand; + for (pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext) + { + if(str_comp_nocase(pCommand->m_pName, pName) == 0) + return pCommand; + } + + return 0x0; +} + +void CConsole::ExecuteLine(const char *pStr) +{ + CConsole::ExecuteLineStroked(1, pStr); +} + + +void CConsole::ExecuteFile(const char *pFilename) +{ + // make sure that this isn't being executed already + for(CExecFile *pCur = m_pFirstExec; pCur; pCur = pCur->m_pPrev) + if(str_comp(pFilename, pCur->m_pFilename) == 0) + return; + + if(!m_pStorage) + m_pStorage = Kernel()->RequestInterface<IStorage>(); + if(!m_pStorage) + return; + + // push this one to the stack + CExecFile ThisFile; + CExecFile *pPrev = m_pFirstExec; + ThisFile.m_pFilename = pFilename; + ThisFile.m_pPrev = m_pFirstExec; + m_pFirstExec = &ThisFile; + + // exec the file + IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ); + + if(File) + { + char *pLine; + CLineReader lr; + + dbg_msg("console", "executing '%s'", pFilename); + lr.Init(File); + + while((pLine = lr.Get())) + ExecuteLine(pLine); + + io_close(File); + } + else + dbg_msg("console", "failed to open '%s'", pFilename); + + m_pFirstExec = pPrev; +} + +void CConsole::Con_Echo(IResult *pResult, void *pUserData) +{ + ((CConsole*)pUserData)->Print(pResult->GetString(0)); +} + +void CConsole::Con_Exec(IResult *pResult, void *pUserData) +{ + ((CConsole*)pUserData)->ExecuteFile(pResult->GetString(0)); +} + +struct CIntVariableData +{ + IConsole *m_pConsole; + int *m_pVariable; + int m_Min; + int m_Max; +}; + +struct CStrVariableData +{ + IConsole *m_pConsole; + char *m_pStr; + int m_MaxSize; +}; + +static void IntVariableCommand(IConsole::IResult *pResult, void *pUserData) +{ + CIntVariableData *pData = (CIntVariableData *)pUserData; + + if(pResult->NumArguments()) + { + int Val = pResult->GetInteger(0); + + // do clamping + if(pData->m_Min != pData->m_Max) + { + if (Val < pData->m_Min) + Val = pData->m_Min; + if (pData->m_Max != 0 && Val > pData->m_Max) + Val = pData->m_Max; + } + + *(pData->m_pVariable) = Val; + } + else + { + char aBuf[1024]; + str_format(aBuf, sizeof(aBuf), "Value: %d", *(pData->m_pVariable)); + pData->m_pConsole->Print(aBuf); + } +} + +static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData) +{ + CStrVariableData *pData = (CStrVariableData *)pUserData; + + if(pResult->NumArguments()) + str_copy(pData->m_pStr, pResult->GetString(0), pData->m_MaxSize); + else + { + char aBuf[1024]; + str_format(aBuf, sizeof(aBuf), "Value: %s", pData->m_pStr); + pData->m_pConsole->Print(aBuf); + } +} + +CConsole::CConsole() +{ + m_pFirstCommand = 0; + m_pFirstExec = 0; + m_pPrintCallbackUserdata = 0; + m_pfnPrintCallback = 0; + + m_pStorage = 0; + + // register some basic commands + Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text"); + Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file"); + + // TODO: this should disappear + #define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \ + { \ + static CIntVariableData Data = { this, &g_Config.m_##Name, Min, Max }; \ + Register(#ScriptName, "?i", Flags, IntVariableCommand, &Data, Desc); \ + } + + #define MACRO_CONFIG_STR(Name,ScriptName,Len,Def,Flags,Desc) \ + { \ + static CStrVariableData Data = { this, g_Config.m_##Name, Len }; \ + Register(#ScriptName, "?r", Flags, StrVariableCommand, &Data, Desc); \ + } + + #include "config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} + +void CConsole::ParseArguments(int NumArgs, const char **ppArguments) +{ + for(int i = 0; i < NumArgs; i++) + { + // check for scripts to execute + if(ppArguments[i][0] == '-' && ppArguments[i][1] == 'f' && ppArguments[i][2] == 0 && NumArgs - i > 1) + { + ExecuteFile(ppArguments[i+1]); + i++; + } + else + { + // search arguments for overrides + ExecuteLine(ppArguments[i]); + } + } +} + +void CConsole::Register(const char *pName, const char *pParams, + int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp) +{ + CCommand *pCommand = (CCommand *)mem_alloc(sizeof(CCommand), sizeof(void*)); + pCommand->m_pfnCallback = pfnFunc; + pCommand->m_pUserData = pUser; + pCommand->m_pHelp = pHelp; + pCommand->m_pName = pName; + pCommand->m_pParams = pParams; + pCommand->m_Flags = Flags; + + + pCommand->m_pNext = m_pFirstCommand; + m_pFirstCommand = pCommand; +} + +void CConsole::Con_Chain(IResult *pResult, void *pUserData) +{ + CChain *pInfo = (CChain *)pUserData; + pInfo->m_pfnChainCallback(pResult, pInfo->m_pUserData, pInfo->m_pfnCallback, pInfo->m_pCallbackUserData); +} + +void CConsole::Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser) +{ + CCommand *pCommand = FindCommand(pName); + + if(!pCommand) + { + dbg_msg("console", "failed to chain '%s'", pName); + return; + } + + CChain *pChainInfo = (CChain *)mem_alloc(sizeof(CChain), sizeof(void*)); + + // store info + pChainInfo->m_pfnChainCallback = pfnChainFunc; + pChainInfo->m_pUserData = pUser; + pChainInfo->m_pfnCallback = pCommand->m_pfnCallback; + pChainInfo->m_pCallbackUserData = pCommand->m_pUserData; + + // chain + pCommand->m_pfnCallback = Con_Chain; + pCommand->m_pUserData = pChainInfo; +} + + +IConsole::CCommandInfo *CConsole::GetCommandInfo(const char *pName) +{ + return FindCommand(pName); +} + + +extern IConsole *CreateConsole() { return new CConsole(); } diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h new file mode 100644 index 00000000..93d23547 --- /dev/null +++ b/src/engine/shared/console.h @@ -0,0 +1,96 @@ +#ifndef ENGINE_SHARED_CONSOLE_H +#define ENGINE_SHARED_CONSOLE_H + +#include <engine/console.h> + +class CConsole : public IConsole +{ + class CCommand : public CCommandInfo + { + public: + CCommand *m_pNext; + int m_Flags; + FCommandCallback m_pfnCallback; + void *m_pUserData; + }; + + + class CChain + { + public: + FChainCommandCallback m_pfnChainCallback; + FCommandCallback m_pfnCallback; + void *m_pCallbackUserData; + void *m_pUserData; + }; + + CCommand *m_pFirstCommand; + + class CExecFile + { + public: + const char *m_pFilename; + struct CExecFile *m_pPrev; + }; + + CExecFile *m_pFirstExec; + class IStorage *m_pStorage; + + static void Con_Chain(IResult *pResult, void *pUserData); + static void Con_Echo(IResult *pResult, void *pUserData); + static void Con_Exec(IResult *pResult, void *pUserData); + + void ExecuteFileRecurse(const char *pFilename); + void ExecuteLineStroked(int Stroke, const char *pStr); + + FPrintCallback m_pfnPrintCallback; + void *m_pPrintCallbackUserdata; + + enum + { + CONSOLE_MAX_STR_LENGTH = 1024, + MAX_PARTS = (CONSOLE_MAX_STR_LENGTH+1)/2 + }; + + class CResult : public IResult + { + public: + char m_aStringStorage[CONSOLE_MAX_STR_LENGTH+1]; + char *m_pArgsStart; + + const char *m_pCommand; + const char *m_apArgs[MAX_PARTS]; + + void AddArgument(const char *pArg) + { + m_apArgs[m_NumArgs++] = pArg; + } + + virtual const char *GetString(unsigned Index); + virtual int GetInteger(unsigned Index); + virtual float GetFloat(unsigned Index); + }; + + int ParseStart(CResult *pResult, const char *pString, int Length); + int ParseArgs(CResult *pResult, const char *pFormat); + + CCommand *FindCommand(const char *pName); + +public: + CConsole(); + + virtual CCommandInfo *GetCommandInfo(const char *pName); + virtual void PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser) ; + + virtual void ParseArguments(int NumArgs, const char **ppArguments); + virtual void Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp); + virtual void Chain(const char *pName, FChainCommandCallback pfnChainFunc, void *pUser); + + virtual void ExecuteLine(const char *pStr); + virtual void ExecuteFile(const char *pFilename); + + virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData); + virtual void Print(const char *pStr); +}; + +#endif diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp new file mode 100644 index 00000000..dcc32ef2 --- /dev/null +++ b/src/engine/shared/datafile.cpp @@ -0,0 +1,643 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> +#include <engine/storage.h> +#include "datafile.h" +#include "engine.h" +#include <zlib.h> + +static const int DEBUG=0; + +struct CDatafileItemType +{ + int m_Type; + int m_Start; + int m_Num; +} ; + +struct CDatafileItem +{ + int m_TypeAndId; + int m_Size; +}; + +struct CDatafileHeader +{ + char m_aId[4]; + int m_Version; + int m_Size; + int m_Swaplen; + int m_NumItemTypes; + int m_NumItems; + int m_NumRawData; + int m_ItemSize; + int m_DataSize; +}; + +struct CDatafileData +{ + int m_NumItemTypes; + int m_NumItems; + int m_NumRawData; + int m_ItemSize; + int m_DataSize; + char m_aStart[4]; +}; + +struct CDatafileInfo +{ + CDatafileItemType *m_pItemTypes; + int *m_pItemOffsets; + int *m_pDataOffsets; + int *m_pDataSizes; + + char *m_pItemStart; + char *m_pDataStart; +}; + +struct CDatafile +{ + IOHANDLE m_File; + unsigned m_Crc; + CDatafileInfo m_Info; + CDatafileHeader m_Header; + int m_DataStartOffset; + char **m_ppDataPtrs; + char *m_pData; +}; + +bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename) +{ + dbg_msg("datafile", "loading. filename='%s'", pFilename); + + IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ); + if(!File) + { + dbg_msg("datafile", "could not open '%s'", pFilename); + return false; + } + + + // take the CRC of the file and store it + unsigned Crc = 0; + { + enum + { + BUFFER_SIZE = 64*1024 + }; + + unsigned char aBuffer[BUFFER_SIZE]; + + while(1) + { + unsigned Bytes = io_read(File, aBuffer, BUFFER_SIZE); + if(Bytes <= 0) + break; + Crc = crc32(Crc, aBuffer, Bytes); // ignore_convention + } + + io_seek(File, 0, IOSEEK_START); + } + + + // TODO: change this header + CDatafileHeader Header; + io_read(File, &Header, sizeof(Header)); + if(Header.m_aId[0] != 'A' || Header.m_aId[1] != 'T' || Header.m_aId[2] != 'A' || Header.m_aId[3] != 'D') + { + if(Header.m_aId[0] != 'D' || Header.m_aId[1] != 'A' || Header.m_aId[2] != 'T' || Header.m_aId[3] != 'A') + { + dbg_msg("datafile", "wrong signature. %x %x %x %x", Header.m_aId[0], Header.m_aId[1], Header.m_aId[2], Header.m_aId[3]); + return 0; + } + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(&Header, sizeof(int), sizeof(Header)/sizeof(int)); +#endif + if(Header.m_Version != 3 && Header.m_Version != 4) + { + dbg_msg("datafile", "wrong version. version=%x", Header.m_Version); + return 0; + } + + // read in the rest except the data + unsigned Size = 0; + Size += Header.m_NumItemTypes*sizeof(CDatafileItemType); + Size += (Header.m_NumItems+Header.m_NumRawData)*sizeof(int); + if(Header.m_Version == 4) + Size += Header.m_NumRawData*sizeof(int); // v4 has uncompressed data sizes aswell + Size += Header.m_ItemSize; + + unsigned AllocSize = Size; + AllocSize += sizeof(CDatafile); // add space for info structure + AllocSize += Header.m_NumRawData*sizeof(void*); // add space for data pointers + + m_pDataFile = (CDatafile*)mem_alloc(AllocSize, 1); + m_pDataFile->m_Header = Header; + m_pDataFile->m_DataStartOffset = sizeof(CDatafileHeader) + Size; + m_pDataFile->m_ppDataPtrs = (char**)(m_pDataFile+1); + m_pDataFile->m_pData = (char *)(m_pDataFile+1)+Header.m_NumRawData*sizeof(char *); + m_pDataFile->m_File = File; + m_pDataFile->m_Crc = Crc; + + // clear the data pointers + mem_zero(m_pDataFile->m_ppDataPtrs, Header.m_NumRawData*sizeof(void*)); + + // read types, offsets, sizes and item data + unsigned ReadSize = io_read(File, m_pDataFile->m_pData, Size); + if(ReadSize != Size) + { + mem_free(m_pDataFile); + m_pDataFile = 0; + dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", Size, ReadSize); + return false; + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(m_pDataFile->m_pData, sizeof(int), Header.Swaplen / sizeof(int)); +#endif + + //if(DEBUG) + { + dbg_msg("datafile", "allocsize=%d", AllocSize); + dbg_msg("datafile", "readsize=%d", ReadSize); + dbg_msg("datafile", "swaplen=%d", Header.m_Swaplen); + dbg_msg("datafile", "item_size=%d", m_pDataFile->m_Header.m_ItemSize); + } + + m_pDataFile->m_Info.m_pItemTypes = (CDatafileItemType *)m_pDataFile->m_pData; + m_pDataFile->m_Info.m_pItemOffsets = (int *)&m_pDataFile->m_Info.m_pItemTypes[m_pDataFile->m_Header.m_NumItemTypes]; + m_pDataFile->m_Info.m_pDataOffsets = (int *)&m_pDataFile->m_Info.m_pItemOffsets[m_pDataFile->m_Header.m_NumItems]; + m_pDataFile->m_Info.m_pDataSizes = (int *)&m_pDataFile->m_Info.m_pDataOffsets[m_pDataFile->m_Header.m_NumRawData]; + + if(Header.m_Version == 4) + m_pDataFile->m_Info.m_pItemStart = (char *)&m_pDataFile->m_Info.m_pDataSizes[m_pDataFile->m_Header.m_NumRawData]; + else + m_pDataFile->m_Info.m_pItemStart = (char *)&m_pDataFile->m_Info.m_pDataOffsets[m_pDataFile->m_Header.m_NumRawData]; + m_pDataFile->m_Info.m_pDataStart = m_pDataFile->m_Info.m_pItemStart + m_pDataFile->m_Header.m_ItemSize; + + dbg_msg("datafile", "loading done. datafile='%s'", pFilename); + + if(DEBUG) + { + /* + for(int i = 0; i < m_pDataFile->data.num_raw_data; i++) + { + void *p = datafile_get_data(df, i); + dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&m_pDataFile->data)), size); + } + + for(int i = 0; i < datafile_num_items(df); i++) + { + int type, id; + void *data = datafile_get_item(df, i, &type, &id); + dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, m_pDataFile->info.item_offsets[i]); + int *idata = (int*)data; + for(int k = 0; k < 3; k++) + dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); + } + + for(int i = 0; i < m_pDataFile->data.num_m_aItemTypes; i++) + { + dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, + m_pDataFile->info.m_aItemTypes[i].type, + m_pDataFile->info.m_aItemTypes[i].start, + m_pDataFile->info.m_aItemTypes[i].num); + for(int k = 0; k < m_pDataFile->info.m_aItemTypes[i].num; k++) + { + int type, id; + datafile_get_item(df, m_pDataFile->info.m_aItemTypes[i].start+k, &type, &id); + if(type != m_pDataFile->info.m_aItemTypes[i].type) + dbg_msg("map", "\tERROR"); + } + } + */ + } + + return true; +} + +int CDataFileReader::NumData() +{ + if(!m_pDataFile) { return 0; } + return m_pDataFile->m_Header.m_NumRawData; +} + +// always returns the size in the file +int CDataFileReader::GetDataSize(int Index) +{ + if(!m_pDataFile) { return 0; } + + if(Index == m_pDataFile->m_Header.m_NumRawData-1) + return m_pDataFile->m_Header.m_DataSize-m_pDataFile->m_Info.m_pDataOffsets[Index]; + return m_pDataFile->m_Info.m_pDataOffsets[Index+1]-m_pDataFile->m_Info.m_pDataOffsets[Index]; +} + +void *CDataFileReader::GetDataImpl(int Index, int Swap) +{ + if(!m_pDataFile) { return 0; } + + // load it if needed + if(!m_pDataFile->m_ppDataPtrs[Index]) + { + // fetch the data size + int DataSize = GetDataSize(Index); + int SwapSize = DataSize; + + if(m_pDataFile->m_Header.m_Version == 4) + { + // v4 has compressed data + void *pTemp = (char *)mem_alloc(DataSize, 1); + unsigned long UncompressedSize = m_pDataFile->m_Info.m_pDataSizes[Index]; + unsigned long s; + + dbg_msg("datafile", "loading data index=%d size=%d uncompressed=%d", Index, DataSize, UncompressedSize); + m_pDataFile->m_ppDataPtrs[Index] = (char *)mem_alloc(UncompressedSize, 1); + + // read the compressed data + io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset+m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START); + io_read(m_pDataFile->m_File, pTemp, DataSize); + + // decompress the data, TODO: check for errors + s = UncompressedSize; + uncompress((Bytef*)m_pDataFile->m_ppDataPtrs[Index], &s, (Bytef*)pTemp, DataSize); // ignore_convention + SwapSize = s; + + // clean up the temporary buffers + mem_free(pTemp); + } + else + { + // load the data + dbg_msg("datafile", "loading data index=%d size=%d", Index, DataSize); + m_pDataFile->m_ppDataPtrs[Index] = (char *)mem_alloc(DataSize, 1); + io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset+m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START); + io_read(m_pDataFile->m_File, m_pDataFile->m_ppDataPtrs[Index], DataSize); + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + if(Swap && SwapSize) + swap_endian(m_pDataFile->m_ppDataPtrs[Index], sizeof(int), SwapSize/sizeof(int)); +#endif + } + + return m_pDataFile->m_ppDataPtrs[Index]; +} + +void *CDataFileReader::GetData(int Index) +{ + return GetDataImpl(Index, 0); +} + +void *CDataFileReader::GetDataSwapped(int Index) +{ + return GetDataImpl(Index, 1); +} + +void CDataFileReader::UnloadData(int Index) +{ + if(Index < 0) + return; + + // + mem_free(m_pDataFile->m_ppDataPtrs[Index]); + m_pDataFile->m_ppDataPtrs[Index] = 0x0; +} + +int CDataFileReader::GetItemSize(int Index) +{ + if(!m_pDataFile) { return 0; } + if(Index == m_pDataFile->m_Header.m_NumItems-1) + return m_pDataFile->m_Header.m_ItemSize-m_pDataFile->m_Info.m_pItemOffsets[Index]; + return m_pDataFile->m_Info.m_pItemOffsets[Index+1]-m_pDataFile->m_Info.m_pItemOffsets[Index]; +} + +void *CDataFileReader::GetItem(int Index, int *pType, int *pId) +{ + if(!m_pDataFile) { if(pType) *pType = 0; if(pId) *pId = 0; return 0; } + + CDatafileItem *i = (CDatafileItem *)(m_pDataFile->m_Info.m_pItemStart+m_pDataFile->m_Info.m_pItemOffsets[Index]); + if(pType) + *pType = (i->m_TypeAndId>>16)&0xffff; // remove sign extention + if(pId) + *pId = i->m_TypeAndId&0xffff; + return (void *)(i+1); +} + +void CDataFileReader::GetType(int Type, int *pStart, int *pNum) +{ + *pStart = 0; + *pNum = 0; + + if(!m_pDataFile) + return; + + for(int i = 0; i < m_pDataFile->m_Header.m_NumItemTypes; i++) + { + if(m_pDataFile->m_Info.m_pItemTypes[i].m_Type == Type) + { + *pStart = m_pDataFile->m_Info.m_pItemTypes[i].m_Start; + *pNum = m_pDataFile->m_Info.m_pItemTypes[i].m_Num; + return; + } + } +} + +void *CDataFileReader::FindItem(int Type, int Id) +{ + if(!m_pDataFile) return 0; + + int Start, Num; + GetType(Type, &Start, &Num); + for(int i = 0; i < Num; i++) + { + int ItemId; + void *pItem = GetItem(Start+i,0, &ItemId); + if(Id == ItemId) + return pItem; + } + return 0; +} + +int CDataFileReader::NumItems() +{ + if(!m_pDataFile) return 0; + return m_pDataFile->m_Header.m_NumItems; +} + +bool CDataFileReader::Close() +{ + if(!m_pDataFile) + return true; + + // free the data that is loaded + int i; + for(i = 0; i < m_pDataFile->m_Header.m_NumRawData; i++) + mem_free(m_pDataFile->m_ppDataPtrs[i]); + + io_close(m_pDataFile->m_File); + mem_free(m_pDataFile); + m_pDataFile = 0; + return true; +} + +unsigned CDataFileReader::Crc() +{ + if(!m_pDataFile) return -1; + return m_pDataFile->m_Crc; +} + +bool CDataFileWriter::Open(class IStorage *pStorage, const char *pFilename) +{ + int i; + //DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); + m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE); + if(!m_File) + return false; + + m_NumItems = 0; + m_NumDatas = 0; + m_NumItemTypes = 0; + mem_zero(&m_aItemTypes, sizeof(m_aItemTypes)); + + for(i = 0; i < 0xffff; i++) + { + m_aItemTypes[i].m_First = -1; + m_aItemTypes[i].m_Last = -1; + } + + return true; +} + +int CDataFileWriter::AddItem(int Type, int Id, int Size, void *pData) +{ + m_aItems[m_NumItems].m_Type = Type; + m_aItems[m_NumItems].m_Id = Id; + m_aItems[m_NumItems].m_Size = Size; + + /* + dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); + int i; + for(i = 0; i < size/4; i++) + dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); + */ + + // copy data + m_aItems[m_NumItems].m_pData = mem_alloc(Size, 1); + mem_copy(m_aItems[m_NumItems].m_pData, pData, Size); + + if(!m_aItemTypes[Type].m_Num) // count item types + m_NumItemTypes++; + + // link + m_aItems[m_NumItems].m_Prev = m_aItemTypes[Type].m_Last; + m_aItems[m_NumItems].m_Next = -1; + + if(m_aItemTypes[Type].m_Last != -1) + m_aItems[m_aItemTypes[Type].m_Last].m_Next = m_NumItems; + m_aItemTypes[Type].m_Last = m_NumItems; + + if(m_aItemTypes[Type].m_First == -1) + m_aItemTypes[Type].m_First = m_NumItems; + + m_aItemTypes[Type].m_Num++; + + m_NumItems++; + return m_NumItems-1; +} + +int CDataFileWriter::AddData(int Size, void *pData) +{ + CDataInfo *pInfo = &m_aDatas[m_NumDatas]; + unsigned long s = compressBound(Size); + void *pCompData = mem_alloc(s, 1); // temporary buffer that we use duing compression + + int Result = compress((Bytef*)pCompData, &s, (Bytef*)pData, Size); // ignore_convention + if(Result != Z_OK) + { + dbg_msg("datafile", "compression error %d", Result); + dbg_assert(0, "zlib error"); + } + + pInfo->m_UncompressedSize = Size; + pInfo->m_CompressedSize = (int)s; + pInfo->m_pCompressedData = mem_alloc(pInfo->m_CompressedSize, 1); + mem_copy(pInfo->m_pCompressedData, pCompData, pInfo->m_CompressedSize); + mem_free(pCompData); + + m_NumDatas++; + return m_NumDatas-1; +} + +int CDataFileWriter::AddDataSwapped(int Size, void *pData) +{ +#if defined(CONF_ARCH_ENDIAN_BIG) + void *pSwapped = mem_alloc(Size, 1); // temporary buffer that we use duing compression + int Index; + mem_copy(pSwapped, pData, Size); + swap_endian(&pSwapped, sizeof(int), Size/sizeof(int)); + Index = AddData(Size, Swapped); + mem_free(pSwapped); + return Index; +#else + return AddData(Size, pData); +#endif +} + + +int CDataFileWriter::Finish() +{ + int ItemSize = 0; + int TypesSize, HeaderSize, OffsetSize, FileSize, SwapSize; + int DataSize = 0; + CDatafileHeader Header; + + // we should now write this file! + if(DEBUG) + dbg_msg("datafile", "writing"); + + // calculate sizes + for(int i = 0; i < m_NumItems; i++) + { + if(DEBUG) + dbg_msg("datafile", "item=%d size=%d (%d)", i, m_aItems[i].m_Size, m_aItems[i].m_Size+sizeof(CDatafileItem)); + ItemSize += m_aItems[i].m_Size + sizeof(CDatafileItem); + } + + + for(int i = 0; i < m_NumDatas; i++) + DataSize += m_aDatas[i].m_CompressedSize; + + // calculate the complete size + TypesSize = m_NumItemTypes*sizeof(CDatafileItemType); + HeaderSize = sizeof(CDatafileHeader); + OffsetSize = m_NumItems*sizeof(int) + m_NumDatas*sizeof(int); + FileSize = HeaderSize + TypesSize + OffsetSize + ItemSize + DataSize; + SwapSize = FileSize - DataSize; + + (void)SwapSize; + + if(DEBUG) + dbg_msg("datafile", "num_m_aItemTypes=%d TypesSize=%d m_aItemsize=%d DataSize=%d", m_NumItemTypes, TypesSize, ItemSize, DataSize); + + // construct Header + { + Header.m_aId[0] = 'D'; + Header.m_aId[1] = 'A'; + Header.m_aId[2] = 'T'; + Header.m_aId[3] = 'A'; + Header.m_Version = 4; + Header.m_Size = FileSize - 16; + Header.m_Swaplen = SwapSize - 16; + Header.m_NumItemTypes = m_NumItemTypes; + Header.m_NumItems = m_NumItems; + Header.m_NumRawData = m_NumDatas; + Header.m_ItemSize = ItemSize; + Header.m_DataSize = DataSize; + + // TODO: apply swapping + // write Header + if(DEBUG) + dbg_msg("datafile", "HeaderSize=%d", sizeof(Header)); + io_write(m_File, &Header, sizeof(Header)); + } + + // write types + for(int i = 0, Count = 0; i < 0xffff; i++) + { + if(m_aItemTypes[i].m_Num) + { + // write info + CDatafileItemType Info; + Info.m_Type = i; + Info.m_Start = Count; + Info.m_Num = m_aItemTypes[i].m_Num; + if(DEBUG) + dbg_msg("datafile", "writing type=%x start=%d num=%d", Info.m_Type, Info.m_Start, Info.m_Num); + io_write(m_File, &Info, sizeof(Info)); + Count += m_aItemTypes[i].m_Num; + } + } + + // write item offsets + for(int i = 0, Offset = 0; i < 0xffff; i++) + { + if(m_aItemTypes[i].m_Num) + { + // write all m_aItems in of this type + int k = m_aItemTypes[i].m_First; + while(k != -1) + { + if(DEBUG) + dbg_msg("datafile", "writing item offset num=%d offset=%d", k, Offset); + io_write(m_File, &Offset, sizeof(Offset)); + Offset += m_aItems[k].m_Size + sizeof(CDatafileItem); + + // next + k = m_aItems[k].m_Next; + } + } + } + + // write data offsets + for(int i = 0, Offset = 0; i < m_NumDatas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, Offset); + io_write(m_File, &Offset, sizeof(Offset)); + Offset += m_aDatas[i].m_CompressedSize; + } + + // write data uncompressed sizes + for(int i = 0; i < m_NumDatas; i++) + { + /* + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + */ + io_write(m_File, &m_aDatas[i].m_UncompressedSize, sizeof(int)); + } + + // write m_aItems + for(int i = 0; i < 0xffff; i++) + { + if(m_aItemTypes[i].m_Num) + { + // write all m_aItems in of this type + int k = m_aItemTypes[i].m_First; + while(k != -1) + { + CDatafileItem Item; + Item.m_TypeAndId = (i<<16)|m_aItems[k].m_Id; + Item.m_Size = m_aItems[k].m_Size; + if(DEBUG) + dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, m_aItems[k].m_Id, m_aItems[k].m_Size); + + io_write(m_File, &Item, sizeof(Item)); + io_write(m_File, m_aItems[k].m_pData, m_aItems[k].m_Size); + + // next + k = m_aItems[k].m_Next; + } + } + } + + // write data + for(int i = 0; i < m_NumDatas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data id=%d size=%d", i, m_aDatas[i].m_CompressedSize); + io_write(m_File, m_aDatas[i].m_pCompressedData, m_aDatas[i].m_CompressedSize); + } + + // free data + for(int i = 0; i < m_NumItems; i++) + mem_free(m_aItems[i].m_pData); + + + io_close(m_File); + + if(DEBUG) + dbg_msg("datafile", "done"); + return 0; +} diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h new file mode 100644 index 00000000..eddce611 --- /dev/null +++ b/src/engine/shared/datafile.h @@ -0,0 +1,77 @@ +#ifndef ENGINE_SHARED_DATAFILE_H +#define ENGINE_SHARED_DATAFILE_H + +// raw datafile access +class CDataFileReader +{ + class CDatafile *m_pDataFile; + void *GetDataImpl(int Index, int Swap); +public: + CDataFileReader() : m_pDataFile(0) {} + ~CDataFileReader() { Close(); } + + bool IsOpen() const { return m_pDataFile != 0; } + + bool Open(class IStorage *pStorage, const char *pFilename); + bool Close(); + + void *GetData(int Index); + void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved + int GetDataSize(int Index); + void UnloadData(int Index); + void *GetItem(int Index, int *pType, int *pId); + int GetItemSize(int Index); + void GetType(int Type, int *pStart, int *pNum); + void *FindItem(int Type, int Id); + int NumItems(); + int NumData(); + void Unload(); + + unsigned Crc(); +}; + +// write access +class CDataFileWriter +{ + struct CDataInfo + { + int m_UncompressedSize; + int m_CompressedSize; + void *m_pCompressedData; + } ; + + struct CItemInfo + { + int m_Type; + int m_Id; + int m_Size; + int m_Next; + int m_Prev; + void *m_pData; + }; + + struct CItemTypeInfo + { + int m_Num; + int m_First; + int m_Last; + }; + + IOHANDLE m_File; + int m_NumItems; + int m_NumDatas; + int m_NumItemTypes; + CItemTypeInfo m_aItemTypes[0xffff]; + CItemInfo m_aItems[1024]; + CDataInfo m_aDatas[1024]; + +public: + bool Open(class IStorage *pStorage, const char *Filename); + int AddData(int Size, void *pData); + int AddDataSwapped(int Size, void *pData); + int AddItem(int Type, int Id, int Size, void *pData); + int Finish(); +}; + + +#endif diff --git a/src/engine/shared/demorec.cpp b/src/engine/shared/demorec.cpp new file mode 100644 index 00000000..48b06e9a --- /dev/null +++ b/src/engine/shared/demorec.cpp @@ -0,0 +1,623 @@ +#include <base/system.h> +#include <engine/shared/protocol.h> +#include <engine/storage.h> +#include "demorec.h" +#include "memheap.h" +#include "snapshot.h" +#include "compression.h" +#include "network.h" +#include "engine.h" + +static const unsigned char gs_aHeaderMarker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1}; + +CDemoRecorder::CDemoRecorder(class CSnapshotDelta *pSnapshotDelta) +{ + m_File = 0; + m_LastTickMarker = -1; + m_pSnapshotDelta = pSnapshotDelta; +} + +//static IOHANDLE m_File = 0; + +// Record +int CDemoRecorder::Start(class IStorage *pStorage, const char *pFilename, const char *pNetVersion, const char *pMap, int Crc, const char *pType) +{ + CDemoHeader Header; + if(m_File) + return -1; + + m_File = pStorage->OpenFile(pFilename, IOFLAG_WRITE); + + if(!m_File) + { + dbg_msg("demorec/record", "Unable to open '%s' for recording", pFilename); + return -1; + } + + // write header + mem_zero(&Header, sizeof(Header)); + mem_copy(Header.m_aMarker, gs_aHeaderMarker, sizeof(Header.m_aMarker)); + str_copy(Header.m_aNetversion, pNetVersion, sizeof(Header.m_aNetversion)); + str_copy(Header.m_aMap, pMap, sizeof(Header.m_aMap)); + str_copy(Header.m_aType, pType, sizeof(Header.m_aType)); + Header.m_aCrc[0] = (Crc>>24)&0xff; + Header.m_aCrc[1] = (Crc>>16)&0xff; + Header.m_aCrc[2] = (Crc>>8)&0xff; + Header.m_aCrc[3] = (Crc)&0xff; + io_write(m_File, &Header, sizeof(Header)); + + m_LastKeyFrame = -1; + m_LastTickMarker = -1; + + dbg_msg("demorec/record", "Recording to '%s'", pFilename); + return 0; +} + +/* + Tickmarker + 7 = Always set + 6 = Keyframe flag + 0-5 = Delta tick + + Normal + 7 = Not set + 5-6 = Type + 0-4 = Size +*/ + +enum +{ + CHUNKTYPEFLAG_TICKMARKER = 0x80, + CHUNKTICKFLAG_KEYFRAME = 0x40, // only when tickmarker is set + + CHUNKMASK_TICK = 0x3f, + CHUNKMASK_TYPE = 0x60, + CHUNKMASK_SIZE = 0x1f, + + CHUNKTYPE_SNAPSHOT = 1, + CHUNKTYPE_MESSAGE = 2, + CHUNKTYPE_DELTA = 3, + + CHUNKFLAG_BIGSIZE = 0x10 +}; + +void CDemoRecorder::WriteTickMarker(int Tick, int Keyframe) +{ + if(m_LastTickMarker == -1 || Tick-m_LastTickMarker > 63 || Keyframe) + { + unsigned char aChunk[5]; + aChunk[0] = CHUNKTYPEFLAG_TICKMARKER; + aChunk[1] = (Tick>>24)&0xff; + aChunk[2] = (Tick>>16)&0xff; + aChunk[3] = (Tick>>8)&0xff; + aChunk[4] = (Tick)&0xff; + + if(Keyframe) + aChunk[0] |= CHUNKTICKFLAG_KEYFRAME; + + io_write(m_File, aChunk, sizeof(aChunk)); + } + else + { + unsigned char aChunk[1]; + aChunk[0] = CHUNKTYPEFLAG_TICKMARKER | (Tick-m_LastTickMarker); + io_write(m_File, aChunk, sizeof(aChunk)); + } + + m_LastTickMarker = Tick; +} + +void CDemoRecorder::Write(int Type, const void *pData, int Size) +{ + char aBuffer[64*1024]; + char aBuffer2[64*1024]; + unsigned char aChunk[3]; + + if(!m_File) + return; + + /* pad the data with 0 so we get an alignment of 4, + else the compression won't work and miss some bytes */ + mem_copy(aBuffer2, pData, Size); + while(Size&3) + aBuffer2[Size++] = 0; + Size = CVariableInt::Compress(aBuffer2, Size, aBuffer); // buffer2 -> buffer + Size = CNetBase::Compress(aBuffer, Size, aBuffer2, sizeof(aBuffer2)); // buffer -> buffer2 + + + aChunk[0] = ((Type&0x3)<<5); + if(Size < 30) + { + aChunk[0] |= Size; + io_write(m_File, aChunk, 1); + } + else + { + if(Size < 256) + { + aChunk[0] |= 30; + aChunk[1] = Size&0xff; + io_write(m_File, aChunk, 2); + } + else + { + aChunk[0] |= 31; + aChunk[1] = Size&0xff; + aChunk[2] = Size>>8; + io_write(m_File, aChunk, 3); + } + } + + io_write(m_File, aBuffer2, Size); +} + +void CDemoRecorder::RecordSnapshot(int Tick, const void *pData, int Size) +{ + if(m_LastKeyFrame == -1 || (Tick-m_LastKeyFrame) > SERVER_TICK_SPEED*5) + { + // write full tickmarker + WriteTickMarker(Tick, 1); + + // write snapshot + Write(CHUNKTYPE_SNAPSHOT, pData, Size); + + m_LastKeyFrame = Tick; + mem_copy(m_aLastSnapshotData, pData, Size); + } + else + { + // create delta, prepend tick + char aDeltaData[CSnapshot::MAX_SIZE+sizeof(int)]; + int DeltaSize; + + // write tickmarker + WriteTickMarker(Tick, 0); + + DeltaSize = m_pSnapshotDelta->CreateDelta((CSnapshot*)m_aLastSnapshotData, (CSnapshot*)pData, &aDeltaData); + if(DeltaSize) + { + // record delta + Write(CHUNKTYPE_DELTA, aDeltaData, DeltaSize); + mem_copy(m_aLastSnapshotData, pData, Size); + } + } +} + +void CDemoRecorder::RecordMessage(const void *pData, int Size) +{ + Write(CHUNKTYPE_MESSAGE, pData, Size); +} + +int CDemoRecorder::Stop() +{ + if(!m_File) + return -1; + + dbg_msg("demorec/record", "Stopped recording"); + io_close(m_File); + m_File = 0; + return 0; +} + + + +CDemoPlayer::CDemoPlayer(class CSnapshotDelta *pSnapshotDelta) +{ + m_File = 0; + m_pKeyFrames = 0; + + m_pSnapshotDelta = pSnapshotDelta; + m_LastSnapshotDataSize = -1; +} + +void CDemoPlayer::SetListner(IListner *pListner) +{ + m_pListner = pListner; +} + + +int CDemoPlayer::ReadChunkHeader(int *pType, int *pSize, int *pTick) +{ + unsigned char Chunk = 0; + + *pSize = 0; + *pType = 0; + + if(io_read(m_File, &Chunk, sizeof(Chunk)) != sizeof(Chunk)) + return -1; + + if(Chunk&CHUNKTYPEFLAG_TICKMARKER) + { + // decode tick marker + int Tickdelta = Chunk&(CHUNKMASK_TICK); + *pType = Chunk&(CHUNKTYPEFLAG_TICKMARKER|CHUNKTICKFLAG_KEYFRAME); + + if(Tickdelta == 0) + { + unsigned char aTickdata[4]; + if(io_read(m_File, aTickdata, sizeof(aTickdata)) != sizeof(aTickdata)) + return -1; + *pTick = (aTickdata[0]<<24) | (aTickdata[1]<<16) | (aTickdata[2]<<8) | aTickdata[3]; + } + else + { + *pTick += Tickdelta; + } + + } + else + { + // decode normal chunk + *pType = (Chunk&CHUNKMASK_TYPE)>>5; + *pSize = Chunk&CHUNKMASK_SIZE; + + if(*pSize == 30) + { + unsigned char aSizedata[1]; + if(io_read(m_File, aSizedata, sizeof(aSizedata)) != sizeof(aSizedata)) + return -1; + *pSize = aSizedata[0]; + + } + else if(*pSize == 31) + { + unsigned char aSizedata[2]; + if(io_read(m_File, aSizedata, sizeof(aSizedata)) != sizeof(aSizedata)) + return -1; + *pSize = (aSizedata[1]<<8) | aSizedata[0]; + } + } + + return 0; +} + +void CDemoPlayer::ScanFile() +{ + long StartPos; + CHeap Heap; + CKeyFrameSearch *pFirstKey = 0; + CKeyFrameSearch *pCurrentKey = 0; + //DEMOREC_CHUNK chunk; + int ChunkSize, ChunkType, ChunkTick = 0; + int i; + + StartPos = io_tell(m_File); + m_Info.m_SeekablePoints = 0; + + while(1) + { + long CurrentPos = io_tell(m_File); + + if(ReadChunkHeader(&ChunkType, &ChunkSize, &ChunkTick)) + break; + + // read the chunk + if(ChunkType&CHUNKTYPEFLAG_TICKMARKER) + { + if(ChunkType&CHUNKTICKFLAG_KEYFRAME) + { + CKeyFrameSearch *pKey; + + // save the position + pKey = (CKeyFrameSearch *)Heap.Allocate(sizeof(CKeyFrameSearch)); + pKey->m_Frame.m_Filepos = CurrentPos; + pKey->m_Frame.m_Tick = ChunkTick; + pKey->m_pNext = 0; + if(pCurrentKey) + pCurrentKey->m_pNext = pKey; + if(!pFirstKey) + pFirstKey = pKey; + pCurrentKey = pKey; + m_Info.m_SeekablePoints++; + } + + if(m_Info.m_Info.m_FirstTick == -1) + m_Info.m_Info.m_FirstTick = ChunkTick; + m_Info.m_Info.m_LastTick = ChunkTick; + } + else if(ChunkSize) + io_skip(m_File, ChunkSize); + + } + + // copy all the frames to an array instead for fast access + m_pKeyFrames = (CKeyFrame*)mem_alloc(m_Info.m_SeekablePoints*sizeof(CKeyFrame), 1); + for(pCurrentKey = pFirstKey, i = 0; pCurrentKey; pCurrentKey = pCurrentKey->m_pNext, i++) + m_pKeyFrames[i] = pCurrentKey->m_Frame; + + // destroy the temporary heap and seek back to the start + io_seek(m_File, StartPos, IOSEEK_START); +} + +void CDemoPlayer::DoTick() +{ + static char aCompresseddata[CSnapshot::MAX_SIZE]; + static char aDecompressed[CSnapshot::MAX_SIZE]; + static char aData[CSnapshot::MAX_SIZE]; + int ChunkType, ChunkTick, ChunkSize; + int DataSize; + int GotSnapshot = 0; + + // update ticks + m_Info.m_PreviousTick = m_Info.m_Info.m_CurrentTick; + m_Info.m_Info.m_CurrentTick = m_Info.m_NextTick; + ChunkTick = m_Info.m_Info.m_CurrentTick; + + while(1) + { + if(ReadChunkHeader(&ChunkType, &ChunkSize, &ChunkTick)) + { + // stop on error or eof + dbg_msg("demorec", "end of file"); + Pause(); + break; + } + + // read the chunk + if(ChunkSize) + { + if(io_read(m_File, aCompresseddata, ChunkSize) != (unsigned)ChunkSize) + { + // stop on error or eof + dbg_msg("demorec", "error reading chunk"); + Stop(); + break; + } + + DataSize = CNetBase::Decompress(aCompresseddata, ChunkSize, aDecompressed, sizeof(aDecompressed)); + if(DataSize < 0) + { + // stop on error or eof + dbg_msg("demorec", "error during network decompression"); + Stop(); + break; + } + + DataSize = CVariableInt::Decompress(aDecompressed, DataSize, aData); + + if(DataSize < 0) + { + dbg_msg("demorec", "error during intpack decompression"); + Stop(); + break; + } + } + + if(ChunkType == CHUNKTYPE_DELTA) + { + // process delta snapshot + static char aNewsnap[CSnapshot::MAX_SIZE]; + + GotSnapshot = 1; + + DataSize = m_pSnapshotDelta->UnpackDelta((CSnapshot*)m_aLastSnapshotData, (CSnapshot*)aNewsnap, aData, DataSize); + + if(DataSize >= 0) + { + if(m_pListner) + m_pListner->OnDemoPlayerSnapshot(aNewsnap, DataSize); + + m_LastSnapshotDataSize = DataSize; + mem_copy(m_aLastSnapshotData, aNewsnap, DataSize); + } + else + dbg_msg("demorec", "error duing unpacking of delta, err=%d", DataSize); + } + else if(ChunkType == CHUNKTYPE_SNAPSHOT) + { + // process full snapshot + GotSnapshot = 1; + + m_LastSnapshotDataSize = DataSize; + mem_copy(m_aLastSnapshotData, aData, DataSize); + if(m_pListner) + m_pListner->OnDemoPlayerSnapshot(aData, DataSize); + } + else + { + // if there were no snapshots in this tick, replay the last one + if(!GotSnapshot && m_pListner && m_LastSnapshotDataSize != -1) + { + GotSnapshot = 1; + m_pListner->OnDemoPlayerSnapshot(m_aLastSnapshotData, m_LastSnapshotDataSize); + } + + // check the remaining types + if(ChunkType&CHUNKTYPEFLAG_TICKMARKER) + { + m_Info.m_NextTick = ChunkTick; + break; + } + else if(ChunkType == CHUNKTYPE_MESSAGE) + { + if(m_pListner) + m_pListner->OnDemoPlayerMessage(aData, DataSize); + } + } + } +} + +void CDemoPlayer::Pause() +{ + m_Info.m_Info.m_Paused = 1; +} + +void CDemoPlayer::Unpause() +{ + if(m_Info.m_Info.m_Paused) + { + /*m_Info.start_tick = m_Info.current_tick; + m_Info.start_time = time_get();*/ + m_Info.m_Info.m_Paused = 0; + } +} + +int CDemoPlayer::Load(class IStorage *pStorage, const char *pFilename) +{ + m_File = pStorage->OpenFile(pFilename, IOFLAG_READ); + if(!m_File) + { + dbg_msg("demorec/playback", "could not open '%s'", pFilename); + return -1; + } + + // clear the playback info + mem_zero(&m_Info, sizeof(m_Info)); + m_Info.m_Info.m_FirstTick = -1; + m_Info.m_Info.m_LastTick = -1; + //m_Info.start_tick = -1; + m_Info.m_NextTick = -1; + m_Info.m_Info.m_CurrentTick = -1; + m_Info.m_PreviousTick = -1; + m_Info.m_Info.m_Speed = 1; + + m_LastSnapshotDataSize = -1; + + // read the header + io_read(m_File, &m_Info.m_Header, sizeof(m_Info.m_Header)); + if(mem_comp(m_Info.m_Header.m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) != 0) + { + dbg_msg("demorec/playback", "'%s' is not a demo file", pFilename); + io_close(m_File); + m_File = 0; + return -1; + } + + // scan the file for interessting points + ScanFile(); + + // ready for playback + return 0; +} + +int CDemoPlayer::NextFrame() +{ + DoTick(); + return IsPlaying(); +} + +int CDemoPlayer::Play() +{ + // fill in previous and next tick + while(m_Info.m_PreviousTick == -1 && IsPlaying()) + DoTick(); + + // set start info + /*m_Info.start_tick = m_Info.previous_tick; + m_Info.start_time = time_get();*/ + m_Info.m_CurrentTime = m_Info.m_PreviousTick*time_freq()/SERVER_TICK_SPEED; + m_Info.m_LastUpdate = time_get(); + return 0; +} + +int CDemoPlayer::SetPos(float Percent) +{ + int Keyframe; + int WantedTick; + if(!m_File) + return -1; + + // -5 because we have to have a current tick and previous tick when we do the playback + WantedTick = m_Info.m_Info.m_FirstTick + (int)((m_Info.m_Info.m_LastTick-m_Info.m_Info.m_FirstTick)*Percent) - 5; + + Keyframe = (int)(m_Info.m_SeekablePoints*Percent); + + if(Keyframe < 0 || Keyframe >= m_Info.m_SeekablePoints) + return -1; + + // get correct key frame + if(m_pKeyFrames[Keyframe].m_Tick < WantedTick) + while(Keyframe < m_Info.m_SeekablePoints-1 && m_pKeyFrames[Keyframe].m_Tick < WantedTick) + Keyframe++; + + while(Keyframe && m_pKeyFrames[Keyframe].m_Tick > WantedTick) + Keyframe--; + + // seek to the correct keyframe + io_seek(m_File, m_pKeyFrames[Keyframe].m_Filepos, IOSEEK_START); + + //m_Info.start_tick = -1; + m_Info.m_NextTick = -1; + m_Info.m_Info.m_CurrentTick = -1; + m_Info.m_PreviousTick = -1; + + // playback everything until we hit our tick + while(m_Info.m_PreviousTick < WantedTick) + DoTick(); + + Play(); + + return 0; +} + +void CDemoPlayer::SetSpeed(float Speed) +{ + m_Info.m_Info.m_Speed = Speed; +} + +int CDemoPlayer::Update() +{ + int64 Now = time_get(); + int64 Deltatime = Now-m_Info.m_LastUpdate; + m_Info.m_LastUpdate = Now; + + if(!IsPlaying()) + return 0; + + if(m_Info.m_Info.m_Paused) + { + + } + else + { + int64 Freq = time_freq(); + m_Info.m_CurrentTime += (int64)(Deltatime*(double)m_Info.m_Info.m_Speed); + + while(1) + { + int64 CurtickStart = (m_Info.m_Info.m_CurrentTick)*Freq/SERVER_TICK_SPEED; + + // break if we are ready + if(CurtickStart > m_Info.m_CurrentTime) + break; + + // do one more tick + DoTick(); + + if(m_Info.m_Info.m_Paused) + return 0; + } + + // update intratick + { + int64 CurtickStart = (m_Info.m_Info.m_CurrentTick)*Freq/SERVER_TICK_SPEED; + int64 PrevtickStart = (m_Info.m_PreviousTick)*Freq/SERVER_TICK_SPEED; + m_Info.m_IntraTick = (m_Info.m_CurrentTime - PrevtickStart) / (float)(CurtickStart-PrevtickStart); + m_Info.m_TickTime = (m_Info.m_CurrentTime - PrevtickStart) / (float)Freq; + } + + if(m_Info.m_Info.m_CurrentTick == m_Info.m_PreviousTick || + m_Info.m_Info.m_CurrentTick == m_Info.m_NextTick) + { + dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d", + m_Info.m_PreviousTick, m_Info.m_Info.m_CurrentTick, m_Info.m_NextTick); + } + } + + return 0; +} + +int CDemoPlayer::Stop() +{ + if(!m_File) + return -1; + + dbg_msg("demorec/playback", "Stopped playback"); + io_close(m_File); + m_File = 0; + mem_free(m_pKeyFrames); + m_pKeyFrames = 0; + return 0; +} + + diff --git a/src/engine/shared/demorec.h b/src/engine/shared/demorec.h new file mode 100644 index 00000000..0936c30c --- /dev/null +++ b/src/engine/shared/demorec.h @@ -0,0 +1,117 @@ +#ifndef ENGINE_SHARED_DEMOREC_H +#define ENGINE_SHARED_DEMOREC_H + +#include <engine/demo.h> +#include "snapshot.h" + +struct CDemoHeader +{ + char m_aMarker[8]; + char m_aNetversion[64]; + char m_aMap[64]; + unsigned char m_aCrc[4]; + char m_aType[8]; +}; + +class CDemoRecorder +{ + IOHANDLE m_File; + int m_LastTickMarker; + int m_LastKeyFrame; + unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE]; + class CSnapshotDelta *m_pSnapshotDelta; + + void WriteTickMarker(int Tick, int Keyframe); + void Write(int Type, const void *pData, int Size); +public: + CDemoRecorder(class CSnapshotDelta *pSnapshotDelta); + + int Start(class IStorage *pStorage, const char *pFilename, const char *pNetversion, const char *pMap, int MapCrc, const char *pType); + int Stop(); + + void RecordSnapshot(int Tick, const void *pData, int Size); + void RecordMessage(const void *pData, int Size); + + bool IsRecording() const { return m_File != 0; } +}; + +class CDemoPlayer : public IDemoPlayer +{ +public: + class IListner + { + public: + virtual void OnDemoPlayerSnapshot(void *pData, int Size) = 0; + virtual void OnDemoPlayerMessage(void *pData, int Size) = 0; + }; + + struct CPlaybackInfo + { + CDemoHeader m_Header; + + IDemoPlayer::CInfo m_Info; + + int64 m_LastUpdate; + int64 m_CurrentTime; + + int m_SeekablePoints; + + int m_NextTick; + int m_PreviousTick; + + float m_IntraTick; + float m_TickTime; + }; + +private: + IListner *m_pListner; + + + // Playback + struct CKeyFrame + { + long m_Filepos; + int m_Tick; + }; + + struct CKeyFrameSearch + { + CKeyFrame m_Frame; + CKeyFrameSearch *m_pNext; + }; + + IOHANDLE m_File; + CKeyFrame *m_pKeyFrames; + + CPlaybackInfo m_Info; + unsigned char m_aLastSnapshotData[CSnapshot::MAX_SIZE]; + int m_LastSnapshotDataSize; + class CSnapshotDelta *m_pSnapshotDelta; + + int ReadChunkHeader(int *pType, int *pSize, int *pTick); + void DoTick(); + void ScanFile(); + int NextFrame(); + +public: + + CDemoPlayer(class CSnapshotDelta *m_pSnapshotDelta); + + void SetListner(IListner *pListner); + + int Load(class IStorage *pStorage, const char *pFilename); + int Play(); + void Pause(); + void Unpause(); + int Stop(); + void SetSpeed(float Speed); + int SetPos(float Precent); + const CInfo *BaseInfo() const { return &m_Info.m_Info; } + + int Update(); + + const CPlaybackInfo *Info() const { return &m_Info; } + int IsPlaying() const { return m_File != 0; } +}; + +#endif diff --git a/src/engine/shared/engine.cpp b/src/engine/shared/engine.cpp new file mode 100644 index 00000000..5cd50cf0 --- /dev/null +++ b/src/engine/shared/engine.cpp @@ -0,0 +1,76 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info + +#include <base/system.h> + +#include <engine/shared/config.h> +#include <engine/shared/engine.h> +#include <engine/shared/network.h> +#include <engine/console.h> +#include "linereader.h" + +// compiled-in data-dir path +#define DATA_DIR "data" + +//static int engine_find_datadir(char *argv0); +/* +static void con_dbg_dumpmem(IConsole::IResult *result, void *user_data) +{ + mem_debug_dump(); +} + +static void con_dbg_lognetwork(IConsole::IResult *result, void *user_data) +{ + CNetBase::OpenLog("network_sent.dat", "network_recv.dat"); +}*/ + +/* +static char application_save_path[512] = {0}; +static char datadir[512] = {0}; + +const char *engine_savepath(const char *filename, char *buffer, int max) +{ + str_format(buffer, max, "%s/%s", application_save_path, filename); + return buffer; +}*/ + +void CEngine::Init(const char *pAppname) +{ + dbg_logger_stdout(); + dbg_logger_debugger(); + + // + dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING); +#ifdef CONF_ARCH_ENDIAN_LITTLE + dbg_msg("engine", "arch is little endian"); +#elif defined(CONF_ARCH_ENDIAN_BIG) + dbg_msg("engine", "arch is big endian"); +#else + dbg_msg("engine", "unknown endian"); +#endif + + // init the network + net_init(); + CNetBase::Init(); + + m_HostLookupPool.Init(1); + + //MACRO_REGISTER_COMMAND("dbg_dumpmem", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_dumpmem, 0x0, "Dump the memory"); + //MACRO_REGISTER_COMMAND("dbg_lognetwork", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_lognetwork, 0x0, "Log the network"); + + // reset the config + //config_reset(); +} + + +static int HostLookupThread(void *pUser) +{ + CHostLookup *pLookup = (CHostLookup *)pUser; + net_host_lookup(pLookup->m_aHostname, &pLookup->m_Addr, NETTYPE_IPV4); + return 0; +} + +void CEngine::HostLookup(CHostLookup *pLookup, const char *pHostname) +{ + str_copy(pLookup->m_aHostname, pHostname, sizeof(pLookup->m_aHostname)); + m_HostLookupPool.Add(&pLookup->m_Job, HostLookupThread, pLookup); +} diff --git a/src/engine/shared/engine.h b/src/engine/shared/engine.h new file mode 100644 index 00000000..ad266ae4 --- /dev/null +++ b/src/engine/shared/engine.h @@ -0,0 +1,23 @@ +#ifndef ENGINE_SHARED_E_ENGINE_H +#define ENGINE_SHARED_E_ENGINE_H + +#include "jobs.h" + +class CHostLookup +{ +public: + CJob m_Job; + char m_aHostname[128]; + NETADDR m_Addr; +}; + +class CEngine +{ + class CJobPool m_HostLookupPool; + +public: + void Init(const char *pAppname); + void HostLookup(CHostLookup *pLookup, const char *pHostname); +}; + +#endif diff --git a/src/engine/shared/huffman.cpp b/src/engine/shared/huffman.cpp new file mode 100644 index 00000000..8b0c1cd0 --- /dev/null +++ b/src/engine/shared/huffman.cpp @@ -0,0 +1,276 @@ +#include <base/system.h> +#include "huffman.h" + +struct CHuffmanConstructNode +{ + unsigned short m_NodeId; + int m_Frequency; +}; + +void CHuffman::Setbits_r(CNode *pNode, int Bits, int Depth) +{ + if(pNode->m_aLeafs[1] != 0xffff) + Setbits_r(&m_aNodes[pNode->m_aLeafs[1]], Bits|(1<<Depth), Depth+1); + if(pNode->m_aLeafs[0] != 0xffff) + Setbits_r(&m_aNodes[pNode->m_aLeafs[0]], Bits, Depth+1); + + if(pNode->m_NumBits) + { + pNode->m_Bits = Bits; + pNode->m_NumBits = Depth; + } +} + +// TODO: this should be something faster, but it's enough for now +static void BubbleSort(CHuffmanConstructNode **ppList, int Size) +{ + int Changed = 1; + CHuffmanConstructNode *pTemp; + + while(Changed) + { + Changed = 0; + for(int i = 0; i < Size-1; i++) + { + if(ppList[i]->m_Frequency < ppList[i+1]->m_Frequency) + { + pTemp = ppList[i]; + ppList[i] = ppList[i+1]; + ppList[i+1] = pTemp; + Changed = 1; + } + } + } +} + +void CHuffman::ConstructTree(const unsigned *pFrequencies) +{ + CHuffmanConstructNode aNodesLeftStorage[HUFFMAN_MAX_SYMBOLS]; + CHuffmanConstructNode *apNodesLeft[HUFFMAN_MAX_SYMBOLS]; + int NumNodesLeft = HUFFMAN_MAX_SYMBOLS; + + // add the symbols + for(int i = 0; i < HUFFMAN_MAX_SYMBOLS; i++) + { + m_aNodes[i].m_NumBits = -1; + m_aNodes[i].m_Symbol = i; + m_aNodes[i].m_aLeafs[0] = -1; + m_aNodes[i].m_aLeafs[1] = -1; + + if(i == HUFFMAN_EOF_SYMBOL) + aNodesLeftStorage[i].m_Frequency = 1; + else + aNodesLeftStorage[i].m_Frequency = pFrequencies[i]; + aNodesLeftStorage[i].m_NodeId = i; + apNodesLeft[i] = &aNodesLeftStorage[i]; + + } + + m_NumNodes = HUFFMAN_MAX_SYMBOLS; + + // construct the table + while(NumNodesLeft > 1) + { + // we can't rely on stdlib's qsort for this, it can generate different results on different implementations + BubbleSort(apNodesLeft, NumNodesLeft); + + m_aNodes[m_NumNodes].m_NumBits = 0; + m_aNodes[m_NumNodes].m_aLeafs[0] = apNodesLeft[NumNodesLeft-1]->m_NodeId; + m_aNodes[m_NumNodes].m_aLeafs[1] = apNodesLeft[NumNodesLeft-2]->m_NodeId; + apNodesLeft[NumNodesLeft-2]->m_NodeId = m_NumNodes; + apNodesLeft[NumNodesLeft-2]->m_Frequency = apNodesLeft[NumNodesLeft-1]->m_Frequency + apNodesLeft[NumNodesLeft-2]->m_Frequency; + + m_NumNodes++; + NumNodesLeft--; + } + + // set start node + m_pStartNode = &m_aNodes[m_NumNodes-1]; + + // build symbol bits + Setbits_r(m_pStartNode, 0, 0); +} + +void CHuffman::Init(const unsigned *pFrequencies) +{ + int i; + + // make sure to cleanout every thing + mem_zero(this, sizeof(*this)); + + // construct the tree + ConstructTree(pFrequencies); + + // build decode LUT + for(i = 0; i < HUFFMAN_LUTSIZE; i++) + { + unsigned Bits = i; + int k; + CNode *pNode = m_pStartNode; + for(k = 0; k < HUFFMAN_LUTBITS; k++) + { + pNode = &m_aNodes[pNode->m_aLeafs[Bits&1]]; + Bits >>= 1; + + if(!pNode) + break; + + if(pNode->m_NumBits) + { + m_apDecodeLut[i] = pNode; + break; + } + } + + if(k == HUFFMAN_LUTBITS) + m_apDecodeLut[i] = pNode; + } + +} + +//*************************************************************** +int CHuffman::Compress(const void *pInput, int InputSize, void *pOutput, int OutputSize) +{ + // this macro loads a symbol for a byte into bits and bitcount +#define HUFFMAN_MACRO_LOADSYMBOL(Sym) \ + Bits |= m_aNodes[Sym].m_Bits << Bitcount; \ + Bitcount += m_aNodes[Sym].m_NumBits; + + // this macro writes the symbol stored in bits and bitcount to the dst pointer +#define HUFFMAN_MACRO_WRITE() \ + while(Bitcount >= 8) \ + { \ + *pDst++ = (unsigned char)(Bits&0xff); \ + if(pDst == pDstEnd) \ + return -1; \ + Bits >>= 8; \ + Bitcount -= 8; \ + } + + // setup buffer pointers + const unsigned char *pSrc = (const unsigned char *)pInput; + const unsigned char *pSrcEnd = pSrc + InputSize; + unsigned char *pDst = (unsigned char *)pOutput; + unsigned char *pDstEnd = pDst + OutputSize; + + // symbol variables + unsigned Bits = 0; + unsigned Bitcount = 0; + + // make sure that we have data that we want to compress + if(InputSize) + { + // {A} load the first symbol + int Symbol = *pSrc++; + + while(pSrc != pSrcEnd) + { + // {B} load the symbol + HUFFMAN_MACRO_LOADSYMBOL(Symbol) + + // {C} fetch next symbol, this is done here because it will reduce dependency in the code + Symbol = *pSrc++; + + // {B} write the symbol loaded at + HUFFMAN_MACRO_WRITE() + } + + // write the last symbol loaded from {C} or {A} in the case of only 1 byte input buffer + HUFFMAN_MACRO_LOADSYMBOL(Symbol) + HUFFMAN_MACRO_WRITE() + } + + // write EOF symbol + HUFFMAN_MACRO_LOADSYMBOL(HUFFMAN_EOF_SYMBOL) + HUFFMAN_MACRO_WRITE() + + // write out the last bits + *pDst++ = Bits; + + // return the size of the output + return (int)(pDst - (const unsigned char *)pOutput); + + // remove macros +#undef HUFFMAN_MACRO_LOADSYMBOL +#undef HUFFMAN_MACRO_WRITE +} + +//*************************************************************** +int CHuffman::Decompress(const void *pInput, int InputSize, void *pOutput, int OutputSize) +{ + // setup buffer pointers + unsigned char *pDst = (unsigned char *)pOutput; + unsigned char *pSrc = (unsigned char *)pInput; + unsigned char *pDstEnd = pDst + OutputSize; + unsigned char *pSrcEnd = pSrc + InputSize; + + unsigned Bits = 0; + unsigned Bitcount = 0; + + CNode *pEof = &m_aNodes[HUFFMAN_EOF_SYMBOL]; + CNode *pNode = 0; + + while(1) + { + // {A} try to load a node now, this will reduce dependency at location {D} + pNode = 0; + if(Bitcount >= HUFFMAN_LUTBITS) + pNode = m_apDecodeLut[Bits&HUFFMAN_LUTMASK]; + + // {B} fill with new bits + while(Bitcount < 24 && pSrc != pSrcEnd) + { + Bits |= (*pSrc++) << Bitcount; + Bitcount += 8; + } + + // {C} load symbol now if we didn't that earlier at location {A} + if(!pNode) + pNode = m_apDecodeLut[Bits&HUFFMAN_LUTMASK]; + + // {D} check if we hit a symbol already + if(pNode->m_NumBits) + { + // remove the bits for that symbol + Bits >>= pNode->m_NumBits; + Bitcount -= pNode->m_NumBits; + } + else + { + // remove the bits that the lut checked up for us + Bits >>= HUFFMAN_LUTBITS; + Bitcount -= HUFFMAN_LUTBITS; + + // walk the tree bit by bit + while(1) + { + // traverse tree + pNode = &m_aNodes[pNode->m_aLeafs[Bits&1]]; + + // remove bit + Bitcount--; + Bits >>= 1; + + // check if we hit a symbol + if(pNode->m_NumBits) + break; + + // no more bits, decoding error + if(Bitcount == 0) + return -1; + } + } + + // check for eof + if(pNode == pEof) + break; + + // output character + if(pDst == pDstEnd) + return -1; + *pDst++ = pNode->m_Symbol; + } + + // return the size of the decompressed buffer + return (int)(pDst - (const unsigned char *)pOutput); +} diff --git a/src/engine/shared/huffman.h b/src/engine/shared/huffman.h new file mode 100644 index 00000000..5aa56c8f --- /dev/null +++ b/src/engine/shared/huffman.h @@ -0,0 +1,89 @@ +#ifndef ENGINE_SHARED_HUFFMAN_H +#define ENGINE_SHARED_HUFFMAN_H + + + +class CHuffman +{ + enum + { + HUFFMAN_EOF_SYMBOL = 256, + + HUFFMAN_MAX_SYMBOLS=HUFFMAN_EOF_SYMBOL+1, + HUFFMAN_MAX_NODES=HUFFMAN_MAX_SYMBOLS*2-1, + + HUFFMAN_LUTBITS = 10, + HUFFMAN_LUTSIZE = (1<<HUFFMAN_LUTBITS), + HUFFMAN_LUTMASK = (HUFFMAN_LUTSIZE-1) + }; + + struct CNode + { + // symbol + unsigned m_Bits; + unsigned m_NumBits; + + // don't use pointers for this. shorts are smaller so we can fit more data into the cache + unsigned short m_aLeafs[2]; + + // what the symbol represents + unsigned char m_Symbol; + }; + + CNode m_aNodes[HUFFMAN_MAX_NODES]; + CNode *m_apDecodeLut[HUFFMAN_LUTSIZE]; + CNode *m_pStartNode; + int m_NumNodes; + + void Setbits_r(CNode *pNode, int Bits, int Depth); + void ConstructTree(const unsigned *pFrequencies); + +public: + /* + Function: huffman_init + Inits the compressor/decompressor. + + Parameters: + huff - Pointer to the state to init + frequencies - A pointer to an array of 256 entries of the frequencies of the bytes + + Remarks: + - Does no allocation what so ever. + - You don't have to call any cleanup functions when you are done with it + */ + void Init(const unsigned *pFrequencies); + + /* + Function: huffman_compress + Compresses a buffer and outputs a compressed buffer. + + Parameters: + huff - Pointer to the huffman state + input - Buffer to compress + input_size - Size of the buffer to compress + output - Buffer to put the compressed data into + output_size - Size of the output buffer + + Returns: + Returns the size of the compressed data. Negative value on failure. + */ + int Compress(const void *pInput, int InputSize, void *pOutput, int OutputSize); + + /* + Function: huffman_decompress + Decompresses a buffer + + Parameters: + huff - Pointer to the huffman state + input - Buffer to decompress + input_size - Size of the buffer to decompress + output - Buffer to put the uncompressed data into + output_size - Size of the output buffer + + Returns: + Returns the size of the uncompressed data. Negative value on failure. + */ + int Decompress(const void *pInput, int InputSize, void *pOutput, int OutputSize); + +}; +#endif // __HUFFMAN_HEADER__ diff --git a/src/engine/shared/jobs.cpp b/src/engine/shared/jobs.cpp new file mode 100644 index 00000000..83d7290b --- /dev/null +++ b/src/engine/shared/jobs.cpp @@ -0,0 +1,74 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> +#include "jobs.h" + +CJobPool::CJobPool() +{ + // empty the pool + m_Lock = lock_create(); + m_pFirstJob = 0; + m_pLastJob = 0; +} + +void CJobPool::WorkerThread(void *pUser) +{ + CJobPool *pPool = (CJobPool *)pUser; + + while(1) + { + CJob *pJob = 0; + + // fetch job from queue + lock_wait(pPool->m_Lock); + if(pPool->m_pFirstJob) + { + pJob = pPool->m_pFirstJob; + pPool->m_pFirstJob = pPool->m_pFirstJob->m_pNext; + if(pPool->m_pFirstJob) + pPool->m_pFirstJob->m_pPrev = 0; + else + pPool->m_pLastJob = 0; + } + lock_release(pPool->m_Lock); + + // do the job if we have one + if(pJob) + { + pJob->m_Status = CJob::STATE_RUNNING; + pJob->m_Result = pJob->m_pfnFunc(pJob->m_pFuncData); + pJob->m_Status = CJob::STATE_DONE; + } + else + thread_sleep(10); + } + +} + +int CJobPool::Init(int NumThreads) +{ + // start threads + for(int i = 0; i < NumThreads; i++) + thread_create(WorkerThread, this); + return 0; +} + +int CJobPool::Add(CJob *pJob, JOBFUNC pfnFunc, void *pData) +{ + mem_zero(pJob, sizeof(CJob)); + pJob->m_pfnFunc = pfnFunc; + pJob->m_pFuncData = pData; + + lock_wait(m_Lock); + + // add job to queue + pJob->m_pPrev = m_pLastJob; + if(m_pLastJob) + m_pLastJob->m_pNext = pJob; + m_pLastJob = pJob; + if(!m_pFirstJob) + m_pFirstJob = pJob; + + lock_release(m_Lock); + return 0; +} + diff --git a/src/engine/shared/jobs.h b/src/engine/shared/jobs.h new file mode 100644 index 00000000..d04815b0 --- /dev/null +++ b/src/engine/shared/jobs.h @@ -0,0 +1,51 @@ +#ifndef ENGINE_SHARED_JOBS_H +#define ENGINE_SHARED_JOBS_H +typedef int (*JOBFUNC)(void *pData); + +class CJobPool; + +class CJob +{ + friend class CJobPool; + + CJobPool *m_pPool; + CJob *m_pPrev; + CJob *m_pNext; + + volatile int m_Status; + volatile int m_Result; + + JOBFUNC m_pfnFunc; + void *m_pFuncData; +public: + CJob() + { + m_Status = STATE_DONE; + m_pFuncData = 0; + } + + enum + { + STATE_PENDING=0, + STATE_RUNNING, + STATE_DONE + }; + + int Status() const { return m_Status; } +}; + +class CJobPool +{ + LOCK m_Lock; + CJob *m_pFirstJob; + CJob *m_pLastJob; + + static void WorkerThread(void *pUser); + +public: + CJobPool(); + + int Init(int NumThreads); + int Add(CJob *pJob, JOBFUNC pfnFunc, void *pData); +}; +#endif diff --git a/src/engine/shared/kernel.cpp b/src/engine/shared/kernel.cpp new file mode 100644 index 00000000..9f6850ba --- /dev/null +++ b/src/engine/shared/kernel.cpp @@ -0,0 +1,93 @@ +#include <base/system.h> +#include <engine/kernel.h> + +class CKernel : public IKernel +{ + enum + { + MAX_INTERFACES=32, + }; + + class CInterfaceInfo + { + public: + CInterfaceInfo() + { + m_aName[0] = 0; + m_pInterface = 0x0; + } + + char m_aName[64]; + IInterface *m_pInterface; + }; + + CInterfaceInfo m_aInterfaces[MAX_INTERFACES]; + int m_NumInterfaces; + + CInterfaceInfo *FindInterfaceInfo(const char *pName) + { + for(int i = 0; i < m_NumInterfaces; i++) + { + if(str_comp(pName, m_aInterfaces[i].m_aName) == 0) + return &m_aInterfaces[i]; + } + return 0x0; + } + +public: + + CKernel() + { + m_NumInterfaces = 0; + } + + + virtual bool RegisterInterfaceImpl(const char *pName, IInterface *pInterface) + { + // TODO: More error checks here + if(m_NumInterfaces == MAX_INTERFACES) + { + dbg_msg("kernel", "ERROR: couldn't register interface '%s'. maximum of interfaces reached", pName); + return false; + } + + if(FindInterfaceInfo(pName) != 0) + { + dbg_msg("kernel", "ERROR: couldn't register interface '%s'. interface already exists"); + return false; + } + + pInterface->m_pKernel = this; + m_aInterfaces[m_NumInterfaces].m_pInterface = pInterface; + str_copy(m_aInterfaces[m_NumInterfaces].m_aName, pName, sizeof(m_aInterfaces[m_NumInterfaces].m_aName)); + m_NumInterfaces++; + + return true; + } + + virtual bool ReregisterInterfaceImpl(const char *pName, IInterface *pInterface) + { + if(FindInterfaceInfo(pName) == 0) + { + dbg_msg("kernel", "ERROR: couldn't reregister interface '%s'. interface doesn't exist"); + return false; + } + + pInterface->m_pKernel = this; + + return true; + } + + virtual IInterface *RequestInterfaceImpl(const char *pName) + { + CInterfaceInfo *pInfo = FindInterfaceInfo(pName); + if(!pInfo) + { + dbg_msg("kernel", "failed to find interface with the name '%s'", pName); + return 0; + } + return pInfo->m_pInterface; + } +}; + +IKernel *IKernel::Create() { return new CKernel; } diff --git a/src/engine/shared/linereader.cpp b/src/engine/shared/linereader.cpp new file mode 100644 index 00000000..b3de233b --- /dev/null +++ b/src/engine/shared/linereader.cpp @@ -0,0 +1,62 @@ +#include "linereader.h" + +void CLineReader::Init(IOHANDLE io) +{ + m_BufferMaxSize = 4*1024; + m_BufferSize = 0; + m_BufferPos = 0; + m_IO = io; +} + +char *CLineReader::Get() +{ + unsigned LineStart = m_BufferPos; + + while(1) + { + if(m_BufferPos >= m_BufferSize) + { + // fetch more + + // move the remaining part to the front + unsigned Read; + unsigned Left = m_BufferSize - LineStart; + + if(LineStart > m_BufferSize) + Left = 0; + if(Left) + mem_move(m_aBuffer, &m_aBuffer[LineStart], Left); + m_BufferPos = Left; + + // fill the buffer + Read = io_read(m_IO, &m_aBuffer[m_BufferPos], m_BufferMaxSize-m_BufferPos); + m_BufferSize = Left + Read; + LineStart = 0; + + if(!Read) + { + if(Left) + { + m_aBuffer[Left] = 0; // return the last line + m_BufferPos = Left; + m_BufferSize = Left; + return m_aBuffer; + } + else + return 0x0; // we are done! + } + } + else + { + if(m_aBuffer[m_BufferPos] == '\n' || m_aBuffer[m_BufferPos] == '\r') + { + // line found + m_aBuffer[m_BufferPos] = 0; + m_BufferPos++; + return &m_aBuffer[LineStart]; + } + else + m_BufferPos++; + } + } +} diff --git a/src/engine/shared/linereader.h b/src/engine/shared/linereader.h new file mode 100644 index 00000000..f28d42f6 --- /dev/null +++ b/src/engine/shared/linereader.h @@ -0,0 +1,17 @@ +#ifndef ENGINE_SHARED_LINEREADER_H +#define ENGINE_SHARED_LINEREADER_H +#include <base/system.h> + +// buffered stream for reading lines, should perhaps be something smaller +class CLineReader +{ + char m_aBuffer[4*1024]; + unsigned m_BufferPos; + unsigned m_BufferSize; + unsigned m_BufferMaxSize; + IOHANDLE m_IO; +public: + void Init(IOHANDLE IoHandle); + char *Get(); +}; +#endif diff --git a/src/engine/shared/map.cpp b/src/engine/shared/map.cpp new file mode 100644 index 00000000..505d18e9 --- /dev/null +++ b/src/engine/shared/map.cpp @@ -0,0 +1,45 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> +#include <engine/map.h> +#include <engine/storage.h> +#include "datafile.h" + +class CMap : public IEngineMap +{ + CDataFileReader m_DataFile; +public: + CMap() {} + + virtual void *GetData(int Index) { return m_DataFile.GetData(Index); } + virtual void *GetDataSwapped(int Index) { return m_DataFile.GetDataSwapped(Index); } + virtual void UnloadData(int Index) { m_DataFile.UnloadData(Index); } + virtual void *GetItem(int Index, int *pType, int *pId) { return m_DataFile.GetItem(Index, pType, pId); } + virtual void GetType(int Type, int *pStart, int *pNum) { m_DataFile.GetType(Type, pStart, pNum); } + virtual void *FindItem(int Type, int Id) { return m_DataFile.FindItem(Type, Id); } + virtual int NumItems() { return m_DataFile.NumItems(); } + + virtual void Unload() + { + m_DataFile.Close(); + } + + virtual bool Load(const char *pMapName) + { + IStorage *pStorage = Kernel()->RequestInterface<IStorage>(); + if(!pStorage) + return false; + return m_DataFile.Open(pStorage, pMapName); + } + + virtual bool IsLoaded() + { + return m_DataFile.IsOpen(); + } + + virtual unsigned Crc() + { + return m_DataFile.Crc(); + } +}; + +extern IEngineMap *CreateEngineMap() { return new CMap; } diff --git a/src/engine/shared/masterserver.cpp b/src/engine/shared/masterserver.cpp new file mode 100644 index 00000000..beade5bf --- /dev/null +++ b/src/engine/shared/masterserver.cpp @@ -0,0 +1,187 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <stdio.h> + +#include <base/system.h> +#include <engine/masterserver.h> +#include <engine/storage.h> +#include "engine.h" +#include "linereader.h" + +class CMasterServer : public IEngineMasterServer +{ +public: + // master server functions + struct CMasterInfo + { + char m_aHostname[128]; + NETADDR m_Addr; + + CHostLookup m_Lookup; + } ; + + CMasterInfo m_aMasterServers[MAX_MASTERSERVERS]; + int m_NeedsUpdate; + CEngine *m_pEngine; + + CMasterServer() + { + SetDefault(); + m_NeedsUpdate = -1; + m_pEngine = 0; + } + + virtual int RefreshAddresses() + { + int i; + + if(m_NeedsUpdate != -1) + return 0; + + dbg_msg("engine/mastersrv", "refreshing master server addresses"); + + // add lookup jobs + for(i = 0; i < MAX_MASTERSERVERS; i++) + m_pEngine->HostLookup(&m_aMasterServers[i].m_Lookup, m_aMasterServers[i].m_aHostname); + + m_NeedsUpdate = 1; + return 0; + } + + virtual void Update() + { + // check if we need to update + if(m_NeedsUpdate != 1) + return; + m_NeedsUpdate = 0; + + for(int i = 0; i < MAX_MASTERSERVERS; i++) + { + if(m_aMasterServers[i].m_Lookup.m_Job.Status() != CJob::STATE_DONE) + m_NeedsUpdate = 1; + else + { + m_aMasterServers[i].m_Addr = m_aMasterServers[i].m_Lookup.m_Addr; + m_aMasterServers[i].m_Addr.port = 8300; + } + } + + if(!m_NeedsUpdate) + { + dbg_msg("engine/mastersrv", "saving addresses"); + Save(); + } + } + + virtual int IsRefreshing() + { + return m_NeedsUpdate; + } + + virtual NETADDR GetAddr(int Index) + { + return m_aMasterServers[Index].m_Addr; + } + + virtual const char *GetName(int Index) + { + return m_aMasterServers[Index].m_aHostname; + } + + virtual void DumpServers() + { + for(int i = 0; i < MAX_MASTERSERVERS; i++) + { + dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i, + m_aMasterServers[i].m_Addr.ip[0], m_aMasterServers[i].m_Addr.ip[1], + m_aMasterServers[i].m_Addr.ip[2], m_aMasterServers[i].m_Addr.ip[3]); + } + } + + virtual void Init(class CEngine *pEngine) + { + m_pEngine = pEngine; + } + + virtual void SetDefault() + { + mem_zero(m_aMasterServers, sizeof(m_aMasterServers)); + for(int i = 0; i < MAX_MASTERSERVERS; i++) + str_format(m_aMasterServers[i].m_aHostname, sizeof(m_aMasterServers[i].m_aHostname), "master%d.teeworlds.com", i+1); + } + + virtual int Load() + { + CLineReader LineReader; + IOHANDLE File; + int Count = 0; + IStorage *pStorage = Kernel()->RequestInterface<IStorage>(); + if(!pStorage) + return -1; + + // try to open file + File = pStorage->OpenFile("masters.cfg", IOFLAG_READ); + if(!File) + return -1; + + LineReader.Init(File); + while(1) + { + CMasterInfo Info = {{0}}; + int aIp[4]; + const char *pLine = LineReader.Get(); + if(!pLine) + break; + + // parse line + if(sscanf(pLine, "%s %d.%d.%d.%d", Info.m_aHostname, &aIp[0], &aIp[1], &aIp[2], &aIp[3]) == 5) + { + Info.m_Addr.ip[0] = (unsigned char)aIp[0]; + Info.m_Addr.ip[1] = (unsigned char)aIp[1]; + Info.m_Addr.ip[2] = (unsigned char)aIp[2]; + Info.m_Addr.ip[3] = (unsigned char)aIp[3]; + Info.m_Addr.port = 8300; + if(Count != MAX_MASTERSERVERS) + { + m_aMasterServers[Count] = Info; + Count++; + } + //else + // dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", pLine, MAX_MASTERSERVERS); + } + //else + // dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", pLine); + } + + io_close(File); + return 0; + } + + virtual int Save() + { + IOHANDLE File; + + IStorage *pStorage = Kernel()->RequestInterface<IStorage>(); + if(!pStorage) + return -1; + + // try to open file + File = pStorage->OpenFile("masters.cfg", IOFLAG_WRITE); + if(!File) + return -1; + + for(int i = 0; i < MAX_MASTERSERVERS; i++) + { + char aBuf[1024]; + str_format(aBuf, sizeof(aBuf), "%s %d.%d.%d.%d\n", m_aMasterServers[i].m_aHostname, + m_aMasterServers[i].m_Addr.ip[0], m_aMasterServers[i].m_Addr.ip[1], + m_aMasterServers[i].m_Addr.ip[2], m_aMasterServers[i].m_Addr.ip[3]); + + io_write(File, aBuf, str_length(aBuf)); + } + + io_close(File); + return 0; + } +}; + +IEngineMasterServer *CreateEngineMasterServer() { return new CMasterServer; } diff --git a/src/engine/shared/memheap.cpp b/src/engine/shared/memheap.cpp new file mode 100644 index 00000000..6661962b --- /dev/null +++ b/src/engine/shared/memheap.cpp @@ -0,0 +1,96 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> +#include "memheap.h" + +static const int CHUNK_SIZE = 1024*64; + +// allocates a new chunk to be used +void CHeap::NewChunk() +{ + CChunk *pChunk; + char *pMem; + + // allocate memory + pMem = (char*)mem_alloc(sizeof(CChunk)+CHUNK_SIZE, 1); + if(!pMem) + return; + + // the chunk structure is located in the begining of the chunk + // init it and return the chunk + pChunk = (CChunk*)pMem; + pChunk->m_pMemory = (char*)(pChunk+1); + pChunk->m_pCurrent = pChunk->m_pMemory; + pChunk->m_pEnd = pChunk->m_pMemory + CHUNK_SIZE; + pChunk->m_pNext = (CChunk *)0x0; + + pChunk->m_pNext = m_pCurrent; + m_pCurrent = pChunk; +} + +//**************** +void *CHeap::AllocateFromChunk(unsigned int Size) +{ + char *pMem; + + // check if we need can fit the allocation + if(m_pCurrent->m_pCurrent + Size > m_pCurrent->m_pEnd) + return (void*)0x0; + + // get memory and move the pointer forward + pMem = m_pCurrent->m_pCurrent; + m_pCurrent->m_pCurrent += Size; + return pMem; +} + +// creates a heap +CHeap::CHeap() +{ + m_pCurrent = 0x0; + Reset(); +} + +CHeap::~CHeap() +{ + Clear(); +} + +void CHeap::Reset() +{ + Clear(); + NewChunk(); +} + +// destroys the heap +void CHeap::Clear() +{ + CChunk *pChunk = m_pCurrent; + CChunk *pNext; + + while(pChunk) + { + pNext = pChunk->m_pNext; + mem_free(pChunk); + pChunk = pNext; + } + + m_pCurrent = 0x0; +} + +// +void *CHeap::Allocate(unsigned Size) +{ + char *pMem; + + // try to allocate from current chunk + pMem = (char *)AllocateFromChunk(Size); + if(!pMem) + { + // allocate new chunk and add it to the heap + NewChunk(); + + // try to allocate again + pMem = (char *)AllocateFromChunk(Size); + } + + return pMem; +} diff --git a/src/engine/shared/memheap.h b/src/engine/shared/memheap.h new file mode 100644 index 00000000..706395f2 --- /dev/null +++ b/src/engine/shared/memheap.h @@ -0,0 +1,32 @@ +#ifndef ENGINE_SHARED_MEMHEAP_H +#define ENGINE_SHARED_MEMHEAP_H +class CHeap +{ + struct CChunk + { + char *m_pMemory; + char *m_pCurrent; + char *m_pEnd; + CChunk *m_pNext; + }; + + enum + { + // how large each chunk should be + CHUNK_SIZE = 1025*64, + }; + + CChunk *m_pCurrent; + + + void Clear(); + void NewChunk(); + void *AllocateFromChunk(unsigned int Size); + +public: + CHeap(); + ~CHeap(); + void Reset(); + void *Allocate(unsigned Size); +}; +#endif diff --git a/src/engine/shared/message.h b/src/engine/shared/message.h new file mode 100644 index 00000000..4e67a8e1 --- /dev/null +++ b/src/engine/shared/message.h @@ -0,0 +1,9 @@ +#ifndef ENGINE_SHARED_MESSAGE_H +#define ENGINE_SHARED_MESSAGE_H +class CMessage +{ +public: + virtual bool Pack(void *pData, unsigned MaxDataSize); + virtual bool Unpack(const void *pData, unsigned DataSize); +}; +#endif diff --git a/src/engine/e_network.cpp b/src/engine/shared/network.cpp index ac753e50..0305ffff 100644 --- a/src/engine/e_network.cpp +++ b/src/engine/shared/network.cpp @@ -1,12 +1,11 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> -#include <string.h> /* strlen */ -#include "e_config.h" -#include "e_engine.h" -#include "e_network.h" -#include "e_huffman.h" +#include "config.h" +#include "engine.h" +#include "network.h" +#include "huffman.h" void CNetRecvUnpacker::Clear() { @@ -22,7 +21,7 @@ void CNetRecvUnpacker::Start(const NETADDR *pAddr, CNetConnection *pConnection, m_Valid = true; } -/* TODO: rename this function */ +// TODO: rename this function int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) { CNetChunkHeader Header; @@ -32,21 +31,21 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) { unsigned char *pData = m_Data.m_aChunkData; - /* check for old data to unpack */ + // check for old data to unpack if(!m_Valid || m_CurrentChunk >= m_Data.m_NumChunks) { Clear(); return 0; } - /* TODO: add checking here so we don't read too far */ + // TODO: add checking here so we don't read too far for(int i = 0; i < m_CurrentChunk; i++) { pData = Header.Unpack(pData); pData += Header.m_Size; } - /* unpack the header */ + // unpack the header pData = Header.Unpack(pData); m_CurrentChunk++; @@ -56,29 +55,29 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) return 0; } - /* handle sequence stuff */ + // handle sequence stuff if(m_pConnection && (Header.m_Flags&NET_CHUNKFLAG_VITAL)) { if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE) { - /* in sequence */ + // in sequence m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE; } else { - /* old packet that we already got */ + // old packet that we already got if(CNetBase::IsSeqInBackroom(Header.m_Sequence, m_pConnection->m_Ack)) continue; - /* out of sequence, request resend */ - if(config.debug) + // out of sequence, request resend + if(g_Config.m_Debug) dbg_msg("conn", "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE); m_pConnection->SignalResend(); - continue; /* take the next chunk in the packet */ + continue; // take the next chunk in the packet } } - /* fill in the info */ + // fill in the info pChunk->m_ClientID = m_ClientID; pChunk->m_Address = m_Addr; pChunk->m_Flags = 0; @@ -88,7 +87,7 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) } } -/* packs the data tight and sends it */ +// packs the data tight and sends it void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize) { unsigned char aBuffer[NET_MAX_PACKETSIZE]; @@ -108,20 +107,20 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct int CompressedSize = -1; int FinalSize = -1; - /* log the data */ + // log the data if(ms_DataLogSent) { - int type = 1; - io_write(ms_DataLogSent, &type, sizeof(type)); + int Type = 1; + io_write(ms_DataLogSent, &Type, sizeof(Type)); io_write(ms_DataLogSent, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize)); io_write(ms_DataLogSent, &pPacket->m_aChunkData, pPacket->m_DataSize); io_flush(ms_DataLogSent); } - /* compress */ - CompressedSize = huffman_compress(&ms_HuffmanState, pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4); + // compress + CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4); - /* check if the compression was enabled, successful and good enough */ + // check if the compression was enabled, successful and good enough if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize) { FinalSize = CompressedSize; @@ -129,13 +128,13 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct } else { - /* use uncompressed data */ + // use uncompressed data FinalSize = pPacket->m_DataSize; mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize); pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION; } - /* set header and send the packet if all things are good */ + // set header and send the packet if all things are good if(FinalSize >= 0) { FinalSize += NET_PACKETHEADERSIZE; @@ -144,11 +143,11 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct aBuffer[2] = pPacket->m_NumChunks; net_udp_send(Socket, pAddr, aBuffer, FinalSize); - /* log raw socket data */ + // log raw socket data if(ms_DataLogSent) { - int type = 0; - io_write(ms_DataLogSent, &type, sizeof(type)); + int Type = 0; + io_write(ms_DataLogSent, &Type, sizeof(Type)); io_write(ms_DataLogSent, &FinalSize, sizeof(FinalSize)); io_write(ms_DataLogSent, aBuffer, FinalSize); io_flush(ms_DataLogSent); @@ -156,27 +155,27 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct } } -/* TODO: rename this function */ +// TODO: rename this function int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket) { - /* check the size */ + // check the size if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE) { dbg_msg("", "packet too small, %d", Size); return -1; } - /* log the data */ + // log the data if(ms_DataLogRecv) { - int type = 0; - io_write(ms_DataLogRecv, &type, sizeof(type)); + int Type = 0; + io_write(ms_DataLogRecv, &Type, sizeof(Type)); io_write(ms_DataLogRecv, &Size, sizeof(Size)); io_write(ms_DataLogRecv, pBuffer, Size); io_flush(ms_DataLogRecv); } - /* read the packet */ + // read the packet pPacket->m_Flags = pBuffer[0]>>4; pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1]; pPacket->m_NumChunks = pBuffer[2]; @@ -199,30 +198,30 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct else { if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION) - pPacket->m_DataSize = huffman_decompress(&ms_HuffmanState, &pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData)); + pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData)); else mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize); } - /* check for errors */ + // check for errors if(pPacket->m_DataSize < 0) { - if(config.debug) + if(g_Config.m_Debug) dbg_msg("network", "error during packet decoding"); return -1; } - /* log the data */ + // log the data if(ms_DataLogRecv) { - int type = 1; - io_write(ms_DataLogRecv, &type, sizeof(type)); + int Type = 1; + io_write(ms_DataLogRecv, &Type, sizeof(Type)); io_write(ms_DataLogRecv, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize)); io_write(ms_DataLogRecv, pPacket->m_aChunkData, pPacket->m_DataSize); io_flush(ms_DataLogRecv); } - /* return success */ + // return success return 0; } @@ -237,7 +236,7 @@ void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int Con Construct.m_aChunkData[0] = ControlMsg; mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize); - /* send the control message */ + // send the control message CNetBase::SendPacket(Socket, pAddr, &Construct); } @@ -291,11 +290,12 @@ int CNetBase::IsSeqInBackroom(int Seq, int Ack) IOHANDLE CNetBase::ms_DataLogSent = 0; IOHANDLE CNetBase::ms_DataLogRecv = 0; -HUFFMAN_STATE CNetBase::ms_HuffmanState; +CHuffman CNetBase::ms_Huffman; void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog) { + /* if(pSentLog) { ms_DataLogSent = engine_openfile(pSentLog, IOFLAG_WRITE); @@ -312,17 +312,17 @@ void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog) dbg_msg("network", "logging recv packages to '%s'", pRecvLog); else dbg_msg("network", "failed to open for logging '%s'", pRecvLog); - } + }*/ } int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize) { - return huffman_compress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize); + return ms_Huffman.Compress(pData, DataSize, pOutput, OutputSize); } int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize) { - return huffman_decompress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize); + return ms_Huffman.Decompress(pData, DataSize, pOutput, OutputSize); } @@ -343,5 +343,5 @@ static const unsigned gs_aFreqTable[256+1] = { void CNetBase::Init() { - huffman_init(&ms_HuffmanState, gs_aFreqTable); + ms_Huffman.Init(gs_aFreqTable); } diff --git a/src/engine/e_network.h b/src/engine/shared/network.h index 19eca8da..11a1b70d 100644 --- a/src/engine/e_network.h +++ b/src/engine/shared/network.h @@ -1,9 +1,8 @@ -#ifndef ENGINE_NETWORK_H -#define ENGINE_NETWORK_H -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_SHARED_NETWORK_H +#define ENGINE_SHARED_NETWORK_H -#include "e_ringbuffer.h" -#include "e_huffman.h" +#include "ringbuffer.h" +#include "huffman.h" /* @@ -80,15 +79,15 @@ enum }; -typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); -typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); +typedef int (*NETFUNC_DELCLIENT)(int ClientID, void *pUser); +typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser); struct CNetChunk { - /* -1 means that it's a stateless packet */ - /* 0 on the client means the server */ + // -1 means that it's a stateless packet + // 0 on the client means the server int m_ClientID; - NETADDR m_Address; /* only used when client_id == -1 */ + NETADDR m_Address; // only used when client_id == -1 int m_Flags; int m_DataSize; const void *m_pData; @@ -162,7 +161,7 @@ private: void SetError(const char *pString); void AckChunks(int Ack); - void QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence); + int QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence); void SendControl(int ControlMsg, const void *pExtra, int ExtraSize); void ResendChunk(CNetChunkResend *pResend); void Resend(); @@ -176,7 +175,7 @@ public: int Flush(); int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr); - void QueueChunk(int Flags, int DataSize, const void *pData); + int QueueChunk(int Flags, int DataSize, const void *pData); const char *ErrorString(); void SignalResend(); @@ -210,7 +209,7 @@ public: int FetchChunk(CNetChunk *pChunk); }; -/* server side */ +// server side class CNetServer { public: @@ -232,11 +231,11 @@ private: public: CBanInfo m_Info; - /* hash list */ + // hash list CBan *m_pHashNext; CBan *m_pHashPrev; - /* used or free list */ + // used or free list CBan *m_pNext; CBan *m_pPrev; }; @@ -257,13 +256,13 @@ private: CNetRecvUnpacker m_RecvUnpacker; - void BanRemoveByObject(CBan *ban); + void BanRemoveByObject(CBan *pBan); public: int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); // - bool Open(NETADDR bindaddr, int MaxClients, int Flags); + bool Open(NETADDR BindAddr, int MaxClients, int Flags); int Close(); // @@ -277,8 +276,8 @@ public: // banning int BanAdd(NETADDR Addr, int Seconds); int BanRemove(NETADDR Addr); - int BanNum(); /* caution, slow */ - int BanGet(int Index, CBanInfo *pInfo); /* caution, slow */ + int BanNum(); // caution, slow + int BanGet(int Index, CBanInfo *pInfo); // caution, slow // status requests NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } @@ -288,7 +287,7 @@ public: -/* client side */ +// client side class CNetClient { NETADDR m_ServerAddr; @@ -327,7 +326,7 @@ class CNetBase { static IOHANDLE ms_DataLogSent; static IOHANDLE ms_DataLogRecv; - static HUFFMAN_STATE ms_HuffmanState; + static CHuffman ms_Huffman; public: static void OpenLog(const char *pSentlog, const char *pRecvlog); static void Init(); @@ -339,7 +338,7 @@ public: static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket); static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket); - /* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */ + // The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not static int IsSeqInBackroom(int Seq, int Ack); }; diff --git a/src/engine/e_network_client.cpp b/src/engine/shared/network_client.cpp index ce243b32..f7859c0a 100644 --- a/src/engine/e_network_client.cpp +++ b/src/engine/shared/network_client.cpp @@ -1,5 +1,5 @@ #include <base/system.h> -#include "e_network.h" +#include "network.h" bool CNetClient::Open(NETADDR BindAddr, int Flags) { @@ -14,7 +14,7 @@ bool CNetClient::Open(NETADDR BindAddr, int Flags) int CNetClient::Close() { - /* TODO: implement me */ + // TODO: implement me return 0; } @@ -50,15 +50,15 @@ int CNetClient::Recv(CNetChunk *pChunk) { while(1) { - /* check for a chunk */ + // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; - /* TODO: empty the recvinfo */ + // TODO: empty the recvinfo NETADDR Addr; int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); - /* no more packets for now */ + // no more packets for now if(Bytes <= 0) break; @@ -93,7 +93,7 @@ int CNetClient::Send(CNetChunk *pChunk) if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) { - /* send connectionless packet */ + // send connectionless packet CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); } else diff --git a/src/engine/e_network_conn.cpp b/src/engine/shared/network_conn.cpp index a54c9ce3..4ed157eb 100644 --- a/src/engine/e_network_conn.cpp +++ b/src/engine/shared/network_conn.cpp @@ -1,7 +1,6 @@ #include <base/system.h> -#include <string.h> -#include "e_config.h" -#include "e_network.h" +#include "config.h" +#include "network.h" void CNetConnection::ResetStats() { @@ -71,27 +70,27 @@ int CNetConnection::Flush() if(!NumChunks && !m_Construct.m_Flags) return 0; - /* send of the packets */ + // send of the packets m_Construct.m_Ack = m_Ack; CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct); - /* update send times */ + // update send times m_LastSendTime = time_get(); - /* clear construct so we can start building a new package */ + // clear construct so we can start building a new package mem_zero(&m_Construct, sizeof(m_Construct)); return NumChunks; } -void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence) +int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence) { unsigned char *pChunkData; - /* check if we have space for it, if not, flush the connection */ + // check if we have space for it, if not, flush the connection if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData)) Flush(); - /* pack all the data */ + // pack all the data CNetChunkHeader Header; Header.m_Flags = Flags; Header.m_Size = DataSize; @@ -101,15 +100,15 @@ void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, in mem_copy(pChunkData, pData, DataSize); pChunkData += DataSize; - /* */ + // m_Construct.m_NumChunks++; m_Construct.m_DataSize = (int)(pChunkData-m_Construct.m_aChunkData); - /* set packet flags aswell */ + // set packet flags aswell if(Flags&NET_CHUNKFLAG_VITAL && !(Flags&NET_CHUNKFLAG_RESEND)) { - /* save packet if we need to resend */ + // save packet if we need to resend CNetChunkResend *pResend = m_Buffer.Allocate(sizeof(CNetChunkResend)+DataSize); if(pResend) { @@ -123,22 +122,25 @@ void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, in } else { - /* out of buffer */ + // out of buffer Disconnect("too weak connection (out of buffer)"); + return -1; } } + + return 0; } -void CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData) +int CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData) { if(Flags&NET_CHUNKFLAG_VITAL) m_Sequence = (m_Sequence+1)%NET_MAX_SEQUENCE; - QueueChunkEx(Flags, DataSize, pData, m_Sequence); + return QueueChunkEx(Flags, DataSize, pData, m_Sequence); } void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSize) { - /* send the control message */ + // send the control message m_LastSendTime = time_get(); CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize); } @@ -151,7 +153,7 @@ void CNetConnection::ResendChunk(CNetChunkResend *pResend) void CNetConnection::Resend() { - for(CNetChunkResend *pResend = m_Buffer.First(); pResend; m_Buffer.Next(pResend)) + for(CNetChunkResend *pResend = m_Buffer.First(); pResend; pResend = m_Buffer.Next(pResend)) ResendChunk(pResend); } @@ -160,7 +162,7 @@ int CNetConnection::Connect(NETADDR *pAddr) if(State() != NET_CONNSTATE_OFFLINE) return -1; - /* init connection */ + // init connection Reset(); m_PeerAddr = *pAddr; mem_zero(m_ErrorString, sizeof(m_ErrorString)); @@ -177,7 +179,7 @@ void CNetConnection::Disconnect(const char *pReason) if(m_RemoteClosed == 0) { if(pReason) - SendControl(NET_CTRLMSG_CLOSE, pReason, strlen(pReason)+1); + SendControl(NET_CTRLMSG_CLOSE, pReason, str_length(pReason)+1); else SendControl(NET_CTRLMSG_CLOSE, 0, 0); @@ -191,41 +193,44 @@ void CNetConnection::Disconnect(const char *pReason) int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) { - int64 now = time_get(); - m_LastRecvTime = now; + int64 Now = time_get(); + m_LastRecvTime = Now; - /* check if resend is requested */ + // check if resend is requested if(pPacket->m_Flags&NET_PACKETFLAG_RESEND) Resend(); - /* */ + // if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL) { int CtrlMsg = pPacket->m_aChunkData[0]; if(CtrlMsg == NET_CTRLMSG_CLOSE) { - m_State = NET_CONNSTATE_ERROR; - m_RemoteClosed = 1; - - if(pPacket->m_DataSize) + if(net_addr_comp(&m_PeerAddr, pAddr) == 0) { - /* make sure to sanitize the error string form the other party*/ - char Str[128]; - if(pPacket->m_DataSize < 128) - str_copy(Str, (char *)pPacket->m_aChunkData, pPacket->m_DataSize); - else - str_copy(Str, (char *)pPacket->m_aChunkData, sizeof(Str)); - str_sanitize_strong(Str); + m_State = NET_CONNSTATE_ERROR; + m_RemoteClosed = 1; - /* set the error string */ - SetError(Str); + if(pPacket->m_DataSize) + { + // make sure to sanitize the error string form the other party + char Str[128]; + if(pPacket->m_DataSize < 128) + str_copy(Str, (char *)pPacket->m_aChunkData, pPacket->m_DataSize); + else + str_copy(Str, (char *)pPacket->m_aChunkData, sizeof(Str)); + str_sanitize_strong(Str); + + // set the error string + SetError(Str); + } + else + SetError("no reason given"); + + if(g_Config.m_Debug) + dbg_msg("conn", "closed reason='%s'", ErrorString()); } - else - SetError("no reason given"); - - if(config.debug) - dbg_msg("conn", "closed reason='%s'", ErrorString()); return 0; } else @@ -234,32 +239,32 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) { if(CtrlMsg == NET_CTRLMSG_CONNECT) { - /* send response and init connection */ + // send response and init connection Reset(); m_State = NET_CONNSTATE_PENDING; m_PeerAddr = *pAddr; - m_LastSendTime = now; - m_LastRecvTime = now; - m_LastUpdateTime = now; + m_LastSendTime = Now; + m_LastRecvTime = Now; + m_LastUpdateTime = Now; SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); - if(config.debug) + if(g_Config.m_Debug) dbg_msg("connection", "got connection, sending connect+accept"); } } else if(State() == NET_CONNSTATE_CONNECT) { - /* connection made */ + // connection made if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT) { SendControl(NET_CTRLMSG_ACCEPT, 0, 0); m_State = NET_CONNSTATE_ONLINE; - if(config.debug) + if(g_Config.m_Debug) dbg_msg("connection", "got connect+accept, sending accept. connection online"); } } else if(State() == NET_CONNSTATE_ONLINE) { - /* connection made */ + // connection made /* if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) { @@ -273,7 +278,7 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) if(State() == NET_CONNSTATE_PENDING) { m_State = NET_CONNSTATE_ONLINE; - if(config.debug) + if(g_Config.m_Debug) dbg_msg("connection", "connecting online"); } } @@ -288,46 +293,46 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) int CNetConnection::Update() { - int64 now = time_get(); + int64 Now = time_get(); if(State() == NET_CONNSTATE_OFFLINE || State() == NET_CONNSTATE_ERROR) return 0; - /* check for timeout */ + // check for timeout if(State() != NET_CONNSTATE_OFFLINE && State() != NET_CONNSTATE_CONNECT && - (now-m_LastRecvTime) > time_freq()*10) + (Now-m_LastRecvTime) > time_freq()*10) { m_State = NET_CONNSTATE_ERROR; SetError("timeout"); } - /* fix resends */ + // fix resends if(m_Buffer.First()) { CNetChunkResend *pResend = m_Buffer.First(); - /* check if we have some really old stuff laying around and abort if not acked */ - if(now-pResend->m_FirstSendTime > time_freq()*10) + // check if we have some really old stuff laying around and abort if not acked + if(Now-pResend->m_FirstSendTime > time_freq()*10) { m_State = NET_CONNSTATE_ERROR; SetError("too weak connection (not acked for 10 seconds)"); } else { - /* resend packet if we havn't got it acked in 1 second */ - if(now-pResend->m_LastSendTime > time_freq()) + // resend packet if we havn't got it acked in 1 second + if(Now-pResend->m_LastSendTime > time_freq()) ResendChunk(pResend); } } - /* send keep alives if nothing has happend for 250ms */ + // send keep alives if nothing has happend for 250ms if(State() == NET_CONNSTATE_ONLINE) { - if(time_get()-m_LastSendTime > time_freq()/2) /* flush connection after 500ms if needed */ + if(time_get()-m_LastSendTime > time_freq()/2) // flush connection after 500ms if needed { int NumFlushedChunks = Flush(); - if(NumFlushedChunks && config.debug) + if(NumFlushedChunks && g_Config.m_Debug) dbg_msg("connection", "flushed connection due to timeout. %d chunks.", NumFlushedChunks); } @@ -336,12 +341,12 @@ int CNetConnection::Update() } else if(State() == NET_CONNSTATE_CONNECT) { - if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect every 500ms */ + if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect every 500ms SendControl(NET_CTRLMSG_CONNECT, 0, 0); } else if(State() == NET_CONNSTATE_PENDING) { - if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect/accept every 500ms */ + if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect/accept every 500ms SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); } diff --git a/src/engine/e_network_server.cpp b/src/engine/shared/network_server.cpp index 995290ef..32b08bf6 100644 --- a/src/engine/e_network_server.cpp +++ b/src/engine/shared/network_server.cpp @@ -1,28 +1,28 @@ #include <base/system.h> -#include "e_network.h" +#include "network.h" -#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \ - { if(first) first->prev = object; \ - object->prev = (struct CBan *)0; \ - object->next = first; \ - first = object; } +#define MACRO_LIST_LINK_FIRST(Object, First, Prev, Next) \ + { if(First) First->Prev = Object; \ + Object->Prev = (struct CBan *)0; \ + Object->Next = First; \ + First = Object; } -#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \ - { object->prev = after; \ - object->next = after->next; \ - after->next = object; \ - if(object->next) \ - object->next->prev = object; \ +#define MACRO_LIST_LINK_AFTER(Object, After, Prev, Next) \ + { Object->Prev = After; \ + Object->Next = After->Next; \ + After->Next = Object; \ + if(Object->Next) \ + Object->Next->Prev = Object; \ } -#define MACRO_LIST_UNLINK(object, first, prev, next) \ - { if(object->next) object->next->prev = object->prev; \ - if(object->prev) object->prev->next = object->next; \ - else first = object->next; \ - object->next = 0; object->prev = 0; } +#define MACRO_LIST_UNLINK(Object, First, Prev, Next) \ + { if(Object->Next) Object->Next->Prev = Object->Prev; \ + if(Object->Prev) Object->Prev->Next = Object->Next; \ + else First = Object->Next; \ + Object->Next = 0; Object->Prev = 0; } -#define MACRO_LIST_FIND(start, next, expression) \ - { while(start && !(expression)) start = start->next; } +#define MACRO_LIST_FIND(Start, Next, Expression) \ + { while(Start && !(Expression)) Start = Start->Next; } bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int Flags) { @@ -44,7 +44,7 @@ bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int Flags) for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aSlots[i].m_Connection.Init(m_Socket); - /* setup all pointers for bans */ + // setup all pointers for bans for(int i = 1; i < NET_SERVER_MAXBANS-1; i++) { m_BanPool[i].m_pNext = &m_BanPool[i+1]; @@ -68,13 +68,13 @@ int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT p int CNetServer::Close() { - /* TODO: implement me */ + // TODO: implement me return 0; } int CNetServer::Drop(int ClientID, const char *pReason) { - /* TODO: insert lots of checks here */ + // TODO: insert lots of checks here NETADDR Addr = ClientAddr(ClientID); dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"", @@ -114,18 +114,18 @@ int CNetServer::BanNum() void CNetServer::BanRemoveByObject(CBan *pBan) { - int iphash = (pBan->m_Info.m_Addr.ip[0]+pBan->m_Info.m_Addr.ip[1]+pBan->m_Info.m_Addr.ip[2]+pBan->m_Info.m_Addr.ip[3])&0xff; + int IpHash = (pBan->m_Info.m_Addr.ip[0]+pBan->m_Info.m_Addr.ip[1]+pBan->m_Info.m_Addr.ip[2]+pBan->m_Info.m_Addr.ip[3])&0xff; dbg_msg("netserver", "removing ban on %d.%d.%d.%d", pBan->m_Info.m_Addr.ip[0], pBan->m_Info.m_Addr.ip[1], pBan->m_Info.m_Addr.ip[2], pBan->m_Info.m_Addr.ip[3]); MACRO_LIST_UNLINK(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); - MACRO_LIST_UNLINK(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext); + MACRO_LIST_UNLINK(pBan, m_aBans[IpHash], m_pHashPrev, m_pHashNext); MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); } int CNetServer::BanRemove(NETADDR Addr) { - int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; - CBan *pBan = m_aBans[iphash]; + int IpHash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; + CBan *pBan = m_aBans[IpHash]; MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); @@ -140,22 +140,22 @@ int CNetServer::BanRemove(NETADDR Addr) int CNetServer::BanAdd(NETADDR Addr, int Seconds) { - int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; + int IpHash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; int Stamp = -1; CBan *pBan; - /* remove the port */ + // remove the port Addr.port = 0; if(Seconds) Stamp = time_timestamp() + Seconds; - /* search to see if it already exists */ - pBan = m_aBans[iphash]; + // search to see if it already exists + pBan = m_aBans[IpHash]; MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); if(pBan) { - /* adjust the ban */ + // adjust the ban pBan->m_Info.m_Expires = Stamp; return 0; } @@ -163,18 +163,18 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds) if(!m_BanPool_FirstFree) return -1; - /* fetch and clear the new ban */ + // fetch and clear the new ban pBan = m_BanPool_FirstFree; MACRO_LIST_UNLINK(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); - /* setup the ban info */ + // setup the ban info pBan->m_Info.m_Expires = Stamp; pBan->m_Info.m_Addr = Addr; - /* add it to the ban hash */ - MACRO_LIST_LINK_FIRST(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext); + // add it to the ban hash + MACRO_LIST_LINK_FIRST(pBan, m_aBans[IpHash], m_pHashPrev, m_pHashNext); - /* insert it into the used list */ + // insert it into the used list { if(m_BanPool_FirstUsed) { @@ -185,7 +185,7 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds) pInsertAfter = pInsertAfter->m_pPrev; else { - /* add to last */ + // add to last pInsertAfter = m_BanPool_FirstUsed; while(pInsertAfter->m_pNext) pInsertAfter = pInsertAfter->m_pNext; @@ -206,7 +206,7 @@ int CNetServer::BanAdd(NETADDR Addr, int Seconds) } } - /* drop banned clients */ + // drop banned clients { char Buf[128]; NETADDR BanAddr; @@ -238,7 +238,7 @@ int CNetServer::Update() Drop(i, m_aSlots[i].m_Connection.ErrorString()); } - /* remove expired bans */ + // remove expired bans while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now) { CBan *pBan = m_BanPool_FirstUsed; @@ -253,20 +253,20 @@ int CNetServer::Update() */ int CNetServer::Recv(CNetChunk *pChunk) { - unsigned now = time_timestamp(); + unsigned Now = time_timestamp(); while(1) { NETADDR Addr; - /* check for a chunk */ + // check for a chunk if(m_RecvUnpacker.FetchChunk(pChunk)) return 1; - /* TODO: empty the recvinfo */ + // TODO: empty the recvinfo int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); - /* no more packets for now */ + // no more packets for now if(Bytes <= 0) break; @@ -274,25 +274,25 @@ int CNetServer::Recv(CNetChunk *pChunk) { CBan *pBan = 0; NETADDR BanAddr = Addr; - int iphash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3])&0xff; + int IpHash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3])&0xff; int Found = 0; BanAddr.port = 0; - /* search a ban */ - for(pBan = m_aBans[iphash]; pBan; pBan = pBan->m_pHashNext) + // search a ban + for(pBan = m_aBans[IpHash]; pBan; pBan = pBan->m_pHashNext) { if(net_addr_comp(&pBan->m_Info.m_Addr, &BanAddr) == 0) break; } - /* check if we just should drop the packet */ + // check if we just should drop the packet if(pBan) { // banned, reply with a message char BanStr[128]; if(pBan->m_Info.m_Expires) { - int Mins = ((pBan->m_Info.m_Expires - now)+59)/60; + int Mins = ((pBan->m_Info.m_Expires - Now)+59)/60; if(Mins == 1) str_format(BanStr, sizeof(BanStr), "banned for %d minute", Mins); else @@ -315,24 +315,24 @@ int CNetServer::Recv(CNetChunk *pChunk) } else { - /* TODO: check size here */ + // TODO: check size here if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) { Found = 0; - /* check if we already got this client */ + // check if we already got this client for(int i = 0; i < MaxClients(); i++) { NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && net_addr_comp(&PeerAddr, &Addr) == 0) { - Found = 1; /* silent ignore.. we got this client already */ + Found = 1; // silent ignore.. we got this client already break; } } - /* client that wants to connect */ + // client that wants to connect if(!Found) { for(int i = 0; i < MaxClients(); i++) @@ -356,7 +356,7 @@ int CNetServer::Recv(CNetChunk *pChunk) } else { - /* normal packet, find matching slot */ + // normal packet, find matching slot for(int i = 0; i < MaxClients(); i++) { NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); @@ -386,7 +386,7 @@ int CNetServer::Send(CNetChunk *pChunk) if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) { - /* send connectionless packet */ + // send connectionless packet CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); } else @@ -398,10 +398,15 @@ int CNetServer::Send(CNetChunk *pChunk) if(pChunk->m_Flags&NETSENDFLAG_VITAL) Flags = NET_CHUNKFLAG_VITAL; - m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData); - - if(pChunk->m_Flags&NETSENDFLAG_FLUSH) - m_aSlots[pChunk->m_ClientID].m_Connection.Flush(); + if(m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData) == 0) + { + if(pChunk->m_Flags&NETSENDFLAG_FLUSH) + m_aSlots[pChunk->m_ClientID].m_Connection.Flush(); + } + else + { + Drop(pChunk->m_ClientID, "error sending data"); + } } return 0; } diff --git a/src/engine/e_packer.cpp b/src/engine/shared/packer.cpp index 0d8aeab3..3e1d8dd6 100644 --- a/src/engine/e_packer.cpp +++ b/src/engine/shared/packer.cpp @@ -1,11 +1,10 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdlib.h> /* rand() */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> -#include "e_packer.h" -#include "e_compression.h" -#include "e_engine.h" -#include "e_config.h" +#include "packer.h" +#include "compression.h" +#include "engine.h" +#include "config.h" void CPacker::Reset() { @@ -19,14 +18,14 @@ void CPacker::AddInt(int i) if(m_Error) return; - /* make sure that we have space enough */ + // make sure that we have space enough if(m_pEnd - m_pCurrent < 6) { dbg_break(); m_Error = 1; } else - m_pCurrent = vint_pack(m_pCurrent, i); + m_pCurrent = CVariableInt::Pack(m_pCurrent, i); } void CPacker::AddString(const char *pStr, int Limit) @@ -34,7 +33,7 @@ void CPacker::AddString(const char *pStr, int Limit) if(m_Error) return; - /* */ + // if(Limit > 0) { while(*pStr && Limit != 0) @@ -66,7 +65,7 @@ void CPacker::AddString(const char *pStr, int Limit) } } -void CPacker::AddRaw(const unsigned char *pData, int Size) +void CPacker::AddRaw(const void *pData, int Size) { if(m_Error) return; @@ -77,18 +76,19 @@ void CPacker::AddRaw(const unsigned char *pData, int Size) return; } + const unsigned char *pSrc = (const unsigned char *)pData; while(Size) { - *m_pCurrent++ = *pData++; + *m_pCurrent++ = *pSrc++; Size--; } } -void CUnpacker::Reset(const unsigned char *pData, int Size) +void CUnpacker::Reset(const void *pData, int Size) { m_Error = 0; - m_pStart = pData; + m_pStart = (const unsigned char *)pData; m_pEnd = m_pStart + Size; m_pCurrent = m_pStart; } @@ -105,7 +105,7 @@ int CUnpacker::GetInt() } int i; - m_pCurrent = vint_unpack(m_pCurrent, &i); + m_pCurrent = CVariableInt::Unpack(m_pCurrent, &i); if(m_pCurrent > m_pEnd) { m_Error = 1; @@ -120,7 +120,7 @@ const char *CUnpacker::GetString() return ""; char *pPtr = (char *)m_pCurrent; - while(*m_pCurrent) /* skip the string */ + while(*m_pCurrent) // skip the string { m_pCurrent++; if(m_pCurrent == m_pEnd) @@ -131,7 +131,7 @@ const char *CUnpacker::GetString() } m_pCurrent++; - /* sanitize all strings */ + // sanitize all strings str_sanitize(pPtr); return pPtr; } @@ -142,14 +142,14 @@ const unsigned char *CUnpacker::GetRaw(int Size) if(m_Error) return 0; - /* check for nasty sizes */ + // check for nasty sizes if(Size < 0 || m_pCurrent+Size > m_pEnd) { m_Error = 1; return 0; } - /* "unpack" the data */ + // "unpack" the data m_pCurrent += Size; return pPtr; } diff --git a/src/engine/e_packer.h b/src/engine/shared/packer.h index 5dc80e7a..7a98501a 100644 --- a/src/engine/e_packer.h +++ b/src/engine/shared/packer.h @@ -1,4 +1,7 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_SHARED_PACKER_H +#define ENGINE_SHARED_PACKER_H + + class CPacker { @@ -15,7 +18,7 @@ public: void Reset(); void AddInt(int i); void AddString(const char *pStr, int Limit); - void AddRaw(const unsigned char *pData, int Size); + void AddRaw(const void *pData, int Size); int Size() const { return (int)(m_pCurrent-m_aBuffer); } const unsigned char *Data() const { return m_aBuffer; } @@ -29,9 +32,11 @@ class CUnpacker const unsigned char *m_pEnd; int m_Error; public: - void Reset(const unsigned char *pData, int Size); + void Reset(const void *pData, int Size); int GetInt(); const char *GetString(); const unsigned char *GetRaw(int Size); bool Error() const { return m_Error; } }; + +#endif diff --git a/src/engine/shared/protocol.h b/src/engine/shared/protocol.h new file mode 100644 index 00000000..d09cff5a --- /dev/null +++ b/src/engine/shared/protocol.h @@ -0,0 +1,91 @@ +#ifndef ENGINE_SHARED_PROTOCOL_H +#define ENGINE_SHARED_PROTOCOL_H + +#include <base/system.h> + +/* + Connection diagram - How the initilization works. + + Client -> INFO -> Server + Contains version info, name, and some other info. + + Client <- MAP <- Server + Contains current map. + + Client -> READY -> Server + The client has loaded the map and is ready to go, + but the mod needs to send it's information aswell. + modc_connected is called on the client and + mods_connected is called on the server. + The client should call client_entergame when the + mod has done it's initilization. + + Client -> ENTERGAME -> Server + Tells the server to start sending snapshots. + client_entergame and server_client_enter is called. +*/ + + +enum +{ + NETMSG_NULL=0, + + // the first thing sent by the client + // contains the version info for the client + NETMSG_INFO=1, + + // sent by server + NETMSG_MAP_CHANGE, // sent when client should switch map + NETMSG_MAP_DATA, // map transfer, contains a chunk of the map file + NETMSG_SNAP, // normal snapshot, multiple parts + NETMSG_SNAPEMPTY, // empty snapshot + NETMSG_SNAPSINGLE, // ? + NETMSG_SNAPSMALL, // + NETMSG_INPUTTIMING, // reports how off the input was + NETMSG_RCON_AUTH_STATUS,// result of the authentication + NETMSG_RCON_LINE, // line that should be printed to the remote console + + NETMSG_AUTH_CHALLANGE, // + NETMSG_AUTH_RESULT, // + + // sent by client + NETMSG_READY, // + NETMSG_ENTERGAME, + NETMSG_INPUT, // contains the inputdata from the client + NETMSG_RCON_CMD, // + NETMSG_RCON_AUTH, // + NETMSG_REQUEST_MAP_DATA,// + + NETMSG_AUTH_START, // + NETMSG_AUTH_RESPONSE, // + + // sent by both + NETMSG_PING, + NETMSG_PING_REPLY, + NETMSG_ERROR +}; + +// this should be revised +enum +{ + SERVER_TICK_SPEED=50, + SERVER_FLAG_PASSWORD = 0x1, + + MAX_CLIENTS=16, + + MAX_INPUT_SIZE=128, + MAX_SNAPSHOT_PACKSIZE=900, + + MAX_CLANNAME_LENGTH=32, + MAX_NAME_LENGTH=24, + + + // message packing + MSGFLAG_VITAL=1, + MSGFLAG_FLUSH=2, + MSGFLAG_NORECORD=4, + MSGFLAG_RECORD=8, + MSGFLAG_NOSEND=16 +}; + +#endif diff --git a/src/engine/e_ringbuffer.cpp b/src/engine/shared/ringbuffer.cpp index eb8a8af4..45a845ee 100644 --- a/src/engine/e_ringbuffer.cpp +++ b/src/engine/shared/ringbuffer.cpp @@ -1,6 +1,6 @@ #include <base/system.h> -#include "e_ringbuffer.h" +#include "ringbuffer.h" CRingBufferBase::CItem *CRingBufferBase::NextBlock(CItem *pItem) { @@ -18,15 +18,15 @@ CRingBufferBase::CItem *CRingBufferBase::PrevBlock(CItem *pItem) CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem) { - /* make sure that this block and previous block is free */ + // make sure that this block and previous block is free if(!pItem->m_Free || !pItem->m_pPrev || !pItem->m_pPrev->m_Free) return pItem; - /* merge the blocks */ + // merge the blocks pItem->m_pPrev->m_Size += pItem->m_Size; pItem->m_pPrev->m_pNext = pItem->m_pNext; - /* fixup pointers */ + // fixup pointers if(pItem->m_pNext) pItem->m_pNext->m_pPrev = pItem->m_pPrev; else @@ -38,7 +38,7 @@ CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem) if(pItem == m_pConsume) m_pConsume = pItem->m_pPrev; - /* return the current block */ + // return the current block return pItem->m_pPrev; } @@ -61,20 +61,20 @@ void *CRingBufferBase::Allocate(int Size) int WantedSize = (Size+sizeof(CItem)+sizeof(CItem)-1)/sizeof(CItem)*sizeof(CItem); CItem *pBlock = 0; - /* check if we even can fit this block */ + // check if we even can fit this block if(WantedSize > m_Size) return 0; while(1) { - /* check for space */ + // check for space if(m_pProduce->m_Free) { if(m_pProduce->m_Size >= WantedSize) pBlock = m_pProduce; else { - /* wrap around to try to find a block */ + // wrap around to try to find a block if(m_pFirst->m_Free && m_pFirst->m_Size >= WantedSize) pBlock = m_pFirst; } @@ -84,7 +84,7 @@ void *CRingBufferBase::Allocate(int Size) break; else { - /* we have no block, check our policy and see what todo */ + // we have no block, check our policy and see what todo if(m_Flags&FLAG_RECYCLE) { if(!PopFirst()) @@ -95,10 +95,10 @@ void *CRingBufferBase::Allocate(int Size) } } - /* okey, we have our block */ + // okey, we have our block - /* split the block if needed */ - if(pBlock->m_Size > WantedSize) + // split the block if needed + if(pBlock->m_Size > WantedSize+sizeof(CItem)) { CItem *pNewItem = (CItem *)((char *)pBlock + WantedSize); pNewItem->m_pPrev = pBlock; @@ -116,10 +116,10 @@ void *CRingBufferBase::Allocate(int Size) } - /* set next block */ + // set next block m_pProduce = NextBlock(pBlock); - /* set as used and return the item pointer */ + // set as used and return the item pointer pBlock->m_Free = 0; return (void *)(pBlock+1); } @@ -129,13 +129,13 @@ int CRingBufferBase::PopFirst() if(m_pConsume->m_Free) return 0; - /* set the free flag */ + // set the free flag m_pConsume->m_Free = 1; - /* previous block is also free, merge them */ + // previous block is also free, merge them m_pConsume = MergeBack(m_pConsume); - /* advance the consume pointer */ + // advance the consume pointer m_pConsume = NextBlock(m_pConsume); while(m_pConsume->m_Free && m_pConsume != m_pProduce) { @@ -143,8 +143,8 @@ int CRingBufferBase::PopFirst() m_pConsume = NextBlock(m_pConsume); } - /* in the case that we have catched up with the produce pointer */ - /* we might stand on a free block so merge em */ + // in the case that we have catched up with the produce pointer + // we might stand on a free block so merge em MergeBack(m_pConsume); return 1; } @@ -187,8 +187,6 @@ void *CRingBufferBase::First() void *CRingBufferBase::Last() { - if(!m_pProduce->m_Free) - return m_pProduce+1; return Prev(m_pProduce+1); } diff --git a/src/engine/e_ringbuffer.h b/src/engine/shared/ringbuffer.h index b9f7219c..aa02b8d9 100644 --- a/src/engine/e_ringbuffer.h +++ b/src/engine/shared/ringbuffer.h @@ -1,5 +1,5 @@ -#ifndef _RINGBUFFER_H -#define _RINGBUFFER_H +#ifndef ENGINE_SHARED_RINGBUFFER_H +#define ENGINE_SHARED_RINGBUFFER_H typedef struct RINGBUFFER RINGBUFFER; @@ -40,7 +40,7 @@ protected: public: enum { - /* Will start to destroy items to try to fit the next one */ + // Will start to destroy items to try to fit the next one FLAG_RECYCLE=1 }; }; diff --git a/src/engine/shared/snapshot.cpp b/src/engine/shared/snapshot.cpp new file mode 100644 index 00000000..d566d3a3 --- /dev/null +++ b/src/engine/shared/snapshot.cpp @@ -0,0 +1,537 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include "snapshot.h" +#include "engine.h" +#include "compression.h" + +// CSnapshot + +CSnapshotItem *CSnapshot::GetItem(int Index) +{ + return (CSnapshotItem *)(DataStart() + Offsets()[Index]); +} + +int CSnapshot::GetItemSize(int Index) +{ + if(Index == m_NumItems-1) + return (m_DataSize - Offsets()[Index]) - sizeof(CSnapshotItem); + return (Offsets()[Index+1] - Offsets()[Index]) - sizeof(CSnapshotItem); +} + +int CSnapshot::GetItemIndex(int Key) +{ + // TODO: OPT: this should not be a linear search. very bad + for(int i = 0; i < m_NumItems; i++) + { + if(GetItem(i)->Key() == Key) + return i; + } + return -1; +} + +int CSnapshot::Crc() +{ + int Crc = 0; + + for(int i = 0; i < m_NumItems; i++) + { + CSnapshotItem *pItem = GetItem(i); + int Size = GetItemSize(i); + + for(int b = 0; b < Size/4; b++) + Crc += pItem->Data()[b]; + } + return Crc; +} + +void CSnapshot::DebugDump() +{ + dbg_msg("snapshot", "data_size=%d num_items=%d", m_DataSize, m_NumItems); + for(int i = 0; i < m_NumItems; i++) + { + CSnapshotItem *pItem = GetItem(i); + int Size = GetItemSize(i); + dbg_msg("snapshot", "\ttype=%d id=%d", pItem->Type(), pItem->ID()); + for(int b = 0; b < Size/4; b++) + dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, pItem->Data()[b], pItem->Data()[b]); + } +} + + +// CSnapshotDelta + +struct CItemList +{ + int m_Num; + int m_aKeys[64]; + int m_aIndex[64]; +}; + +enum +{ + HASHLIST_SIZE = 256, +}; + +static void GenerateHash(CItemList *pHashlist, CSnapshot *pSnapshot) +{ + for(int i = 0; i < HASHLIST_SIZE; i++) + pHashlist[i].m_Num = 0; + + for(int i = 0; i < pSnapshot->NumItems(); i++) + { + int Key = pSnapshot->GetItem(i)->Key(); + int HashID = ((Key>>8)&0xf0) | (Key&0xf); + if(pHashlist[HashID].m_Num != 64) + { + pHashlist[HashID].m_aIndex[pHashlist[HashID].m_Num] = i; + pHashlist[HashID].m_aKeys[pHashlist[HashID].m_Num] = Key; + pHashlist[HashID].m_Num++; + } + } +} + +static int GetItemIndexHashed(int Key, const CItemList *pHashlist) +{ + int HashID = ((Key>>8)&0xf0) | (Key&0xf); + for(int i = 0; i < pHashlist[HashID].m_Num; i++) + { + if(pHashlist[HashID].m_aKeys[i] == Key) + return pHashlist[HashID].m_aIndex[i]; + } + + return -1; +} + +static int DiffItem(int *pPast, int *pCurrent, int *pOut, int Size) +{ + int Needed = 0; + while(Size) + { + *pOut = *pCurrent-*pPast; + Needed |= *pOut; + pOut++; + pPast++; + pCurrent++; + Size--; + } + + return Needed; +} + +void CSnapshotDelta::UndiffItem(int *pPast, int *pDiff, int *pOut, int Size) +{ + while(Size) + { + *pOut = *pPast+*pDiff; + + if(*pDiff == 0) + m_aSnapshotDataRate[m_SnapshotCurrent] += 1; + else + { + unsigned char aBuf[16]; + unsigned char *pEnd = CVariableInt::Pack(aBuf, *pDiff); + m_aSnapshotDataRate[m_SnapshotCurrent] += (int)(pEnd - (unsigned char*)aBuf) * 8; + } + + pOut++; + pPast++; + pDiff++; + Size--; + } +} + +CSnapshotDelta::CSnapshotDelta() +{ + mem_zero(m_aItemSizes, sizeof(m_aItemSizes)); + mem_zero(m_aSnapshotDataRate, sizeof(m_aSnapshotDataRate)); + mem_zero(m_aSnapshotDataUpdates, sizeof(m_aSnapshotDataUpdates)); + m_SnapshotCurrent = 0; + mem_zero(&m_Empty, sizeof(m_Empty)); +} + +void CSnapshotDelta::SetStaticsize(int ItemType, int Size) +{ + m_aItemSizes[ItemType] = Size; +} + +CSnapshotDelta::CData *CSnapshotDelta::EmptyDelta() +{ + return &m_Empty; +} + +// TODO: OPT: this should be made much faster +int CSnapshotDelta::CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pDstData) +{ + CData *pDelta = (CData *)pDstData; + int *pData = (int *)pDelta->m_pData; + int i, ItemSize, PastIndex; + CSnapshotItem *pFromItem; + CSnapshotItem *pCurItem; + CSnapshotItem *pPastItem; + int Count = 0; + int SizeCount = 0; + + pDelta->m_NumDeletedItems = 0; + pDelta->m_NumUpdateItems = 0; + pDelta->m_NumTempItems = 0; + + CItemList Hashlist[HASHLIST_SIZE]; + GenerateHash(Hashlist, pTo); + + // pack deleted stuff + for(i = 0; i < pFrom->NumItems(); i++) + { + pFromItem = pFrom->GetItem(i); + if(GetItemIndexHashed(pFromItem->Key(), Hashlist) == -1) + { + // deleted + pDelta->m_NumDeletedItems++; + *pData = pFromItem->Key(); + pData++; + } + } + + GenerateHash(Hashlist, pFrom); + int aPastIndecies[1024]; + + // fetch previous indices + // we do this as a separate pass because it helps the cache + for(i = 0; i < pTo->NumItems(); i++) + { + pCurItem = pTo->GetItem(i); // O(1) .. O(n) + aPastIndecies[i] = GetItemIndexHashed(pCurItem->Key(), Hashlist); // O(n) .. O(n^n) + } + + for(i = 0; i < pTo->NumItems(); i++) + { + // do delta + ItemSize = pTo->GetItemSize(i); // O(1) .. O(n) + pCurItem = pTo->GetItem(i); // O(1) .. O(n) + PastIndex = aPastIndecies[i]; + + if(PastIndex != -1) + { + int *pItemDataDst = pData+3; + + pPastItem = pFrom->GetItem(PastIndex); + + if(m_aItemSizes[pCurItem->Type()]) + pItemDataDst = pData+2; + + if(DiffItem((int*)pPastItem->Data(), (int*)pCurItem->Data(), pItemDataDst, ItemSize/4)) + { + + *pData++ = pCurItem->Type(); + *pData++ = pCurItem->ID(); + if(!m_aItemSizes[pCurItem->Type()]) + *pData++ = ItemSize/4; + pData += ItemSize/4; + pDelta->m_NumUpdateItems++; + } + } + else + { + *pData++ = pCurItem->Type(); + *pData++ = pCurItem->ID(); + if(!m_aItemSizes[pCurItem->Type()]) + *pData++ = ItemSize/4; + + mem_copy(pData, pCurItem->Data(), ItemSize); + SizeCount += ItemSize; + pData += ItemSize/4; + pDelta->m_NumUpdateItems++; + Count++; + } + } + + if(0) + { + dbg_msg("snapshot", "%d %d %d", + pDelta->m_NumDeletedItems, + pDelta->m_NumUpdateItems, + pDelta->m_NumTempItems); + } + + /* + // TODO: pack temp stuff + + // finish + //mem_copy(pDelta->offsets, deleted, pDelta->num_deleted_items*sizeof(int)); + //mem_copy(&(pDelta->offsets[pDelta->num_deleted_items]), update, pDelta->num_update_items*sizeof(int)); + //mem_copy(&(pDelta->offsets[pDelta->num_deleted_items+pDelta->num_update_items]), temp, pDelta->num_temp_items*sizeof(int)); + //mem_copy(pDelta->data_start(), data, data_size); + //pDelta->data_size = data_size; + * */ + + if(!pDelta->m_NumDeletedItems && !pDelta->m_NumUpdateItems && !pDelta->m_NumTempItems) + return 0; + + return (int)((char*)pData-(char*)pDstData); +} + +static int RangeCheck(void *pEnd, void *pPtr, int Size) +{ + if((const char *)pPtr + Size > (const char *)pEnd) + return -1; + return 0; +} + +int CSnapshotDelta::UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pSrcData, int DataSize) +{ + CSnapshotBuilder Builder; + CData *pDelta = (CData *)pSrcData; + int *pData = (int *)pDelta->m_pData; + int *pEnd = (int *)(((char *)pSrcData + DataSize)); + + CSnapshotItem *pFromItem; + int Keep, ItemSize; + int *pDeleted; + int Id, Type, Key; + int FromIndex; + int *pNewData; + + Builder.Init(); + + // unpack deleted stuff + pDeleted = pData; + pData += pDelta->m_NumDeletedItems; + if(pData > pEnd) + return -1; + + // copy all non deleted stuff + for(int i = 0; i < pFrom->NumItems(); i++) + { + // dbg_assert(0, "fail!"); + pFromItem = pFrom->GetItem(i); + ItemSize = pFrom->GetItemSize(i); + Keep = 1; + for(int d = 0; d < pDelta->m_NumDeletedItems; d++) + { + if(pDeleted[d] == pFromItem->Key()) + { + Keep = 0; + break; + } + } + + if(Keep) + { + // keep it + mem_copy( + Builder.NewItem(pFromItem->Type(), pFromItem->ID(), ItemSize), + pFromItem->Data(), ItemSize); + } + } + + // unpack updated stuff + for(int i = 0; i < pDelta->m_NumUpdateItems; i++) + { + if(pData+2 > pEnd) + return -1; + + Type = *pData++; + Id = *pData++; + if(m_aItemSizes[Type]) + ItemSize = m_aItemSizes[Type]; + else + { + if(pData+1 > pEnd) + return -2; + ItemSize = (*pData++) * 4; + } + m_SnapshotCurrent = Type; + + if(RangeCheck(pEnd, pData, ItemSize) || ItemSize < 0) return -3; + + Key = (Type<<16)|Id; + + // create the item if needed + pNewData = Builder.GetItemData(Key); + if(!pNewData) + pNewData = (int *)Builder.NewItem(Key>>16, Key&0xffff, ItemSize); + + //if(range_check(pEnd, pNewData, ItemSize)) return -4; + + FromIndex = pFrom->GetItemIndex(Key); + if(FromIndex != -1) + { + // we got an update so we need pTo apply the diff + UndiffItem((int *)pFrom->GetItem(FromIndex)->Data(), pData, pNewData, ItemSize/4); + m_aSnapshotDataUpdates[m_SnapshotCurrent]++; + } + else // no previous, just copy the pData + { + mem_copy(pNewData, pData, ItemSize); + m_aSnapshotDataRate[m_SnapshotCurrent] += ItemSize*8; + m_aSnapshotDataUpdates[m_SnapshotCurrent]++; + } + + pData += ItemSize/4; + } + + // finish up + return Builder.Finish(pTo); +} + + +// CSnapshotStorage + +void CSnapshotStorage::Init() +{ + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::PurgeAll() +{ + CHolder *pHolder = m_pFirst; + CHolder *pNext; + + while(pHolder) + { + pNext = pHolder->m_pNext; + mem_free(pHolder); + pHolder = pNext; + } + + // no more snapshots in storage + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::PurgeUntil(int Tick) +{ + CHolder *pHolder = m_pFirst; + CHolder *pNext; + + while(pHolder) + { + pNext = pHolder->m_pNext; + if(pHolder->m_Tick >= Tick) + return; // no more to remove + mem_free(pHolder); + + // did we come to the end of the list? + if (!pNext) + break; + + m_pFirst = pNext; + pNext->m_pPrev = 0x0; + + pHolder = pNext; + } + + // no more snapshots in storage + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt) +{ + // allocate memory for holder + snapshot_data + int TotalSize = sizeof(CHolder)+DataSize; + + if(CreateAlt) + TotalSize += DataSize; + + CHolder *pHolder = (CHolder *)mem_alloc(TotalSize, 1); + + // set data + pHolder->m_Tick = Tick; + pHolder->m_Tagtime = Tagtime; + pHolder->m_SnapSize = DataSize; + pHolder->m_pSnap = (CSnapshot*)(pHolder+1); + mem_copy(pHolder->m_pSnap, pData, DataSize); + + if(CreateAlt) // create alternative if wanted + { + pHolder->m_pAltSnap = (CSnapshot*)(((char *)pHolder->m_pSnap) + DataSize); + mem_copy(pHolder->m_pAltSnap, pData, DataSize); + } + else + pHolder->m_pAltSnap = 0; + + + // link + pHolder->m_pNext = 0; + pHolder->m_pPrev = m_pLast; + if(m_pLast) + m_pLast->m_pNext = pHolder; + else + m_pFirst = pHolder; + m_pLast = pHolder; +} + +int CSnapshotStorage::Get(int Tick, int64 *pTagtime, CSnapshot **ppData, CSnapshot **ppAltData) +{ + CHolder *pHolder = m_pFirst; + + while(pHolder) + { + if(pHolder->m_Tick == Tick) + { + if(pTagtime) + *pTagtime = pHolder->m_Tagtime; + if(ppData) + *ppData = pHolder->m_pSnap; + if(ppAltData) + *ppData = pHolder->m_pAltSnap; + return pHolder->m_SnapSize; + } + + pHolder = pHolder->m_pNext; + } + + return -1; +} + +// CSnapshotBuilder + +void CSnapshotBuilder::Init() +{ + m_DataSize = 0; + m_NumItems = 0; +} + +CSnapshotItem *CSnapshotBuilder::GetItem(int Index) +{ + return (CSnapshotItem *)&(m_aData[m_aOffsets[Index]]); +} + +int *CSnapshotBuilder::GetItemData(int Key) +{ + int i; + for(i = 0; i < m_NumItems; i++) + { + if(GetItem(i)->Key() == Key) + return (int *)GetItem(i)->Data(); + } + return 0; +} + +int CSnapshotBuilder::Finish(void *SpnapData) +{ + // flattern and make the snapshot + CSnapshot *pSnap = (CSnapshot *)SpnapData; + int OffsetSize = sizeof(int)*m_NumItems; + pSnap->m_DataSize = m_DataSize; + pSnap->m_NumItems = m_NumItems; + mem_copy(pSnap->Offsets(), m_aOffsets, OffsetSize); + mem_copy(pSnap->DataStart(), m_aData, m_DataSize); + return sizeof(CSnapshot) + OffsetSize + m_DataSize; +} + +void *CSnapshotBuilder::NewItem(int Type, int ID, int Size) +{ + CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize); + + mem_zero(pObj, sizeof(CSnapshotItem) + Size); + pObj->m_TypeAndID = (Type<<16)|ID; + m_aOffsets[m_NumItems] = m_DataSize; + m_DataSize += sizeof(CSnapshotItem) + Size; + m_NumItems++; + + dbg_assert(m_DataSize < CSnapshot::MAX_SIZE, "too much data"); + dbg_assert(m_NumItems < MAX_ITEMS, "too many items"); + + return pObj->Data(); +} diff --git a/src/engine/e_snapshot.h b/src/engine/shared/snapshot.h index 60dc254c..ec27d004 100644 --- a/src/engine/e_snapshot.h +++ b/src/engine/shared/snapshot.h @@ -1,12 +1,9 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_SNAPSHOT_H -#define ENGINE_SNAPSHOT_H +#ifndef ENGINE_SHARED_SNAPSHOT_H +#define ENGINE_SHARED_SNAPSHOT_H #include <base/system.h> -/* CSnapshot */ - - +// CSnapshot class CSnapshotItem { @@ -19,48 +16,69 @@ public: int Key() { return m_TypeAndID; } }; -class CSnapshotDelta -{ -public: - int m_NumDeletedItems; - int m_NumUpdateItems; - int m_NumTempItems; /* needed? */ - int m_pData[1]; -}; -// TODO: hide a lot of these members class CSnapshot { + friend class CSnapshotBuilder; + int m_DataSize; + int m_NumItems; + + int *Offsets() const { return (int *)(this+1); } + char *DataStart() const { return (char*)(Offsets()+m_NumItems); } + public: enum { MAX_SIZE=64*1024 }; - int m_DataSize; - int m_NumItems; - - int *Offsets() const { return (int *)(this+1); } - char *DataStart() const { return (char*)(Offsets()+m_NumItems); } + void Clear() { m_DataSize = 0; m_NumItems = 0; } + int NumItems() const { return m_NumItems; } CSnapshotItem *GetItem(int Index); int GetItemSize(int Index); int GetItemIndex(int Key); int Crc(); void DebugDump(); +}; - // TODO: move these - int GetItemIndexHashed(int Key); - int GenerateHash(); - - // - static CSnapshotDelta *EmptyDelta(); - static int CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData); - static int UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData, int DataSize); + +// CSnapshotDelta + +class CSnapshotDelta +{ +public: + class CData + { + public: + int m_NumDeletedItems; + int m_NumUpdateItems; + int m_NumTempItems; // needed? + int m_pData[1]; + }; + +private: + // TODO: strange arbitrary number + short m_aItemSizes[64]; + int m_aSnapshotDataRate[0xffff]; + int m_aSnapshotDataUpdates[0xffff]; + int m_SnapshotCurrent; + CData m_Empty; + + void UndiffItem(int *pPast, int *pDiff, int *pOut, int Size); + +public: + CSnapshotDelta(); + int GetDataRate(int Index) { return m_aSnapshotDataRate[Index]; } + int GetDataUpdates(int Index) { return m_aSnapshotDataUpdates[Index]; } + void SetStaticsize(int ItemType, int Size); + CData *EmptyDelta(); + int CreateDelta(class CSnapshot *pFrom, class CSnapshot *pTo, void *pData); + int UnpackDelta(class CSnapshot *pFrom, class CSnapshot *pTo, void *pData, int DataSize); }; -/* CSnapshotStorage */ +// CSnapshotStorage class CSnapshotStorage { @@ -115,4 +133,4 @@ public: }; -#endif /* ENGINE_SNAPSHOT_H */ +#endif // ENGINE_SNAPSHOT_H diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp new file mode 100644 index 00000000..491795ad --- /dev/null +++ b/src/engine/shared/storage.cpp @@ -0,0 +1,196 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> +#include <engine/storage.h> +#include "engine.h" + +// compiled-in data-dir path +#define DATA_DIR "data" + +class CStorage : public IStorage +{ +public: + char m_aApplicationSavePath[512]; + char m_aDatadir[512]; + + CStorage() + { + m_aApplicationSavePath[0] = 0; + m_aDatadir[0] = 0; + } + + int Init(const char *pApplicationName, const char *pArgv0) + { + char aPath[1024] = {0}; + fs_storage_path(pApplicationName, m_aApplicationSavePath, sizeof(m_aApplicationSavePath)); + if(fs_makedir(m_aApplicationSavePath) == 0) + { + str_format(aPath, sizeof(aPath), "%s/screenshots", m_aApplicationSavePath); + fs_makedir(aPath); + + str_format(aPath, sizeof(aPath), "%s/maps", m_aApplicationSavePath); + fs_makedir(aPath); + + str_format(aPath, sizeof(aPath), "%s/downloadedmaps", m_aApplicationSavePath); + fs_makedir(aPath); + + str_format(aPath, sizeof(aPath), "%s/demos", m_aApplicationSavePath); + fs_makedir(aPath); + } + + return FindDatadir(pArgv0); + } + + int FindDatadir(const char *pArgv0) + { + // 1) use provided data-dir override + if(m_aDatadir[0]) + { + if(fs_is_dir(m_aDatadir)) + return 0; + else + { + dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", m_aDatadir); + return -1; + } + } + + // 2) use data-dir in PWD if present + if(fs_is_dir("data/mapres")) + { + str_copy(m_aDatadir, "data", sizeof(m_aDatadir)); + return 0; + } + + // 3) use compiled-in data-dir if present + if (fs_is_dir(DATA_DIR "/mapres")) + { + str_copy(m_aDatadir, DATA_DIR, sizeof(m_aDatadir)); + return 0; + } + + // 4) check for usable path in argv[0] + { + unsigned int Pos = ~0U; + for(unsigned i = 0; pArgv0[i]; i++) + if(pArgv0[i] == '/') + Pos = i; + + if (Pos < sizeof(m_aDatadir)) + { + char aBaseDir[sizeof(m_aDatadir)]; + str_copy(aBaseDir, pArgv0, Pos); + aBaseDir[Pos] = '\0'; + str_format(m_aDatadir, sizeof(m_aDatadir), "%s/data", aBaseDir); + + if (fs_is_dir(m_aDatadir)) + return 0; + } + } + + #if defined(CONF_FAMILY_UNIX) + // 5) check for all default locations + { + const char *aDirs[] = { + "/usr/share/teeworlds/data", + "/usr/share/games/teeworlds/data", + "/usr/local/share/teeworlds/data", + "/usr/local/share/games/teeworlds/data", + "/opt/teeworlds/data" + }; + const int DirsCount = sizeof(aDirs) / sizeof(aDirs[0]); + + int i; + for (i = 0; i < DirsCount; i++) + { + if (fs_is_dir(aDirs[i])) + { + str_copy(m_aDatadir, aDirs[i], sizeof(m_aDatadir)); + return 0; + } + } + } + #endif + + // no data-dir found + dbg_msg("engine/datadir", "warning no data directory found"); + return -1; + } + + virtual void ListDirectory(int Types, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) + { + char aBuffer[1024]; + + // list current directory + if(Types&TYPE_CURRENT) + { + fs_listdir(pPath, pfnCallback, pUser); + } + + // list users directory + if(Types&TYPE_SAVE) + { + str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aApplicationSavePath, pPath); + fs_listdir(aBuffer, pfnCallback, pUser); + } + + // list datadir directory + if(Types&TYPE_DATA) + { + str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aDatadir, pPath); + fs_listdir(aBuffer, pfnCallback, pUser); + } + } + + virtual IOHANDLE OpenFile(const char *pFilename, int Flags, char *pBuffer = 0, int BufferSize = 0) + { + char aBuffer[1024]; + if(!pBuffer) + { + pBuffer = aBuffer; + BufferSize = sizeof(aBuffer); + } + + if(Flags&IOFLAG_WRITE) + { + str_format(pBuffer, BufferSize, "%s/%s", m_aApplicationSavePath, pFilename); + return io_open(pBuffer, Flags); + } + else + { + IOHANDLE Handle = 0; + + // check current directory + Handle = io_open(pFilename, Flags); + if(Handle) + return Handle; + + // check user directory + str_format(pBuffer, BufferSize, "%s/%s", m_aApplicationSavePath, pFilename); + Handle = io_open(pBuffer, Flags); + if(Handle) + return Handle; + + // check normal data directory + str_format(pBuffer, BufferSize, "%s/%s", m_aDatadir, pFilename); + Handle = io_open(pBuffer, Flags); + if(Handle) + return Handle; + } + + pBuffer[0] = 0; + return 0; + } + + static IStorage *Create(const char *pApplicationName, const char *pArgv0) + { + CStorage *p = new CStorage(); + if(p->Init(pApplicationName, pArgv0)) + { + delete p; + p = 0; + } + return p; + } +}; + +IStorage *CreateStorage(const char *pApplicationName, const char *pArgv0) { return CStorage::Create(pApplicationName, pArgv0); } diff --git a/src/engine/sound.h b/src/engine/sound.h new file mode 100644 index 00000000..bbbc4288 --- /dev/null +++ b/src/engine/sound.h @@ -0,0 +1,40 @@ +#ifndef ENGINE_SOUND_H +#define ENGINE_SOUND_H + +#include "kernel.h" + +class ISound : public IInterface +{ + MACRO_INTERFACE("sound", 0) +public: + enum + { + FLAG_LOOP=1, + FLAG_POS=2, + FLAG_ALL=3 + }; + + virtual int LoadWV(const char *pFilename) = 0; + + virtual void SetChannel(int ChannelId, float Volume, float Panning) = 0; + virtual void SetListenerPos(float x, float y) = 0; + + virtual int PlayAt(int ChannelId, int SoundId, int Flags, float x, float y) = 0; + virtual int Play(int ChannelId, int SoundId, int Flags) = 0; + virtual void Stop(int VoiceId) = 0; + virtual void StopAll() = 0; +}; + + +class IEngineSound : public ISound +{ + MACRO_INTERFACE("enginesound", 0) +public: + virtual int Init() = 0; + virtual int Update() = 0; + virtual int Shutdown() = 0; +}; + +extern IEngineSound *CreateEngineSound(); + +#endif diff --git a/src/engine/storage.h b/src/engine/storage.h new file mode 100644 index 00000000..4f875d40 --- /dev/null +++ b/src/engine/storage.h @@ -0,0 +1,25 @@ +#ifndef ENGINE_STORAGE_H +#define ENGINE_STORAGE_H + +#include "kernel.h" + +class IStorage : public IInterface +{ + MACRO_INTERFACE("storage", 0) +public: + enum + { + TYPE_SAVE = 1, + TYPE_CURRENT = 2, + TYPE_DATA = 4, + TYPE_ALL = ~0 + }; + + virtual void ListDirectory(int Types, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) = 0; + virtual IOHANDLE OpenFile(const char *pFilename, int Flags, char *pBuffer = 0, int BufferSize = 0) = 0; +}; + +extern IStorage *CreateStorage(const char *pApplicationName, const char *pArgv0); + + +#endif diff --git a/src/engine/textrender.h b/src/engine/textrender.h new file mode 100644 index 00000000..7c7e036b --- /dev/null +++ b/src/engine/textrender.h @@ -0,0 +1,60 @@ +#ifndef ENGINE_TEXTRENDER_H +#define ENGINE_TEXTRENDER_H +#include "kernel.h" + +enum +{ + TEXTFLAG_RENDER=1, + TEXTFLAG_ALLOW_NEWLINE=2, + TEXTFLAG_STOP_AT_END=4 +}; + +class CFont; + +class CTextCursor +{ +public: + int m_Flags; + int m_LineCount; + int m_CharCount; + + float m_StartX; + float m_StartY; + float m_LineWidth; + float m_X, m_Y; + + struct CFont *m_pFont; + float m_FontSize; +}; + +class ITextRender : public IInterface +{ + MACRO_INTERFACE("textrender", 0) +public: + virtual void SetCursor(CTextCursor *pCursor, float x, float y, float FontSize, int Flags) = 0; + + virtual CFont *LoadFont(const char *pFilename) = 0; + virtual void DestroyFont(CFont *pFont) = 0; + + virtual void SetDefaultFont(struct CFont *pFont) = 0; + + // + virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length) = 0; + + // old foolish interface + virtual void TextColor(float r, float g, float b, float a) = 0; + virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, int MaxWidth) = 0; + virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int Length) = 0; + virtual float TextLineCount(void *pFontSetV, float Size, const char *pText, int LineWidth) = 0; +}; + +class IEngineTextRender : public ITextRender +{ + MACRO_INTERFACE("enginetextrender", 0) +public: + virtual void Init() = 0; +}; + +extern IEngineTextRender *CreateEngineTextRender(); + +#endif diff --git a/src/game/client/animstate.cpp b/src/game/client/animstate.cpp index d8c20dec..cb706774 100644 --- a/src/game/client/animstate.cpp +++ b/src/game/client/animstate.cpp @@ -1,95 +1,94 @@ -#include <base/math.hpp> -#include <engine/e_client_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <base/math.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include "animstate.hpp" +#include "animstate.h" -static void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame) +static void AnimSeqEval(ANIM_SEQUENCE *pSeq, float Time, ANIM_KEYFRAME *pFrame) { - if(seq->num_frames == 0) + if(pSeq->m_NumFrames == 0) { - frame->time = 0; - frame->x = 0; - frame->y = 0; - frame->angle = 0; + pFrame->m_Time = 0; + pFrame->m_X = 0; + pFrame->m_Y = 0; + pFrame->m_Angle = 0; } - else if(seq->num_frames == 1) + else if(pSeq->m_NumFrames == 1) { - *frame = seq->frames[0]; + *pFrame = pSeq->m_aFrames[0]; } else { //time = max(0.0f, min(1.0f, time / duration)); // TODO: use clamp - ANIM_KEYFRAME *frame1 = 0; - ANIM_KEYFRAME *frame2 = 0; - float blend = 0.0f; + ANIM_KEYFRAME *pFrame1 = 0; + ANIM_KEYFRAME *pFrame2 = 0; + float Blend = 0.0f; // TODO: make this smarter.. binary search - for (int i = 1; i < seq->num_frames; i++) + for (int i = 1; i < pSeq->m_NumFrames; i++) { - if (seq->frames[i-1].time <= time && seq->frames[i].time >= time) + if (pSeq->m_aFrames[i-1].m_Time <= Time && pSeq->m_aFrames[i].m_Time >= Time) { - frame1 = &seq->frames[i-1]; - frame2 = &seq->frames[i]; - blend = (time - frame1->time) / (frame2->time - frame1->time); + pFrame1 = &pSeq->m_aFrames[i-1]; + pFrame2 = &pSeq->m_aFrames[i]; + Blend = (Time - pFrame1->m_Time) / (pFrame2->m_Time - pFrame1->m_Time); break; } } - if (frame1 && frame2) + if (pFrame1 && pFrame2) { - frame->time = time; - frame->x = mix(frame1->x, frame2->x, blend); - frame->y = mix(frame1->y, frame2->y, blend); - frame->angle = mix(frame1->angle, frame2->angle, blend); + pFrame->m_Time = Time; + pFrame->m_X = mix(pFrame1->m_X, pFrame2->m_X, Blend); + pFrame->m_Y = mix(pFrame1->m_Y, pFrame2->m_Y, Blend); + pFrame->m_Angle = mix(pFrame1->m_Angle, pFrame2->m_Angle, Blend); } } } -static void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount) +static void AnimAddKeyframe(ANIM_KEYFRAME *pSeq, ANIM_KEYFRAME *pAdded, float Amount) { - seq->x += added->x*amount; - seq->y += added->y*amount; - seq->angle += added->angle*amount; + pSeq->m_X += pAdded->m_X*Amount; + pSeq->m_Y += pAdded->m_Y*Amount; + pSeq->m_Angle += pAdded->m_Angle*Amount; } -static void anim_add(ANIMSTATE *state, ANIMSTATE *added, float amount) +static void AnimAdd(CAnimState *pState, CAnimState *pAdded, float Amount) { - anim_add_keyframe(&state->body, &added->body, amount); - anim_add_keyframe(&state->back_foot, &added->back_foot, amount); - anim_add_keyframe(&state->front_foot, &added->front_foot, amount); - anim_add_keyframe(&state->attach, &added->attach, amount); + AnimAddKeyframe(pState->GetBody(), pAdded->GetBody(), Amount); + AnimAddKeyframe(pState->GetBackFoot(), pAdded->GetBackFoot(), Amount); + AnimAddKeyframe(pState->GetFrontFoot(), pAdded->GetFrontFoot(), Amount); + AnimAddKeyframe(pState->GetAttach(), pAdded->GetAttach(), Amount); } -void ANIMSTATE::set(ANIMATION *anim, float time) +void CAnimState::Set(ANIMATION *pAnim, float Time) { - anim_seq_eval(&anim->body, time, &body); - anim_seq_eval(&anim->back_foot, time, &back_foot); - anim_seq_eval(&anim->front_foot, time, &front_foot); - anim_seq_eval(&anim->attach, time, &attach); + AnimSeqEval(&pAnim->m_Body, Time, &m_Body); + AnimSeqEval(&pAnim->m_BackFoot, Time, &m_BackFoot); + AnimSeqEval(&pAnim->m_FrontFoot, Time, &m_FrontFoot); + AnimSeqEval(&pAnim->m_Attach, Time, &m_Attach); } -void ANIMSTATE::add(ANIMATION *anim, float time, float amount) +void CAnimState::Add(ANIMATION *pAnim, float Time, float Amount) { - ANIMSTATE add; - add.set(anim, time); - anim_add(this, &add, amount); + CAnimState Add; + Add.Set(pAnim, Time); + AnimAdd(this, &Add, Amount); } -ANIMSTATE *ANIMSTATE::get_idle() +CAnimState *CAnimState::GetIdle() { - static ANIMSTATE state; - static bool init = true; + static CAnimState State; + static bool Init = true; - if(init) + if(Init) { - state.set(&data->animations[ANIM_BASE], 0); - state.add(&data->animations[ANIM_IDLE], 0, 1.0f); - init = false; + State.Set(&g_pData->m_aAnimations[ANIM_BASE], 0); + State.Add(&g_pData->m_aAnimations[ANIM_IDLE], 0, 1.0f); + Init = false; } - return &state; + return &State; } diff --git a/src/game/client/animstate.h b/src/game/client/animstate.h new file mode 100644 index 00000000..e7fce9a1 --- /dev/null +++ b/src/game/client/animstate.h @@ -0,0 +1,22 @@ +#ifndef GAME_CLIENT_ANIMSTATE_H +#define GAME_CLIENT_ANIMSTATE_H + +class CAnimState +{ + ANIM_KEYFRAME m_Body; + ANIM_KEYFRAME m_BackFoot; + ANIM_KEYFRAME m_FrontFoot; + ANIM_KEYFRAME m_Attach; + +public: + ANIM_KEYFRAME *GetBody() { return &m_Body; }; + ANIM_KEYFRAME *GetBackFoot() { return &m_BackFoot; }; + ANIM_KEYFRAME *GetFrontFoot() { return &m_FrontFoot; }; + ANIM_KEYFRAME *GetAttach() { return &m_Attach; }; + void Set(ANIMATION *pAnim, float Time); + void Add(ANIMATION *pAdded, float Time, float Amount); + + static CAnimState *GetIdle(); +}; + +#endif diff --git a/src/game/client/animstate.hpp b/src/game/client/animstate.hpp deleted file mode 100644 index 4b84dd66..00000000 --- a/src/game/client/animstate.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef GAME_CLIENT_ANIMATION_H -#define GAME_CLIENT_ANIMATION_H - -class ANIMSTATE -{ -public: - ANIM_KEYFRAME body; - ANIM_KEYFRAME back_foot; - ANIM_KEYFRAME front_foot; - ANIM_KEYFRAME attach; - - void set(ANIMATION *anim, float time); - void add(ANIMATION *added, float time, float amount); - - static ANIMSTATE *get_idle(); -}; - -//void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame); -//void anim_eval(ANIMATION *anim, float time, ANIM_STATE *state); -//void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount); -//void anim_add(ANIM_STATE *state, ANIM_STATE *added, float amount); -//void anim_eval_add(ANIM_STATE *state, ANIMATION *anim, float time, float amount); - -#endif diff --git a/src/game/client/clienthooks.cpp b/src/game/client/clienthooks.cpp deleted file mode 100644 index 76fa8dcd..00000000 --- a/src/game/client/clienthooks.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include <string.h> -#include <engine/e_client_interface.h> -#include <game/version.hpp> - -#include "gameclient.hpp" -#include "components/console.hpp" - - - -// clean hooks -void modc_entergame() {} -void modc_shutdown() {} -void modc_console_init() { gameclient.on_console_init(); } -void modc_save_config() { gameclient.on_save(); } -void modc_init() { gameclient.on_init(); } -void modc_connected() { gameclient.on_connected(); } -void modc_predict() { gameclient.on_predict(); } -void modc_newsnapshot() { gameclient.on_snapshot(); } -int modc_snap_input(int *data) { return gameclient.on_snapinput(data); } -void modc_statechange(int state, int old) { gameclient.on_statechange(state, old); } -void modc_render() { gameclient.on_render(); } -void modc_message(int msgtype) { gameclient.on_message(msgtype); } -void modc_rcon_line(const char *line) { gameclient.console->print_line(1, line); } - -const char *modc_net_version() { return GAME_NETVERSION; } -const char *modc_getitemname(int type) { return netobj_get_name(type); } - diff --git a/src/game/client/component.h b/src/game/client/component.h new file mode 100644 index 00000000..410be623 --- /dev/null +++ b/src/game/client/component.h @@ -0,0 +1,43 @@ +#ifndef GAME_CLIENT_COMPONENT_H +#define GAME_CLIENT_COMPONENT_H + +#include <engine/input.h> +#include "gameclient.h" + +class CComponent +{ +protected: + friend class CGameClient; + + CGameClient *m_pClient; + + // perhaps propagte pointers for these as well + class IKernel *Kernel() const { return m_pClient->Kernel(); } + class IGraphics *Graphics() const { return m_pClient->Graphics(); } + class ITextRender *TextRender() const { return m_pClient->TextRender(); } + class IClient *Client() const { return m_pClient->Client(); } + class IInput *Input() const { return m_pClient->Input(); } + class IStorage *Storage() const { return m_pClient->Storage(); } + class CUI *UI() const { return m_pClient->UI(); } + class ISound *Sound() const { return m_pClient->Sound(); } + class CRenderTools *RenderTools() const { return m_pClient->RenderTools(); } + class IConsole *Console() const { return m_pClient->Console(); } + class IDemoPlayer *DemoPlayer() const { return m_pClient->DemoPlayer(); } + class IServerBrowser *ServerBrowser() const { return m_pClient->ServerBrowser(); } + class CLayers *Layers() const { return m_pClient->Layers(); } + class CCollision *Collision() const { return m_pClient->Collision(); } +public: + virtual ~CComponent() {} + + virtual void OnStateChange(int NewState, int OldState) {}; + virtual void OnConsoleInit() {}; + virtual void OnInit() {}; + virtual void OnReset() {}; + virtual void OnRender() {}; + virtual void OnMapLoad() {}; + virtual void OnMessage(int Msg, void *pRawMsg) {} + virtual bool OnMouseMove(float x, float y) { return false; } + virtual bool OnInput(IInput::CEvent e) { return false; } +}; + +#endif diff --git a/src/game/client/component.hpp b/src/game/client/component.hpp deleted file mode 100644 index 6534e56b..00000000 --- a/src/game/client/component.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef GAME_CLIENT_GAMESYSTEM_H -#define GAME_CLIENT_GAMESYSTEM_H - -#include <engine/e_client_interface.h> -#include "gameclient.hpp" - -class GAMECLIENT; - -class COMPONENT -{ -protected: - friend class GAMECLIENT; - - GAMECLIENT *client; - - // perhaps propagte pointers for these as well - class IGraphics *Graphics() const { return client->Graphics(); } - class CUI *UI() const { return client->UI(); } - class CRenderTools *RenderTools() const { return client->RenderTools(); } -public: - virtual ~COMPONENT() {} - - virtual void on_statechange(int new_state, int old_state) {}; - virtual void on_console_init() {}; - virtual void on_init() {}; - virtual void on_save() {}; - virtual void on_reset() {}; - virtual void on_render() {}; - virtual void on_mapload() {}; - virtual void on_message(int msg, void *rawmsg) {} - virtual bool on_mousemove(float x, float y) { return false; } - virtual bool on_input(INPUT_EVENT e) { return false; } -}; - -#endif diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index e4252656..533658f2 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -1,184 +1,188 @@ -#include <stdlib.h> // atoi -#include <string.h> // strcmp -#include <engine/e_client_interface.h> -#include "binds.hpp" +#include <engine/config.h> +#include <engine/shared/config.h> +#include "binds.h" -bool BINDS::BINDS_SPECIAL::on_input(INPUT_EVENT e) +bool CBinds::CBindsSpecial::OnInput(IInput::CEvent Event) { // don't handle invalid events and keys that arn't set to anything - if(e.key >= KEY_F1 && e.key <= KEY_F15 && binds->keybindings[e.key][0] != 0) + if(Event.m_Key >= KEY_F1 && Event.m_Key <= KEY_F15 && m_pBinds->m_aaKeyBindings[Event.m_Key][0] != 0) { - int stroke = 0; - if(e.flags&INPFLAG_PRESS) - stroke = 1; - console_execute_line_stroked(stroke, binds->keybindings[e.key]); + int Stroke = 0; + if(Event.m_Flags&IInput::FLAG_PRESS) + Stroke = 1; + + m_pBinds->GetConsole()->ExecuteLineStroked(Stroke, m_pBinds->m_aaKeyBindings[Event.m_Key]); return true; } return false; } -BINDS::BINDS() +CBinds::CBinds() { - mem_zero(keybindings, sizeof(keybindings)); - special_binds.binds = this; + mem_zero(m_aaKeyBindings, sizeof(m_aaKeyBindings)); + m_SpecialBinds.m_pBinds = this; } -void BINDS::bind(int keyid, const char *str) +void CBinds::Bind(int KeyId, const char *pStr) { - if(keyid < 0 || keyid >= KEY_LAST) + if(KeyId < 0 || KeyId >= KEY_LAST) return; - str_copy(keybindings[keyid], str, sizeof(keybindings[keyid])); - if(!keybindings[keyid][0]) - dbg_msg("binds", "unbound %s (%d)", inp_key_name(keyid), keyid); + str_copy(m_aaKeyBindings[KeyId], pStr, sizeof(m_aaKeyBindings[KeyId])); + if(!m_aaKeyBindings[KeyId][0]) + dbg_msg("binds", "unbound %s (%d)", Input()->KeyName(KeyId), KeyId); else - dbg_msg("binds", "bound %s (%d) = %s", inp_key_name(keyid), keyid, keybindings[keyid]); + dbg_msg("binds", "bound %s (%d) = %s", Input()->KeyName(KeyId), KeyId, m_aaKeyBindings[KeyId]); } -bool BINDS::on_input(INPUT_EVENT e) +bool CBinds::OnInput(IInput::CEvent e) { // don't handle invalid events and keys that arn't set to anything - if(e.key <= 0 || e.key >= KEY_LAST || keybindings[e.key][0] == 0) + if(e.m_Key <= 0 || e.m_Key >= KEY_LAST || m_aaKeyBindings[e.m_Key][0] == 0) return false; - int stroke = 0; - if(e.flags&INPFLAG_PRESS) - stroke = 1; - console_execute_line_stroked(stroke, keybindings[e.key]); + int Stroke = 0; + if(e.m_Flags&IInput::FLAG_PRESS) + Stroke = 1; + Console()->ExecuteLineStroked(Stroke, m_aaKeyBindings[e.m_Key]); return true; } -void BINDS::unbindall() +void CBinds::UnbindAll() { for(int i = 0; i < KEY_LAST; i++) - keybindings[i][0] = 0; + m_aaKeyBindings[i][0] = 0; } -const char *BINDS::get(int keyid) +const char *CBinds::Get(int KeyId) { - if(keyid > 0 && keyid < KEY_LAST) - return keybindings[keyid]; + if(KeyId > 0 && KeyId < KEY_LAST) + return m_aaKeyBindings[KeyId]; return ""; } -const char *BINDS::get_key(const char *bindstr) +const char *CBinds::GetKey(const char *pBindStr) { - for(int keyid = 0; keyid < KEY_LAST; keyid++) + for(int KeyId = 0; KeyId < KEY_LAST; KeyId++) { - const char *bind = get(keyid); - if(!bind[0]) + const char *pBind = Get(KeyId); + if(!pBind[0]) continue; - if(strcmp(bind, bindstr) == 0) - return inp_key_name(keyid); + if(str_comp(pBind, pBindStr) == 0) + return Input()->KeyName(KeyId); } return ""; } -void BINDS::set_defaults() +void CBinds::SetDefaults() { // set default key bindings - unbindall(); - bind(KEY_F1, "toggle_local_console"); - bind(KEY_F2, "toggle_remote_console"); - bind(KEY_TAB, "+scoreboard"); - bind(KEY_F10, "screenshot"); - - bind('a', "+left"); - bind('d', "+right"); - - bind(KEY_SPACE, "+jump"); - bind(KEY_MOUSE_1, "+fire"); - bind(KEY_MOUSE_2, "+hook"); - bind(KEY_LSHIFT, "+emote"); - - bind('1', "+weapon1"); - bind('2', "+weapon2"); - bind('3', "+weapon3"); - bind('4', "+weapon4"); - bind('5', "+weapon5"); + UnbindAll(); + Bind(KEY_F1, "toggle_local_console"); + Bind(KEY_F2, "toggle_remote_console"); + Bind(KEY_TAB, "+scoreboard"); + Bind(KEY_F10, "screenshot"); + + Bind('a', "+left"); + Bind('d', "+right"); + + Bind(KEY_SPACE, "+jump"); + Bind(KEY_MOUSE_1, "+fire"); + Bind(KEY_MOUSE_2, "+hook"); + Bind(KEY_LSHIFT, "+emote"); + + Bind('1', "+weapon1"); + Bind('2', "+weapon2"); + Bind('3', "+weapon3"); + Bind('4', "+weapon4"); + Bind('5', "+weapon5"); - bind(KEY_MOUSE_WHEEL_UP, "+prevweapon"); - bind(KEY_MOUSE_WHEEL_DOWN, "+nextweapon"); + Bind(KEY_MOUSE_WHEEL_UP, "+prevweapon"); + Bind(KEY_MOUSE_WHEEL_DOWN, "+nextweapon"); - bind('t', "chat all"); - bind('y', "chat team"); + Bind('t', "chat all"); + Bind('y', "chat team"); - bind(KEY_F3, "vote yes"); - bind(KEY_F4, "vote no"); + Bind(KEY_F3, "vote yes"); + Bind(KEY_F4, "vote no"); } -void BINDS::on_console_init() +void CBinds::OnConsoleInit() { // bindings - MACRO_REGISTER_COMMAND("bind", "sr", CFGFLAG_CLIENT, con_bind, this, "Bind key to execute the command"); - MACRO_REGISTER_COMMAND("unbind", "s", CFGFLAG_CLIENT, con_unbind, this, "Unbind key"); - MACRO_REGISTER_COMMAND("unbindall", "", CFGFLAG_CLIENT, con_unbindall, this, "Unbind all keys"); - MACRO_REGISTER_COMMAND("dump_binds", "", CFGFLAG_CLIENT, con_dump_binds, this, "Dump binds"); + IConfig *pConfig = Kernel()->RequestInterface<IConfig>(); + if(pConfig) + pConfig->RegisterCallback(ConfigSaveCallback, this); + + Console()->Register("bind", "sr", CFGFLAG_CLIENT, ConBind, this, "Bind key to execute the command"); + Console()->Register("unbind", "s", CFGFLAG_CLIENT, ConUnbind, this, "Unbind key"); + Console()->Register("unbindall", "", CFGFLAG_CLIENT, ConUnbindAll, this, "Unbind all keys"); + Console()->Register("dump_binds", "", CFGFLAG_CLIENT, ConDumpBinds, this, "Dump binds"); // default bindings - set_defaults(); + SetDefaults(); } -void BINDS::con_bind(void *result, void *user_data) +void CBinds::ConBind(IConsole::IResult *pResult, void *pUserData) { - BINDS *binds = (BINDS *)user_data; - const char *key_name = console_arg_string(result, 0); - int id = binds->get_key_id(key_name); + CBinds *pBinds = (CBinds *)pUserData; + const char *pKeyName = pResult->GetString(0); + int id = pBinds->GetKeyId(pKeyName); if(!id) { - dbg_msg("binds", "key %s not found", key_name); + dbg_msg("binds", "key %s not found", pKeyName); return; } - binds->bind(id, console_arg_string(result, 1)); + pBinds->Bind(id, pResult->GetString(1)); } -void BINDS::con_unbind(void *result, void *user_data) +void CBinds::ConUnbind(IConsole::IResult *pResult, void *pUserData) { - BINDS *binds = (BINDS *)user_data; - const char *key_name = console_arg_string(result, 0); - int id = binds->get_key_id(key_name); + CBinds *pBinds = (CBinds *)pUserData; + const char *pKeyName = pResult->GetString(0); + int id = pBinds->GetKeyId(pKeyName); if(!id) { - dbg_msg("binds", "key %s not found", key_name); + dbg_msg("binds", "key %s not found", pKeyName); return; } - binds->bind(id, ""); + pBinds->Bind(id, ""); } -void BINDS::con_unbindall(void *result, void *user_data) +void CBinds::ConUnbindAll(IConsole::IResult *pResult, void *pUserData) { - BINDS *binds = (BINDS *)user_data; - binds->unbindall(); + CBinds *pBinds = (CBinds *)pUserData; + pBinds->UnbindAll(); } -void BINDS::con_dump_binds(void *result, void *user_data) +void CBinds::ConDumpBinds(IConsole::IResult *pResult, void *pUserData) { - BINDS *binds = (BINDS *)user_data; + CBinds *pBinds = (CBinds *)pUserData; for(int i = 0; i < KEY_LAST; i++) { - if(binds->keybindings[i][0] == 0) + if(pBinds->m_aaKeyBindings[i][0] == 0) continue; - dbg_msg("binds", "%s (%d) = %s", inp_key_name(i), i, binds->keybindings[i]); + dbg_msg("binds", "%s (%d) = %s", pBinds->Input()->KeyName(i), i, pBinds->m_aaKeyBindings[i]); } } -int BINDS::get_key_id(const char *key_name) +int CBinds::GetKeyId(const char *pKeyName) { // check for numeric - if(key_name[0] == '&') + if(pKeyName[0] == '&') { - int i = atoi(key_name+1); + int i = str_toint(pKeyName+1); if(i > 0 && i < KEY_LAST) return i; // numeric } @@ -186,37 +190,39 @@ int BINDS::get_key_id(const char *key_name) // search for key for(int i = 0; i < KEY_LAST; i++) { - if(strcmp(key_name, inp_key_name(i)) == 0) + if(str_comp(pKeyName, Input()->KeyName(i)) == 0) return i; } return 0; } -void BINDS::on_save() +void CBinds::ConfigSaveCallback(IConfig *pConfig, void *pUserData) { - char buffer[256]; - char *end = buffer+sizeof(buffer)-8; - client_save_line("unbindall"); + CBinds *pSelf = (CBinds *)pUserData; + + char aBuffer[256]; + char *pEnd = aBuffer+sizeof(aBuffer)-8; + pConfig->WriteLine("unbindall"); for(int i = 0; i < KEY_LAST; i++) { - if(keybindings[i][0] == 0) + if(pSelf->m_aaKeyBindings[i][0] == 0) continue; - str_format(buffer, sizeof(buffer), "bind %s ", inp_key_name(i)); + str_format(aBuffer, sizeof(aBuffer), "bind %s ", pSelf->Input()->KeyName(i)); // process the string. we need to escape some characters - const char *src = keybindings[i]; - char *dst = buffer + strlen(buffer); - *dst++ = '"'; - while(*src && dst < end) + const char *pSrc = pSelf->m_aaKeyBindings[i]; + char *pDst = aBuffer + str_length(aBuffer); + *pDst++ = '"'; + while(*pSrc && pDst < pEnd) { - if(*src == '"' || *src == '\\') // escape \ and " - *dst++ = '\\'; - *dst++ = *src++; + if(*pSrc == '"' || *pSrc == '\\') // escape \ and " + *pDst++ = '\\'; + *pDst++ = *pSrc++; } - *dst++ = '"'; - *dst++ = 0; + *pDst++ = '"'; + *pDst++ = 0; - client_save_line(buffer); + pConfig->WriteLine(aBuffer); } } diff --git a/src/game/client/components/binds.h b/src/game/client/components/binds.h new file mode 100644 index 00000000..e8393979 --- /dev/null +++ b/src/game/client/components/binds.h @@ -0,0 +1,41 @@ +#ifndef GAME_CLIENT_COMPONENTS_BINDS_H +#define GAME_CLIENT_COMPONENTS_BINDS_H +#include <game/client/component.h> +#include <engine/keys.h> + +class CBinds : public CComponent +{ + char m_aaKeyBindings[KEY_LAST][128]; + + int GetKeyId(const char *pKeyName); + + static void ConBind(IConsole::IResult *pResult, void *pUserData); + static void ConUnbind(IConsole::IResult *pResult, void *pUserData); + static void ConUnbindAll(IConsole::IResult *pResult, void *pUserData); + static void ConDumpBinds(IConsole::IResult *pResult, void *pUserData); + class IConsole *GetConsole() const { return Console(); } + + static void ConfigSaveCallback(class IConfig *pConfig, void *pUserData); + +public: + CBinds(); + + class CBindsSpecial : public CComponent + { + public: + CBinds *m_pBinds; + virtual bool OnInput(IInput::CEvent Event); + }; + + CBindsSpecial m_SpecialBinds; + + void Bind(int KeyId, const char *pStr); + void SetDefaults(); + void UnbindAll(); + const char *Get(int KeyId); + const char *GetKey(const char *pBindStr); + + virtual void OnConsoleInit(); + virtual bool OnInput(IInput::CEvent Event); +}; +#endif diff --git a/src/game/client/components/binds.hpp b/src/game/client/components/binds.hpp deleted file mode 100644 index bdf242f9..00000000 --- a/src/game/client/components/binds.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <game/client/component.hpp> - -class BINDS : public COMPONENT -{ - char keybindings[KEY_LAST][128]; - - int get_key_id(const char *key_name); - - static void con_bind(void *result, void *user_data); - static void con_unbind(void *result, void *user_data); - static void con_unbindall(void *result, void *user_data); - static void con_dump_binds(void *result, void *user_data); - -public: - BINDS(); - - class BINDS_SPECIAL : public COMPONENT - { - public: - BINDS *binds; - virtual bool on_input(INPUT_EVENT e); - }; - - BINDS_SPECIAL special_binds; - - void bind(int keyid, const char *str); - void set_defaults(); - void unbindall(); - const char *get(int keyid); - const char *get_key(const char *bindstr); - - virtual void on_save(); - virtual void on_console_init(); - virtual bool on_input(INPUT_EVENT e); -}; diff --git a/src/game/client/components/broadcast.cpp b/src/game/client/components/broadcast.cpp index b86ed658..c3eb3b56 100644 --- a/src/game/client/components/broadcast.cpp +++ b/src/game/client/components/broadcast.cpp @@ -1,36 +1,36 @@ -#include <engine/e_client_interface.h> -#include <engine/e_config.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/client/gameclient.hpp> +#include <game/client/gameclient.h> -#include "broadcast.hpp" +#include "broadcast.h" -void BROADCAST::on_reset() +void CBroadcast::OnReset() { - broadcast_time = 0; + m_BroadcastTime = 0; } -void BROADCAST::on_render() +void CBroadcast::OnRender() { Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); - if(time_get() < broadcast_time) + if(time_get() < m_BroadcastTime) { - float w = gfx_text_width(0, 14, broadcast_text, -1); - gfx_text(0, 150*Graphics()->ScreenAspect()-w/2, 35, 14, broadcast_text, -1); + float w = TextRender()->TextWidth(0, 14, m_aBroadcastText, -1); + TextRender()->Text(0, 150*Graphics()->ScreenAspect()-w/2, 35, 14, m_aBroadcastText, -1); } } -void BROADCAST::on_message(int msgtype, void *rawmsg) +void CBroadcast::OnMessage(int MsgType, void *pRawMsg) { - if(msgtype == NETMSGTYPE_SV_BROADCAST) + if(MsgType == NETMSGTYPE_SV_BROADCAST) { - NETMSG_SV_BROADCAST *msg = (NETMSG_SV_BROADCAST *)rawmsg; - str_copy(broadcast_text, msg->message, sizeof(broadcast_text)); - broadcast_time = time_get()+time_freq()*10; + CNetMsg_Sv_Broadcast *pMsg = (CNetMsg_Sv_Broadcast *)pRawMsg; + str_copy(m_aBroadcastText, pMsg->m_pMessage, sizeof(m_aBroadcastText)); + m_BroadcastTime = time_get()+time_freq()*10; } } diff --git a/src/game/client/components/broadcast.h b/src/game/client/components/broadcast.h new file mode 100644 index 00000000..ed281a2f --- /dev/null +++ b/src/game/client/components/broadcast.h @@ -0,0 +1,17 @@ +#ifndef GAME_CLIENT_COMPONENTS_BROADCAST_H +#define GAME_CLIENT_COMPONENTS_BROADCAST_H +#include <game/client/component.h> + +class CBroadcast : public CComponent +{ +public: + // broadcasts + char m_aBroadcastText[1024]; + int64 m_BroadcastTime; + + virtual void OnReset(); + virtual void OnRender(); + virtual void OnMessage(int MsgType, void *pRawMsg); +}; + +#endif diff --git a/src/game/client/components/broadcast.hpp b/src/game/client/components/broadcast.hpp deleted file mode 100644 index 102201cc..00000000 --- a/src/game/client/components/broadcast.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#include <game/client/component.hpp> - -class BROADCAST : public COMPONENT -{ -public: - // broadcasts - char broadcast_text[1024]; - int64 broadcast_time; - - virtual void on_reset(); - virtual void on_render(); - virtual void on_message(int msgtype, void *rawmsg); -}; - diff --git a/src/game/client/components/camera.cpp b/src/game/client/components/camera.cpp index 7b188e00..96baf459 100644 --- a/src/game/client/components/camera.cpp +++ b/src/game/client/components/camera.cpp @@ -1,40 +1,37 @@ -extern "C" { - #include <engine/e_config.h> - #include <engine/e_client_interface.h> -} +#include <engine/shared/config.h> -#include <base/math.hpp> -#include <game/collision.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/component.hpp> +#include <base/math.h> +#include <game/collision.h> +#include <game/client/gameclient.h> +#include <game/client/component.h> -#include "camera.hpp" -#include "controls.hpp" +#include "camera.h" +#include "controls.h" -CAMERA::CAMERA() +CCamera::CCamera() { } -void CAMERA::on_render() +void CCamera::OnRender() { //vec2 center; - zoom = 1.0f; + m_Zoom = 1.0f; // update camera center - if(gameclient.snap.spectate) - center = gameclient.controls->mouse_pos; + if(m_pClient->m_Snap.m_Spectate) + m_Center = m_pClient->m_pControls->m_MousePos; else { - float l = length(gameclient.controls->mouse_pos); - float deadzone = config.cl_mouse_deadzone; - float follow_factor = config.cl_mouse_followfactor/100.0f; - vec2 camera_offset(0, 0); + float l = length(m_pClient->m_pControls->m_MousePos); + float DeadZone = g_Config.m_ClMouseDeadzone; + float FollowFactor = g_Config.m_ClMouseFollowfactor/100.0f; + vec2 CameraOffset(0, 0); - float offset_amount = max(l-deadzone, 0.0f) * follow_factor; + float OffsetAmount = max(l-DeadZone, 0.0f) * FollowFactor; if(l > 0.0001f) // make sure that this isn't 0 - camera_offset = normalize(gameclient.controls->mouse_pos)*offset_amount; + CameraOffset = normalize(m_pClient->m_pControls->m_MousePos)*OffsetAmount; - center = gameclient.local_character_pos + camera_offset; + m_Center = m_pClient->m_LocalCharacterPos + CameraOffset; } } diff --git a/src/game/client/components/camera.h b/src/game/client/components/camera.h new file mode 100644 index 00000000..9654bdf6 --- /dev/null +++ b/src/game/client/components/camera.h @@ -0,0 +1,16 @@ +#ifndef GAME_CLIENT_COMPONENTS_CAMERA_H +#define GAME_CLIENT_COMPONENTS_CAMERA_H +#include <base/vmath.h> +#include <game/client/component.h> + +class CCamera : public CComponent +{ +public: + vec2 m_Center; + float m_Zoom; + + CCamera(); + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/camera.hpp b/src/game/client/components/camera.hpp deleted file mode 100644 index 1cb05f5b..00000000 --- a/src/game/client/components/camera.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#include <base/vmath.hpp> -#include <game/client/component.hpp> - -class CAMERA : public COMPONENT -{ -public: - vec2 center; - float zoom; - - CAMERA(); - virtual void on_render(); -}; - diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index fdf1d21b..1a2c828d 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -1,227 +1,263 @@ -#include <string.h> // strcmp -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/keys.h> +#include <engine/shared/config.h> -#include <game/client/gameclient.hpp> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/client/components/sounds.hpp> +#include <game/client/gameclient.h> -#include "chat.hpp" +#include <game/client/components/sounds.h> +#include <game/localization.h> -void CHAT::on_statechange(int new_state, int old_state) +#include "chat.h" + + +CChat::CChat() { - if(old_state <= CLIENTSTATE_CONNECTING) + OnReset(); +} + +void CChat::OnReset() +{ + for(int i = 0; i < MAX_LINES; i++) { - mode = MODE_NONE; + m_aLines[i].m_Time = 0; + m_aLines[i].m_aText[0] = 0; + m_aLines[i].m_aName[0] = 0; + } +} + +void CChat::OnStateChange(int NewState, int OldState) +{ + if(OldState <= IClient::STATE_CONNECTING) + { + m_Mode = MODE_NONE; for(int i = 0; i < MAX_LINES; i++) - lines[i].time = 0; - current_line = 0; + m_aLines[i].m_Time = 0; + m_CurrentLine = 0; } } -void CHAT::con_say(void *result, void *user_data) +void CChat::ConSay(IConsole::IResult *pResult, void *pUserData) { - ((CHAT*)user_data)->say(0, console_arg_string(result, 0)); + ((CChat*)pUserData)->Say(0, pResult->GetString(0)); } -void CHAT::con_sayteam(void *result, void *user_data) +void CChat::ConSayTeam(IConsole::IResult *pResult, void *pUserData) { - ((CHAT*)user_data)->say(1, console_arg_string(result, 0)); + ((CChat*)pUserData)->Say(1, pResult->GetString(0)); } -void CHAT::con_chat(void *result, void *user_data) +void CChat::ConChat(IConsole::IResult *pResult, void *pUserData) { - const char *mode = console_arg_string(result, 0); - if(strcmp(mode, "all") == 0) - ((CHAT*)user_data)->enable_mode(0); - else if(strcmp(mode, "team") == 0) - ((CHAT*)user_data)->enable_mode(1); + const char *pMode = pResult->GetString(0); + if(str_comp(pMode, "all") == 0) + ((CChat*)pUserData)->EnableMode(0); + else if(str_comp(pMode, "team") == 0) + ((CChat*)pUserData)->EnableMode(1); else dbg_msg("console", "expected all or team as mode"); } -void CHAT::on_console_init() +void CChat::OnConsoleInit() { - MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_CLIENT, con_say, this, "Say in chat"); - MACRO_REGISTER_COMMAND("say_team", "r", CFGFLAG_CLIENT, con_sayteam, this, "Say in team chat"); - MACRO_REGISTER_COMMAND("chat", "s", CFGFLAG_CLIENT, con_chat, this, "Enable chat with all/team mode"); + Console()->Register("say", "r", CFGFLAG_CLIENT, ConSay, this, "Say in chat"); + Console()->Register("say_team", "r", CFGFLAG_CLIENT, ConSayTeam, this, "Say in team chat"); + Console()->Register("chat", "s", CFGFLAG_CLIENT, ConChat, this, "Enable chat with all/team mode"); } -bool CHAT::on_input(INPUT_EVENT e) +bool CChat::OnInput(IInput::CEvent e) { - if(mode == MODE_NONE) + if(m_Mode == MODE_NONE) return false; - if(e.flags&INPFLAG_PRESS && e.key == KEY_ESCAPE) - mode = MODE_NONE; - else if(e.flags&INPFLAG_PRESS && (e.key == KEY_RETURN || e.key == KEY_KP_ENTER)) + if(e.m_Flags&IInput::FLAG_PRESS && e.m_Key == KEY_ESCAPE) + m_Mode = MODE_NONE; + else if(e.m_Flags&IInput::FLAG_PRESS && (e.m_Key == KEY_RETURN || e.m_Key == KEY_KP_ENTER)) { - if(input.get_string()[0]) - gameclient.chat->say(mode == MODE_ALL ? 0 : 1, input.get_string()); - mode = MODE_NONE; + if(m_Input.GetString()[0]) + Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString()); + m_Mode = MODE_NONE; } else - input.process_input(e); + m_Input.ProcessInput(e); return true; } -void CHAT::enable_mode(int team) +void CChat::EnableMode(int Team) { - if(mode == MODE_NONE) + if(m_Mode == MODE_NONE) { - if(team) - mode = MODE_TEAM; + if(Team) + m_Mode = MODE_TEAM; else - mode = MODE_ALL; + m_Mode = MODE_ALL; - input.clear(); - inp_clear_events(); + m_Input.Clear(); + Input()->ClearEvents(); } } -void CHAT::on_message(int msgtype, void *rawmsg) +void CChat::OnMessage(int MsgType, void *pRawMsg) { - if(msgtype == NETMSGTYPE_SV_CHAT) + if(MsgType == NETMSGTYPE_SV_CHAT) { - NETMSG_SV_CHAT *msg = (NETMSG_SV_CHAT *)rawmsg; - add_line(msg->cid, msg->team, msg->message); + CNetMsg_Sv_Chat *pMsg = (CNetMsg_Sv_Chat *)pRawMsg; + AddLine(pMsg->m_Cid, pMsg->m_Team, pMsg->m_pMessage); } } -void CHAT::add_line(int client_id, int team, const char *line) +void CChat::AddLine(int ClientId, int Team, const char *pLine) { - current_line = (current_line+1)%MAX_LINES; - lines[current_line].time = time_get(); - lines[current_line].client_id = client_id; - lines[current_line].team = team; - lines[current_line].name_color = -2; - - if(client_id == -1) // server message + char *p = const_cast<char*>(pLine); + while(*p) { - str_copy(lines[current_line].name, "*** ", sizeof(lines[current_line].name)); - str_format(lines[current_line].text, sizeof(lines[current_line].text), "%s", line); - } - else - { - if(gameclient.clients[client_id].team == -1) - lines[current_line].name_color = -1; + pLine = p; + // find line seperator and strip multiline + while(*p) + { + if(*p++ == '\n') + { + *(p-1) = 0; + break; + } + } - if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) + m_CurrentLine = (m_CurrentLine+1)%MAX_LINES; + m_aLines[m_CurrentLine].m_Time = time_get(); + m_aLines[m_CurrentLine].m_ClientId = ClientId; + m_aLines[m_CurrentLine].m_Team = Team; + m_aLines[m_CurrentLine].m_NameColor = -2; + + if(ClientId == -1) // server message + { + str_copy(m_aLines[m_CurrentLine].m_aName, "*** ", sizeof(m_aLines[m_CurrentLine].m_aName)); + str_format(m_aLines[m_CurrentLine].m_aText, sizeof(m_aLines[m_CurrentLine].m_aText), "%s", pLine); + } + else { - if(gameclient.clients[client_id].team == 0) - lines[current_line].name_color = 0; - else if(gameclient.clients[client_id].team == 1) - lines[current_line].name_color = 1; + if(m_pClient->m_aClients[ClientId].m_Team == -1) + m_aLines[m_CurrentLine].m_NameColor = -1; + + if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) + { + if(m_pClient->m_aClients[ClientId].m_Team == 0) + m_aLines[m_CurrentLine].m_NameColor = 0; + else if(m_pClient->m_aClients[ClientId].m_Team == 1) + m_aLines[m_CurrentLine].m_NameColor = 1; + } + + str_copy(m_aLines[m_CurrentLine].m_aName, m_pClient->m_aClients[ClientId].m_aName, sizeof(m_aLines[m_CurrentLine].m_aName)); + str_format(m_aLines[m_CurrentLine].m_aText, sizeof(m_aLines[m_CurrentLine].m_aText), ": %s", pLine); } - str_copy(lines[current_line].name, gameclient.clients[client_id].name, sizeof(lines[current_line].name)); - str_format(lines[current_line].text, sizeof(lines[current_line].text), ": %s", line); + char aBuf[1024]; + str_format(aBuf, sizeof(aBuf), "[chat]%s%s", m_aLines[m_CurrentLine].m_aName, m_aLines[m_CurrentLine].m_aText); + Console()->Print(aBuf); } - + // play sound - if(client_id >= 0) - gameclient.sounds->play(SOUNDS::CHN_GUI, SOUND_CHAT_CLIENT, 0, vec2(0,0)); + if(ClientId >= 0) + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0, vec2(0,0)); else - gameclient.sounds->play(SOUNDS::CHN_GUI, SOUND_CHAT_SERVER, 0, vec2(0,0)); - - dbg_msg("chat", "%s%s", lines[current_line].name, lines[current_line].text); + m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0, vec2(0,0)); } -void CHAT::on_render() +void CChat::OnRender() { Graphics()->MapScreen(0,0,300*Graphics()->ScreenAspect(),300); float x = 10.0f; float y = 300.0f-20.0f; - if(mode != MODE_NONE) + if(m_Mode != MODE_NONE) { // render chat input - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, x, y, 8.0f, TEXTFLAG_RENDER); - cursor.line_width = 200.0f; + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, x, y, 8.0f, TEXTFLAG_RENDER); + Cursor.m_LineWidth = 200.0f; - if(mode == MODE_ALL) - gfx_text_ex(&cursor, localize("All"), -1); - else if(mode == MODE_TEAM) - gfx_text_ex(&cursor, localize("Team"), -1); + if(m_Mode == MODE_ALL) + TextRender()->TextEx(&Cursor, Localize("All"), -1); + else if(m_Mode == MODE_TEAM) + TextRender()->TextEx(&Cursor, Localize("Team"), -1); else - gfx_text_ex(&cursor, localize("Chat"), -1); + TextRender()->TextEx(&Cursor, Localize("Chat"), -1); - gfx_text_ex(&cursor, ": ", -1); + TextRender()->TextEx(&Cursor, ": ", -1); - gfx_text_ex(&cursor, input.get_string(), input.cursor_offset()); - TEXT_CURSOR marker = cursor; - gfx_text_ex(&marker, "|", -1); - gfx_text_ex(&cursor, input.get_string()+input.cursor_offset(), -1); + TextRender()->TextEx(&Cursor, m_Input.GetString(), m_Input.GetCursorOffset()); + CTextCursor Marker = Cursor; + TextRender()->TextEx(&Marker, "|", -1); + TextRender()->TextEx(&Cursor, m_Input.GetString()+m_Input.GetCursorOffset(), -1); } y -= 8; int i; + int64 Now = time_get(); for(i = 0; i < MAX_LINES; i++) { - int r = ((current_line-i)+MAX_LINES)%MAX_LINES; - if(time_get() > lines[r].time+15*time_freq()) + int r = ((m_CurrentLine-i)+MAX_LINES)%MAX_LINES; + if(Now > m_aLines[r].m_Time+15*time_freq()) break; - float begin = x; - float fontsize = 7.0f; + float Begin = x; + float FontSize = 7.0f; // get the y offset - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, begin, 0, fontsize, 0); - cursor.line_width = 200.0f; - gfx_text_ex(&cursor, lines[r].name, -1); - gfx_text_ex(&cursor, lines[r].text, -1); - y -= cursor.y + cursor.font_size; + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, Begin, 0, FontSize, 0); + Cursor.m_LineWidth = 200.0f; + TextRender()->TextEx(&Cursor, m_aLines[r].m_aName, -1); + TextRender()->TextEx(&Cursor, m_aLines[r].m_aText, -1); + y -= Cursor.m_Y + Cursor.m_FontSize; // cut off if msgs waste too much space if(y < 200.0f) break; // reset the cursor - gfx_text_set_cursor(&cursor, begin, y, fontsize, TEXTFLAG_RENDER); - cursor.line_width = 200.0f; + TextRender()->SetCursor(&Cursor, Begin, y, FontSize, TEXTFLAG_RENDER); + Cursor.m_LineWidth = 200.0f; // render name - gfx_text_color(0.8f,0.8f,0.8f,1); - if(lines[r].client_id == -1) - gfx_text_color(1,1,0.5f,1); // system - else if(lines[r].team) - gfx_text_color(0.45f,0.9f,0.45f,1); // team message - else if(lines[r].name_color == 0) - gfx_text_color(1.0f,0.5f,0.5f,1); // red - else if(lines[r].name_color == 1) - gfx_text_color(0.7f,0.7f,1.0f,1); // blue - else if(lines[r].name_color == -1) - gfx_text_color(0.75f,0.5f,0.75f, 1); // spectator + TextRender()->TextColor(0.8f,0.8f,0.8f,1); + if(m_aLines[r].m_ClientId == -1) + TextRender()->TextColor(1,1,0.5f,1); // system + else if(m_aLines[r].m_Team) + TextRender()->TextColor(0.45f,0.9f,0.45f,1); // team message + else if(m_aLines[r].m_NameColor == 0) + TextRender()->TextColor(1.0f,0.5f,0.5f,1); // red + else if(m_aLines[r].m_NameColor == 1) + TextRender()->TextColor(0.7f,0.7f,1.0f,1); // blue + else if(m_aLines[r].m_NameColor == -1) + TextRender()->TextColor(0.75f,0.5f,0.75f, 1); // spectator // render name - gfx_text_ex(&cursor, lines[r].name, -1); + TextRender()->TextEx(&Cursor, m_aLines[r].m_aName, -1); // render line - gfx_text_color(1,1,1,1); - if(lines[r].client_id == -1) - gfx_text_color(1,1,0.5f,1); // system - else if(lines[r].team) - gfx_text_color(0.65f,1,0.65f,1); // team message + TextRender()->TextColor(1,1,1,1); + if(m_aLines[r].m_ClientId == -1) + TextRender()->TextColor(1,1,0.5f,1); // system + else if(m_aLines[r].m_Team) + TextRender()->TextColor(0.65f,1,0.65f,1); // team message - gfx_text_ex(&cursor, lines[r].text, -1); + TextRender()->TextEx(&Cursor, m_aLines[r].m_aText, -1); } - gfx_text_color(1,1,1,1); + TextRender()->TextColor(1,1,1,1); } -void CHAT::say(int team, const char *line) +void CChat::Say(int Team, const char *pLine) { // send chat message - NETMSG_CL_SAY msg; - msg.team = team; - msg.message = line; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); + CNetMsg_Cl_Say Msg; + Msg.m_Team = Team; + Msg.m_pMessage = pLine; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } diff --git a/src/game/client/components/chat.h b/src/game/client/components/chat.h new file mode 100644 index 00000000..8a33e9e8 --- /dev/null +++ b/src/game/client/components/chat.h @@ -0,0 +1,60 @@ +#ifndef GAME_CLIENT_COMPONENTS_CHAT_H +#define GAME_CLIENT_COMPONENTS_CHAT_H +#include <game/client/component.h> +#include <game/client/lineinput.h> + +class CChat : public CComponent +{ + CLineInput m_Input; + + enum + { + MAX_LINES = 10, + }; + + struct CLine + { + int64 m_Time; + int m_ClientId; + int m_Team; + int m_NameColor; + char m_aName[64]; + char m_aText[512]; + }; + + CLine m_aLines[MAX_LINES]; + int m_CurrentLine; + + // chat + enum + { + MODE_NONE=0, + MODE_ALL, + MODE_TEAM, + }; + + int m_Mode; + + static void ConSay(IConsole::IResult *pResult, void *pUserData); + static void ConSayTeam(IConsole::IResult *pResult, void *pUserData); + static void ConChat(IConsole::IResult *pResult, void *pUserData); + +public: + CChat(); + + bool IsActive() const { return m_Mode != MODE_NONE; } + + void AddLine(int ClientId, int Team, const char *pLine); + + void EnableMode(int Team); + + void Say(int Team, const char *pLine); + + virtual void OnReset(); + virtual void OnConsoleInit(); + virtual void OnStateChange(int NewState, int OldState); + virtual void OnRender(); + virtual void OnMessage(int MsgType, void *pRawMsg); + virtual bool OnInput(IInput::CEvent Event); +}; +#endif diff --git a/src/game/client/components/chat.hpp b/src/game/client/components/chat.hpp deleted file mode 100644 index ca34237d..00000000 --- a/src/game/client/components/chat.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#include <game/client/component.hpp> -#include <game/client/lineinput.hpp> - -class CHAT : public COMPONENT -{ - LINEINPUT input; - - enum - { - MAX_LINES = 10, - }; - - struct LINE - { - int64 time; - int client_id; - int team; - int name_color; - char name[64]; - char text[512]; - }; - - LINE lines[MAX_LINES]; - int current_line; - - // chat - enum - { - MODE_NONE=0, - MODE_ALL, - MODE_TEAM, - }; - - int mode; - - static void con_say(void *result, void *user_data); - static void con_sayteam(void *result, void *user_data); - static void con_chat(void *result, void *user_data); - -public: - bool is_active() const { return mode != MODE_NONE; } - - void add_line(int client_id, int team, const char *line); - - void enable_mode(int team); - - void say(int team, const char *line); - - virtual void on_console_init(); - virtual void on_statechange(int new_state, int old_state); - virtual void on_render(); - virtual void on_message(int msgtype, void *rawmsg); - virtual bool on_input(INPUT_EVENT e); -}; diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index 382cb134..7de85f69 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -1,26 +1,29 @@ -//#include "gc_console.hpp" +//#include "gc_console.h" #include <math.h> -#include <game/generated/gc_data.hpp> +#include <game/generated/client_data.h> #include <base/system.h> -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> - -#include <engine/e_ringbuffer.h> +#include <engine/shared/ringbuffer.h> +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/keys.h> +#include <engine/console.h> #include <cstring> #include <cstdio> -#include <game/client/ui.hpp> +#include <game/client/ui.h> -#include <game/version.hpp> +#include <game/version.h> -#include <game/client/lineinput.hpp> -#include <game/client/render.hpp> +#include <game/client/lineinput.h> +#include <game/client/render.h> +#include <game/client/components/menus.h> -#include "console.hpp" +#include "console.h" enum { @@ -30,479 +33,550 @@ enum CONSOLE_CLOSING, }; -CONSOLE::INSTANCE::INSTANCE(int t) +CGameConsole::CInstance::CInstance(CGameConsole *pGameConsole, int Type) { + m_pGameConsole = pGameConsole; // init ringbuffers //history = ringbuf_init(history_data, sizeof(history_data), RINGBUF_FLAG_RECYCLE); //backlog = ringbuf_init(backlog_data, sizeof(backlog_data), RINGBUF_FLAG_RECYCLE); - history_entry = 0x0; + m_pHistoryEntry = 0x0; - type = t; + m_Type = Type; - if(t == 0) - completion_flagmask = CFGFLAG_CLIENT; + if(Type == 0) + m_CompletionFlagmask = CFGFLAG_CLIENT; else - completion_flagmask = CFGFLAG_SERVER; + m_CompletionFlagmask = CFGFLAG_SERVER; - completion_buffer[0] = 0; - completion_chosen = -1; + m_aCompletionBuffer[0] = 0; + m_CompletionChosen = -1; - command = 0x0; + m_pCommand = 0x0; } -void CONSOLE::INSTANCE::execute_line(const char *line) +void CGameConsole::CInstance::ExecuteLine(const char *pLine) { - if(type == 0) - console_execute_line(line); + if(m_Type == 0) + m_pGameConsole->m_pConsole->ExecuteLine(pLine); else { - if(client_rcon_authed()) - client_rcon(line); + if(m_pGameConsole->Client()->RconAuthed()) + m_pGameConsole->Client()->Rcon(pLine); else - client_rcon_auth("", line); + m_pGameConsole->Client()->RconAuth("", pLine); } } -void CONSOLE::INSTANCE::possible_commands_complete_callback(const char *str, void *user) +void CGameConsole::CInstance::PossibleCommandsCompleteCallback(const char *pStr, void *pUser) { - CONSOLE::INSTANCE *instance = (CONSOLE::INSTANCE *)user; - if(instance->completion_chosen == instance->completion_enumeration_count) - instance->input.set(str); - instance->completion_enumeration_count++; + CGameConsole::CInstance *pInstance = (CGameConsole::CInstance *)pUser; + if(pInstance->m_CompletionChosen == pInstance->m_CompletionEnumerationCount) + pInstance->m_Input.Set(pStr); + pInstance->m_CompletionEnumerationCount++; } -void CONSOLE::INSTANCE::on_input(INPUT_EVENT e) +void CGameConsole::CInstance::OnInput(IInput::CEvent Event) { - bool handled = false; + bool Handled = false; - if(e.flags&INPFLAG_PRESS) + if(Event.m_Flags&IInput::FLAG_PRESS) { - if(e.key == KEY_RETURN || e.key == KEY_KP_ENTER) + if(Event.m_Key == KEY_RETURN || Event.m_Key == KEY_KP_ENTER) { - if(input.get_string()[0]) + if(m_Input.GetString()[0]) { - char *entry = history.Allocate(input.get_length()+1); - mem_copy(entry, input.get_string(), input.get_length()+1); - - execute_line(input.get_string()); - input.clear(); - history_entry = 0x0; + char *pEntry = m_History.Allocate(m_Input.GetLength()+1); + mem_copy(pEntry, m_Input.GetString(), m_Input.GetLength()+1); + ExecuteLine(m_Input.GetString()); + m_Input.Clear(); + m_pHistoryEntry = 0x0; } - handled = true; + Handled = true; } - else if (e.key == KEY_UP) + else if (Event.m_Key == KEY_UP) { - if (history_entry) + if (m_pHistoryEntry) { - char *test = history.Prev(history_entry); + char *pTest = m_History.Prev(m_pHistoryEntry); - if (test) - history_entry = test; + if (pTest) + m_pHistoryEntry = pTest; } else - history_entry = history.Last(); + m_pHistoryEntry = m_History.Last(); - if (history_entry) + if (m_pHistoryEntry) { - unsigned int len = strlen(history_entry); - if (len < sizeof(input) - 1) - input.set(history_entry); + unsigned int Len = str_length(m_pHistoryEntry); + if (Len < sizeof(m_Input) - 1) // TODO: WTF? + m_Input.Set(m_pHistoryEntry); } - handled = true; + Handled = true; } - else if (e.key == KEY_DOWN) + else if (Event.m_Key == KEY_DOWN) { - if (history_entry) - history_entry = history.Next(history_entry); + if (m_pHistoryEntry) + m_pHistoryEntry = m_History.Next(m_pHistoryEntry); - if (history_entry) + if (m_pHistoryEntry) { - unsigned int len = strlen(history_entry); - if (len < sizeof(input) - 1) - input.set(history_entry); + unsigned int Len = str_length(m_pHistoryEntry); + if (Len < sizeof(m_Input) - 1) // TODO: WTF? + m_Input.Set(m_pHistoryEntry); } else - input.clear(); - handled = true; + m_Input.Clear(); + Handled = true; } - else if(e.key == KEY_TAB) + else if(Event.m_Key == KEY_TAB) { - completion_chosen++; - completion_enumeration_count = 0; - console_possible_commands(completion_buffer, completion_flagmask, possible_commands_complete_callback, this); - - // handle wrapping - if(completion_enumeration_count && completion_chosen >= completion_enumeration_count) + if(m_Type == 0 || m_pGameConsole->Client()->RconAuthed()) { - completion_chosen %= completion_enumeration_count; - completion_enumeration_count = 0; - console_possible_commands(completion_buffer, completion_flagmask, possible_commands_complete_callback, this); + m_CompletionChosen++; + m_CompletionEnumerationCount = 0; + m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this); + + // handle wrapping + if(m_CompletionEnumerationCount && m_CompletionChosen >= m_CompletionEnumerationCount) + { + m_CompletionChosen %= m_CompletionEnumerationCount; + m_CompletionEnumerationCount = 0; + m_pGameConsole->m_pConsole->PossibleCommands(m_aCompletionBuffer, m_CompletionFlagmask, PossibleCommandsCompleteCallback, this); + } } } - - if(e.key != KEY_TAB) + else if(Event.m_Key == KEY_PAGEUP) + { + ++m_BacklogActPage; + } + else if(Event.m_Key == KEY_PAGEDOWN) { - completion_chosen = -1; - str_copy(completion_buffer, input.get_string(), sizeof(completion_buffer)); + --m_BacklogActPage; + if(m_BacklogActPage < 0) + m_BacklogActPage = 0; + } + } + + if(!Handled) + m_Input.ProcessInput(Event); + + if(Event.m_Flags&IInput::FLAG_PRESS) + { + if(Event.m_Key != KEY_TAB) + { + m_CompletionChosen = -1; + str_copy(m_aCompletionBuffer, m_Input.GetString(), sizeof(m_aCompletionBuffer)); } // find the current command { - char buf[64] = {0}; - const char *src = get_string(); + char aBuf[64] = {0}; + const char *pSrc = GetString(); int i = 0; - for(; i < (int)sizeof(buf) && *src && *src != ' ' && *src != ' '; i++, src++) - buf[i] = *src; - buf[i] = 0; + for(; i < (int)sizeof(aBuf) && *pSrc && *pSrc != ' ' && *pSrc != ' '; i++, pSrc++) + aBuf[i] = *pSrc; + aBuf[i] = 0; - command = console_get_command(buf); + m_pCommand = m_pGameConsole->m_pConsole->GetCommandInfo(aBuf); } } - - if(!handled) - input.process_input(e); } -void CONSOLE::INSTANCE::print_line(const char *line) +void CGameConsole::CInstance::PrintLine(const char *pLine) { - int len = strlen(line); + int Len = str_length(pLine); - if (len > 255) - len = 255; + if (Len > 255) + Len = 255; - char *entry = backlog.Allocate(len+1); - mem_copy(entry, line, len+1); + char *pEntry = m_Backlog.Allocate(Len+1); + mem_copy(pEntry, pLine, Len); + pEntry[Len] = 0; } -CONSOLE::CONSOLE() -: local_console(0), remote_console(1) +CGameConsole::CGameConsole() +: m_LocalConsole(this, 0), m_RemoteConsole(this, 1) { - console_type = 0; - console_state = CONSOLE_CLOSED; - state_change_end = 0.0f; - state_change_duration = 0.1f; + m_ConsoleType = 0; + m_ConsoleState = CONSOLE_CLOSED; + m_StateChangeEnd = 0.0f; + m_StateChangeDuration = 0.1f; } -float CONSOLE::time_now() +float CGameConsole::TimeNow() { - static long long time_start = time_get(); - return float(time_get()-time_start)/float(time_freq()); + static long long s_TimeStart = time_get(); + return float(time_get()-s_TimeStart)/float(time_freq()); } -CONSOLE::INSTANCE *CONSOLE::current_console() +CGameConsole::CInstance *CGameConsole::CurrentConsole() { - if(console_type != 0) - return &remote_console; - return &local_console; + if(m_ConsoleType != 0) + return &m_RemoteConsole; + return &m_LocalConsole; } -void CONSOLE::on_reset() +void CGameConsole::OnReset() { } // only defined for 0<=t<=1 -static float console_scale_func(float t) +static float ConsoleScaleFunc(float t) { //return t; return sinf(acosf(1.0f-t)); } -struct RENDERINFO +struct CRenderInfo { - CONSOLE *self; - TEXT_CURSOR cursor; - const char *current_cmd; - int wanted_completion; - int enum_count; + CGameConsole *m_pSelf; + CTextCursor m_Cursor; + const char *m_pCurrentCmd; + int m_WantedCompletion; + int m_EnumCount; }; -void CONSOLE::possible_commands_render_callback(const char *str, void *user) +void CGameConsole::PossibleCommandsRenderCallback(const char *pStr, void *pUser) { - RENDERINFO *info = (RENDERINFO *)user; + CRenderInfo *pInfo = static_cast<CRenderInfo *>(pUser); - if(info->enum_count == info->wanted_completion) + if(pInfo->m_EnumCount == pInfo->m_WantedCompletion) { - float tw = gfx_text_width(info->cursor.font, info->cursor.font_size, str, -1); - info->self->Graphics()->TextureSet(-1); - info->self->Graphics()->QuadsBegin(); - info->self->Graphics()->SetColor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,0.85f); - info->self->RenderTools()->draw_round_rect(info->cursor.x-3, info->cursor.y, tw+5, info->cursor.font_size+4, info->cursor.font_size/3); - info->self->Graphics()->QuadsEnd(); + float tw = pInfo->m_pSelf->TextRender()->TextWidth(pInfo->m_Cursor.m_pFont, pInfo->m_Cursor.m_FontSize, pStr, -1); + pInfo->m_pSelf->Graphics()->TextureSet(-1); + pInfo->m_pSelf->Graphics()->QuadsBegin(); + pInfo->m_pSelf->Graphics()->SetColor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,0.85f); + pInfo->m_pSelf->RenderTools()->DrawRoundRect(pInfo->m_Cursor.m_X-3, pInfo->m_Cursor.m_Y, tw+5, pInfo->m_Cursor.m_FontSize+4, pInfo->m_Cursor.m_FontSize/3); + pInfo->m_pSelf->Graphics()->QuadsEnd(); - gfx_text_color(0.05f, 0.05f, 0.05f,1); - gfx_text_ex(&info->cursor, str, -1); + pInfo->m_pSelf->TextRender()->TextColor(0.05f, 0.05f, 0.05f,1); + pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, -1); } else { - const char *match_start = str_find_nocase(str, info->current_cmd); + const char *pMatchStart = str_find_nocase(pStr, pInfo->m_pCurrentCmd); - if(match_start) + if(pMatchStart) { - gfx_text_color(0.5f,0.5f,0.5f,1); - gfx_text_ex(&info->cursor, str, match_start-str); - gfx_text_color(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,1); - gfx_text_ex(&info->cursor, match_start, strlen(info->current_cmd)); - gfx_text_color(0.5f,0.5f,0.5f,1); - gfx_text_ex(&info->cursor, match_start+strlen(info->current_cmd), -1); + pInfo->m_pSelf->TextRender()->TextColor(0.5f,0.5f,0.5f,1); + pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, pMatchStart-pStr); + pInfo->m_pSelf->TextRender()->TextColor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,1); + pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pMatchStart, str_length(pInfo->m_pCurrentCmd)); + pInfo->m_pSelf->TextRender()->TextColor(0.5f,0.5f,0.5f,1); + pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pMatchStart+str_length(pInfo->m_pCurrentCmd), -1); } else { - gfx_text_color(0.75f,0.75f,0.75f,1); - gfx_text_ex(&info->cursor, str, -1); + pInfo->m_pSelf->TextRender()->TextColor(0.75f,0.75f,0.75f,1); + pInfo->m_pSelf->TextRender()->TextEx(&pInfo->m_Cursor, pStr, -1); } } - info->enum_count++; - info->cursor.x += 7.0f; + pInfo->m_EnumCount++; + pInfo->m_Cursor.m_X += 7.0f; } -void CONSOLE::on_render() +void CGameConsole::OnRender() { - CUIRect screen = *UI()->Screen(); - float console_max_height = screen.h*3/5.0f; - float console_height; + CUIRect Screen = *UI()->Screen(); + float ConsoleMaxHeight = Screen.h*3/5.0f; + float ConsoleHeight; - float progress = (time_now()-(state_change_end-state_change_duration))/float(state_change_duration); + float Progress = (TimeNow()-(m_StateChangeEnd-m_StateChangeDuration))/float(m_StateChangeDuration); - if (progress >= 1.0f) + if (Progress >= 1.0f) { - if (console_state == CONSOLE_CLOSING) - console_state = CONSOLE_CLOSED; - else if (console_state == CONSOLE_OPENING) - console_state = CONSOLE_OPEN; + if (m_ConsoleState == CONSOLE_CLOSING) + m_ConsoleState = CONSOLE_CLOSED; + else if (m_ConsoleState == CONSOLE_OPENING) + m_ConsoleState = CONSOLE_OPEN; - progress = 1.0f; + Progress = 1.0f; } - if (console_state == CONSOLE_OPEN && config.cl_editor) - toggle(0); + if (m_ConsoleState == CONSOLE_OPEN && g_Config.m_ClEditor) + Toggle(0); - if (console_state == CONSOLE_CLOSED) + if (m_ConsoleState == CONSOLE_CLOSED) return; - if (console_state == CONSOLE_OPEN) - inp_mouse_mode_absolute(); + if (m_ConsoleState == CONSOLE_OPEN) + Input()->MouseModeAbsolute(); - float console_height_scale; + float ConsoleHeightScale; - if (console_state == CONSOLE_OPENING) - console_height_scale = console_scale_func(progress); - else if (console_state == CONSOLE_CLOSING) - console_height_scale = console_scale_func(1.0f-progress); + if (m_ConsoleState == CONSOLE_OPENING) + ConsoleHeightScale = ConsoleScaleFunc(Progress); + else if (m_ConsoleState == CONSOLE_CLOSING) + ConsoleHeightScale = ConsoleScaleFunc(1.0f-Progress); else //if (console_state == CONSOLE_OPEN) - console_height_scale = console_scale_func(1.0f); + ConsoleHeightScale = ConsoleScaleFunc(1.0f); - console_height = console_height_scale*console_max_height; + ConsoleHeight = ConsoleHeightScale*ConsoleMaxHeight; - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); // do console shadow Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); - Graphics()->SetColorVertex(0, 0,0,0, 0.5f); - Graphics()->SetColorVertex(1, 0,0,0, 0.5f); - Graphics()->SetColorVertex(2, 0,0,0, 0.0f); - Graphics()->SetColorVertex(3, 0,0,0, 0.0f); - Graphics()->QuadsDrawTL(0,console_height,screen.w,10.0f); + IGraphics::CColorVertex Array[4] = { + IGraphics::CColorVertex(0, 0,0,0, 0.5f), + IGraphics::CColorVertex(1, 0,0,0, 0.5f), + IGraphics::CColorVertex(2, 0,0,0, 0.0f), + IGraphics::CColorVertex(3, 0,0,0, 0.0f)}; + Graphics()->SetColorVertex(Array, 4); + IGraphics::CQuadItem QuadItem(0, ConsoleHeight, Screen.w, 10.0f); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); // do background - Graphics()->TextureSet(data->images[IMAGE_CONSOLE_BG].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CONSOLE_BG].m_Id); Graphics()->QuadsBegin(); Graphics()->SetColor(0.2f, 0.2f, 0.2f,0.9f); - if(console_type != 0) + if(m_ConsoleType != 0) Graphics()->SetColor(0.4f, 0.2f, 0.2f,0.9f); - Graphics()->QuadsSetSubset(0,-console_height*0.075f,screen.w*0.075f*0.5f,0); - Graphics()->QuadsDrawTL(0,0,screen.w,console_height); + Graphics()->QuadsSetSubset(0,-ConsoleHeight*0.075f,Screen.w*0.075f*0.5f,0); + QuadItem = IGraphics::CQuadItem(0, 0, Screen.w, ConsoleHeight); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); // do small bar shadow Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); - Graphics()->SetColorVertex(0, 0,0,0, 0.0f); - Graphics()->SetColorVertex(1, 0,0,0, 0.0f); - Graphics()->SetColorVertex(2, 0,0,0, 0.25f); - Graphics()->SetColorVertex(3, 0,0,0, 0.25f); - Graphics()->QuadsDrawTL(0,console_height-20,screen.w,10); + Array[0] = IGraphics::CColorVertex(0, 0,0,0, 0.0f); + Array[1] = IGraphics::CColorVertex(1, 0,0,0, 0.0f); + Array[2] = IGraphics::CColorVertex(2, 0,0,0, 0.25f); + Array[3] = IGraphics::CColorVertex(3, 0,0,0, 0.25f); + Graphics()->SetColorVertex(Array, 4); + QuadItem = IGraphics::CQuadItem(0, ConsoleHeight-20, Screen.w, 10); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); // do the lower bar - Graphics()->TextureSet(data->images[IMAGE_CONSOLE_BAR].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CONSOLE_BAR].m_Id); Graphics()->QuadsBegin(); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.9f); - Graphics()->QuadsSetSubset(0,0.1f,screen.w*0.015f,1-0.1f); - Graphics()->QuadsDrawTL(0,console_height-10.0f,screen.w,10.0f); + Graphics()->QuadsSetSubset(0,0.1f,Screen.w*0.015f,1-0.1f); + QuadItem = IGraphics::CQuadItem(0,ConsoleHeight-10.0f,Screen.w,10.0f); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); - console_height -= 22.0f; + ConsoleHeight -= 22.0f; - INSTANCE *console = current_console(); + CInstance *pConsole = CurrentConsole(); { - float font_size = 10.0f; - float row_height = font_size*1.25f; + float FontSize = 10.0f; + float RowHeight = FontSize*1.25f; float x = 3; - float y = console_height - row_height - 2; + float y = ConsoleHeight - RowHeight - 2; // render prompt - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, x, y, font_size, TEXTFLAG_RENDER); - - RENDERINFO info; - info.self = this; - info.wanted_completion = console->completion_chosen; - info.enum_count = 0; - info.current_cmd = console->completion_buffer; - gfx_text_set_cursor(&info.cursor, x, y+12.0f, font_size, TEXTFLAG_RENDER); - - const char *prompt = "> "; - if(console_type) + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, x, y, FontSize, TEXTFLAG_RENDER); + + CRenderInfo Info; + Info.m_pSelf = this; + Info.m_WantedCompletion = pConsole->m_CompletionChosen; + Info.m_EnumCount = 0; + Info.m_pCurrentCmd = pConsole->m_aCompletionBuffer; + TextRender()->SetCursor(&Info.m_Cursor, x, y+12.0f, FontSize, TEXTFLAG_RENDER); + + const char *pPrompt = "> "; + if(m_ConsoleType) { - if(client_state() == CLIENTSTATE_ONLINE) + if(Client()->State() == IClient::STATE_ONLINE) { - if(client_rcon_authed()) - prompt = "rcon> "; + if(Client()->RconAuthed()) + pPrompt = "rcon> "; else - prompt = "ENTER PASSWORD> "; + pPrompt = "ENTER PASSWORD> "; } else - prompt = "NOT CONNECTED> "; + pPrompt = "NOT CONNECTED> "; } - gfx_text_ex(&cursor, prompt, -1); - - // render console input - gfx_text_ex(&cursor, console->input.get_string(), console->input.cursor_offset()); - TEXT_CURSOR marker = cursor; - gfx_text_ex(&marker, "|", -1); - gfx_text_ex(&cursor, console->input.get_string()+console->input.cursor_offset(), -1); + TextRender()->TextEx(&Cursor, pPrompt, -1); + x = Cursor.m_X; // render version - char buf[128]; - str_format(buf, sizeof(buf), "v%s", GAME_VERSION); - float version_width = gfx_text_width(0, font_size, buf, -1); - gfx_text(0, screen.w-version_width-5, y, font_size, buf, -1); - - // render possible commands - if(console->input.get_string()[0] != 0) + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "v%s", GAME_VERSION); + float VersionWidth = TextRender()->TextWidth(0, FontSize, aBuf, -1); + TextRender()->Text(0, Screen.w-VersionWidth-5, y, FontSize, aBuf, -1); + + // render console input (wrap line) + int Lines = TextRender()->TextLineCount(0, FontSize, pConsole->m_Input.GetString(), Screen.w - (VersionWidth + 10 + x)); + y -= (Lines - 1) * FontSize; + TextRender()->SetCursor(&Cursor, x, y, FontSize, TEXTFLAG_RENDER); + Cursor.m_LineWidth = Screen.w - (VersionWidth + 10 + x); + + //hide rcon password + char aInputString[256]; + str_copy(aInputString, pConsole->m_Input.GetString(), sizeof(aInputString)); + if(m_ConsoleType && Client()->State() == IClient::STATE_ONLINE && !Client()->RconAuthed()) { - console_possible_commands(console->completion_buffer, console->completion_flagmask, possible_commands_render_callback, &info); + for(int i = 0; i < pConsole->m_Input.GetLength(); ++i) + aInputString[i] = '*'; + } - if(info.enum_count <= 0) + TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset()); + CTextCursor Marker = Cursor; + TextRender()->TextEx(&Marker, "|", -1); + TextRender()->TextEx(&Cursor, aInputString+pConsole->m_Input.GetCursorOffset(), -1); + + // render possible commands + if(m_ConsoleType == 0 || Client()->RconAuthed()) + { + if(pConsole->m_Input.GetString()[0] != 0) { - if(console->command) + m_pConsole->PossibleCommands(pConsole->m_aCompletionBuffer, pConsole->m_CompletionFlagmask, PossibleCommandsRenderCallback, &Info); + + if(Info.m_EnumCount <= 0) { - - char buf[512]; - str_format(buf, sizeof(buf), "Help: %s ", console->command->help); - gfx_text_ex(&info.cursor, buf, -1); - gfx_text_color(0.75f, 0.75f, 0.75f, 1); - str_format(buf, sizeof(buf), "Syntax: %s %s", console->command->name, console->command->params); - gfx_text_ex(&info.cursor, buf, -1); + if(pConsole->m_pCommand) + { + + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Help: %s ", pConsole->m_pCommand->m_pHelp); + TextRender()->TextEx(&Info.m_Cursor, aBuf, -1); + TextRender()->TextColor(0.75f, 0.75f, 0.75f, 1); + str_format(aBuf, sizeof(aBuf), "Syntax: %s %s", pConsole->m_pCommand->m_pName, pConsole->m_pCommand->m_pParams); + TextRender()->TextEx(&Info.m_Cursor, aBuf, -1); + } } } } - gfx_text_color(1,1,1,1); + TextRender()->TextColor(1,1,1,1); - // render log - y -= row_height; - char *entry = console->backlog.Last(); - while (y > 0.0f && entry) + // render log (actual page, wrap lines) + char *pEntry = pConsole->m_Backlog.Last(); + for(int Page = 0, Lines = 0; Page <= pConsole->m_BacklogActPage; ++Page, Lines = 0) { - gfx_text(0, x, y, font_size, entry, -1); - y -= row_height; + // next page when lines reach the top + while(y - Lines * RowHeight > RowHeight && pEntry) + { + Lines += TextRender()->TextLineCount(0, FontSize, pEntry, Screen.w-10); + // just render output from actual backlog page (render bottom up) + if(Page == pConsole->m_BacklogActPage) + { + TextRender()->SetCursor(&Cursor, 0, y - Lines * RowHeight, FontSize, TEXTFLAG_RENDER); + Cursor.m_LineWidth = Screen.w-10; + TextRender()->TextEx(&Cursor, pEntry, -1); + } + pEntry = pConsole->m_Backlog.Prev(pEntry); + } - entry = console->backlog.Prev(entry); + // actual backlog page number is too high, render last available page (current checked one, render top down) + if(!pEntry && Page < pConsole->m_BacklogActPage) + { + pConsole->m_BacklogActPage = Page; + pEntry = pConsole->m_Backlog.First(); + while(Lines > 0 && pEntry) + { + TextRender()->SetCursor(&Cursor, 0, y - Lines * RowHeight, FontSize, TEXTFLAG_RENDER); + Cursor.m_LineWidth = Screen.w-10; + Cursor.m_LineCount = 1; + TextRender()->TextEx(&Cursor, pEntry, -1); + Lines -= Cursor.m_LineCount; + pEntry = pConsole->m_Backlog.Next(pEntry); + } + break; + } } } } -void CONSOLE::on_message(int msgtype, void *rawmsg) +void CGameConsole::OnMessage(int MsgType, void *pRawMsg) { } -bool CONSOLE::on_input(INPUT_EVENT e) +bool CGameConsole::OnInput(IInput::CEvent Event) { - if(console_state == CONSOLE_CLOSED) + if(m_ConsoleState == CONSOLE_CLOSED) return false; - if(e.key >= KEY_F1 && e.key <= KEY_F15) + if(Event.m_Key >= KEY_F1 && Event.m_Key <= KEY_F15) return false; - if(e.key == KEY_ESCAPE && (e.flags&INPFLAG_PRESS)) - toggle(console_type); + if(Event.m_Key == KEY_ESCAPE && (Event.m_Flags&IInput::FLAG_PRESS)) + Toggle(m_ConsoleType); else - current_console()->on_input(e); + CurrentConsole()->OnInput(Event); return true; } -void CONSOLE::toggle(int type) +void CGameConsole::Toggle(int Type) { - if(console_type != type && (console_state == CONSOLE_OPEN || console_state == CONSOLE_OPENING)) + if(m_ConsoleType != Type && (m_ConsoleState == CONSOLE_OPEN || m_ConsoleState == CONSOLE_OPENING)) { // don't toggle console, just switch what console to use } else { - if (console_state == CONSOLE_CLOSED || console_state == CONSOLE_OPEN) + if (m_ConsoleState == CONSOLE_CLOSED || m_ConsoleState == CONSOLE_OPEN) { - state_change_end = time_now()+state_change_duration; + m_StateChangeEnd = TimeNow()+m_StateChangeDuration; } else { - float progress = state_change_end-time_now(); - float reversed_progress = state_change_duration-progress; + float Progress = m_StateChangeEnd-TimeNow(); + float ReversedProgress = m_StateChangeDuration-Progress; - state_change_end = time_now()+reversed_progress; + m_StateChangeEnd = TimeNow()+ReversedProgress; } - if (console_state == CONSOLE_CLOSED || console_state == CONSOLE_CLOSING) + if (m_ConsoleState == CONSOLE_CLOSED || m_ConsoleState == CONSOLE_CLOSING) { - inp_mouse_mode_absolute(); - console_state = CONSOLE_OPENING; + Input()->MouseModeAbsolute(); + m_pClient->m_pMenus->UseMouseButtons(false); + m_ConsoleState = CONSOLE_OPENING; } else { - inp_mouse_mode_relative(); - console_state = CONSOLE_CLOSING; + Input()->MouseModeRelative(); + m_pClient->m_pMenus->UseMouseButtons(true); + m_ConsoleState = CONSOLE_CLOSING; } } - console_type = type; + m_ConsoleType = Type; } -void CONSOLE::con_toggle_local_console(void *result, void *user_data) +void CGameConsole::ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData) { - ((CONSOLE *)user_data)->toggle(0); + ((CGameConsole *)pUserData)->Toggle(0); } -void CONSOLE::con_toggle_remote_console(void *result, void *user_data) +void CGameConsole::ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData) { - ((CONSOLE *)user_data)->toggle(1); + ((CGameConsole *)pUserData)->Toggle(1); } -void CONSOLE::client_console_print_callback(const char *str, void *user_data) +void CGameConsole::ClientConsolePrintCallback(const char *pStr, void *pUserData) { - ((CONSOLE *)user_data)->local_console.print_line(str); + ((CGameConsole *)pUserData)->m_LocalConsole.PrintLine(pStr); } -void CONSOLE::print_line(int type, const char *line) +void CGameConsole::PrintLine(int Type, const char *pLine) { - if(type == 0) - local_console.print_line(line); - else if(type == 1) - remote_console.print_line(line); + if(Type == 0) + m_LocalConsole.PrintLine(pLine); + else if(Type == 1) + m_RemoteConsole.PrintLine(pLine); } -void CONSOLE::on_console_init() +void CGameConsole::OnConsoleInit() { + m_pConsole = Kernel()->RequestInterface<IConsole>(); + // - console_register_print_callback(client_console_print_callback, this); + Console()->RegisterPrintCallback(ClientConsolePrintCallback, this); - MACRO_REGISTER_COMMAND("toggle_local_console", "", CFGFLAG_CLIENT, con_toggle_local_console, this, "Toggle local console"); - MACRO_REGISTER_COMMAND("toggle_remote_console", "", CFGFLAG_CLIENT, con_toggle_remote_console, this, "Toggle remote console"); + Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console"); + Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console"); } /* diff --git a/src/game/client/components/console.h b/src/game/client/components/console.h new file mode 100644 index 00000000..b88c6349 --- /dev/null +++ b/src/game/client/components/console.h @@ -0,0 +1,72 @@ +#ifndef GAME_CLIENT_COMPONENTS_CONSOLE_H +#define GAME_CLIENT_COMPONENTS_CONSOLE_H +#include <engine/shared/ringbuffer.h> +#include <game/client/component.h> +#include <game/client/lineinput.h> + +class CGameConsole : public CComponent +{ + class CInstance + { + public: + TStaticRingBuffer<char, 64*1024, CRingBufferBase::FLAG_RECYCLE> m_Backlog; + TStaticRingBuffer<char, 64*1024, CRingBufferBase::FLAG_RECYCLE> m_History; + char *m_pHistoryEntry; + + CLineInput m_Input; + int m_Type; + int m_CompletionEnumerationCount; + int m_BacklogActPage; + + public: + CGameConsole *m_pGameConsole; + + char m_aCompletionBuffer[128]; + int m_CompletionChosen; + int m_CompletionFlagmask; + + IConsole::CCommandInfo *m_pCommand; + + CInstance(CGameConsole *pGameConsole, int t); + + void ExecuteLine(const char *pLine); + + void OnInput(IInput::CEvent Event); + void PrintLine(const char *pLine); + + const char *GetString() const { return m_Input.GetString(); } + static void PossibleCommandsCompleteCallback(const char *pStr, void *pUser); + }; + + class IConsole *m_pConsole; + + CInstance m_LocalConsole; + CInstance m_RemoteConsole; + + CInstance *CurrentConsole(); + float TimeNow(); + + int m_ConsoleType; + int m_ConsoleState; + float m_StateChangeEnd; + float m_StateChangeDuration; + + void Toggle(int Type); + + static void PossibleCommandsRenderCallback(const char *pStr, void *pUser); + static void ClientConsolePrintCallback(const char *pStr, void *pUserData); + static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData); + static void ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData); + +public: + CGameConsole(); + + void PrintLine(int Type, const char *pLine); + + virtual void OnConsoleInit(); + virtual void OnReset(); + virtual void OnRender(); + virtual void OnMessage(int MsgType, void *pRawMsg); + virtual bool OnInput(IInput::CEvent Events); +}; +#endif diff --git a/src/game/client/components/console.hpp b/src/game/client/components/console.hpp deleted file mode 100644 index 78da98b8..00000000 --- a/src/game/client/components/console.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#include <engine/e_client_interface.h> -#include <engine/e_ringbuffer.h> -#include <game/client/component.hpp> -#include <game/client/lineinput.hpp> - -class CONSOLE : public COMPONENT -{ - class INSTANCE - { - public: - TStaticRingBuffer<char, 64*1024, CRingBufferBase::FLAG_RECYCLE> backlog; - TStaticRingBuffer<char, 64*1024, CRingBufferBase::FLAG_RECYCLE> history; - char *history_entry; - - LINEINPUT input; - - int type; - int completion_enumeration_count; - - public: - char completion_buffer[128]; - int completion_chosen; - int completion_flagmask; - - COMMAND *command; - - INSTANCE(int t); - - void execute_line(const char *line); - - void on_input(INPUT_EVENT e); - void print_line(const char *line); - - const char *get_string() const { return input.get_string(); } - - static void possible_commands_complete_callback(const char *str, void *user); - }; - - INSTANCE local_console; - INSTANCE remote_console; - - INSTANCE *current_console(); - float time_now(); - - int console_type; - int console_state; - float state_change_end; - float state_change_duration; - - - void toggle(int type); - - static void possible_commands_render_callback(const char *str, void *user); - static void client_console_print_callback(const char *str, void *user_data); - static void con_toggle_local_console(void *result, void *user_data); - static void con_toggle_remote_console(void *result, void *user_data); - -public: - CONSOLE(); - - void print_line(int type, const char *line); - - virtual void on_console_init(); - virtual void on_reset(); - virtual void on_render(); - virtual void on_message(int msgtype, void *rawmsg); - virtual bool on_input(INPUT_EVENT e); -}; diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp index b48fb198..714f8b0c 100644 --- a/src/game/client/components/controls.cpp +++ b/src/game/client/components/controls.cpp @@ -1,210 +1,215 @@ -#include <engine/e_client_interface.h> -#include <base/math.hpp> -#include <game/collision.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/component.hpp> -#include <game/client/components/chat.hpp> -#include <game/client/components/menus.hpp> +#include <base/math.h> -#include "controls.hpp" +#include <engine/shared/config.h> -CONTROLS::CONTROLS() +#include <game/collision.h> +#include <game/client/gameclient.h> +#include <game/client/component.h> +#include <game/client/components/chat.h> +#include <game/client/components/menus.h> + +#include "controls.h" + +CControls::CControls() { } -static void con_key_input_state(void *result, void *user_data) +static void ConKeyInputState(IConsole::IResult *pResult, void *pUserData) { - ((int *)user_data)[0] = console_arg_int(result, 0); + ((int *)pUserData)[0] = pResult->GetInteger(0); } -static void con_key_input_counter(void *result, void *user_data) +static void ConKeyInputCounter(IConsole::IResult *pResult, void *pUserData) { - int *v = (int *)user_data; - if(((*v)&1) != console_arg_int(result, 0)) + int *v = (int *)pUserData; + if(((*v)&1) != pResult->GetInteger(0)) (*v)++; *v &= INPUT_STATE_MASK; } -struct INPUTSET +struct CInputSet { - CONTROLS *controls; - int *variable; - int value; + CControls *m_pControls; + int *m_pVariable; + int m_Value; }; -static void con_key_input_set(void *result, void *user_data) +static void ConKeyInputSet(IConsole::IResult *pResult, void *pUserData) { - INPUTSET *set = (INPUTSET *)user_data; - if(console_arg_int(result, 0)) - *set->variable = set->value; + CInputSet *pSet = (CInputSet *)pUserData; + if(pResult->GetInteger(0)) + *pSet->m_pVariable = pSet->m_Value; } -static void con_key_input_nextprev_weapon(void *result, void *user_data) +static void ConKeyInputNextPrevWeapon(IConsole::IResult *pResult, void *pUserData) { - INPUTSET *set = (INPUTSET *)user_data; - con_key_input_counter(result, set->variable); - set->controls->input_data.wanted_weapon = 0; + CInputSet *pSet = (CInputSet *)pUserData; + ConKeyInputCounter(pResult, pSet->m_pVariable); + pSet->m_pControls->m_InputData.m_WantedWeapon = 0; } -void CONTROLS::on_console_init() +void CControls::OnConsoleInit() { // game commands - MACRO_REGISTER_COMMAND("+left", "", CFGFLAG_CLIENT, con_key_input_state, &input_direction_left, "Move left"); - MACRO_REGISTER_COMMAND("+right", "", CFGFLAG_CLIENT, con_key_input_state, &input_direction_right, "Move right"); - MACRO_REGISTER_COMMAND("+jump", "", CFGFLAG_CLIENT, con_key_input_state, &input_data.jump, "Jump"); - MACRO_REGISTER_COMMAND("+hook", "", CFGFLAG_CLIENT, con_key_input_state, &input_data.hook, "Hook"); - MACRO_REGISTER_COMMAND("+fire", "", CFGFLAG_CLIENT, con_key_input_counter, &input_data.fire, "Fire"); - - { static INPUTSET set = {this, &input_data.wanted_weapon, 1}; MACRO_REGISTER_COMMAND("+weapon1", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to hammer"); } - { static INPUTSET set = {this, &input_data.wanted_weapon, 2}; MACRO_REGISTER_COMMAND("+weapon2", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to gun"); } - { static INPUTSET set = {this, &input_data.wanted_weapon, 3}; MACRO_REGISTER_COMMAND("+weapon3", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to shotgun"); } - { static INPUTSET set = {this, &input_data.wanted_weapon, 4}; MACRO_REGISTER_COMMAND("+weapon4", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to grenade"); } - { static INPUTSET set = {this, &input_data.wanted_weapon, 5}; MACRO_REGISTER_COMMAND("+weapon5", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to rifle"); } - - { static INPUTSET set = {this, &input_data.next_weapon, 0}; MACRO_REGISTER_COMMAND("+nextweapon", "", CFGFLAG_CLIENT, con_key_input_nextprev_weapon, (void *)&set, "Switch to next weapon"); } - { static INPUTSET set = {this, &input_data.prev_weapon, 0}; MACRO_REGISTER_COMMAND("+prevweapon", "", CFGFLAG_CLIENT, con_key_input_nextprev_weapon, (void *)&set, "Switch to previous weapon"); } + Console()->Register("+left", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputDirectionLeft, "Move left"); + Console()->Register("+right", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputDirectionRight, "Move right"); + Console()->Register("+jump", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputData.m_Jump, "Jump"); + Console()->Register("+hook", "", CFGFLAG_CLIENT, ConKeyInputState, &m_InputData.m_Hook, "Hook"); + Console()->Register("+fire", "", CFGFLAG_CLIENT, ConKeyInputCounter, &m_InputData.m_Fire, "Fire"); + + { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 1}; Console()->Register("+weapon1", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to hammer"); } + { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 2}; Console()->Register("+weapon2", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to gun"); } + { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 3}; Console()->Register("+weapon3", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to shotgun"); } + { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 4}; Console()->Register("+weapon4", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to grenade"); } + { static CInputSet s_Set = {this, &m_InputData.m_WantedWeapon, 5}; Console()->Register("+weapon5", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to rifle"); } + + { static CInputSet s_Set = {this, &m_InputData.m_NextWeapon, 0}; Console()->Register("+nextweapon", "", CFGFLAG_CLIENT, ConKeyInputNextPrevWeapon, (void *)&s_Set, "Switch to next weapon"); } + { static CInputSet s_Set = {this, &m_InputData.m_PrevWeapon, 0}; Console()->Register("+prevweapon", "", CFGFLAG_CLIENT, ConKeyInputNextPrevWeapon, (void *)&s_Set, "Switch to previous weapon"); } } -void CONTROLS::on_message(int msg, void *rawmsg) +void CControls::OnMessage(int Msg, void *pRawMsg) { - if(msg == NETMSGTYPE_SV_WEAPONPICKUP) + if(Msg == NETMSGTYPE_SV_WEAPONPICKUP) { - NETMSG_SV_WEAPONPICKUP *msg = (NETMSG_SV_WEAPONPICKUP *)rawmsg; - if(config.cl_autoswitch_weapons) - input_data.wanted_weapon = msg->weapon+1; + CNetMsg_Sv_WeaponPickup *pMsg = (CNetMsg_Sv_WeaponPickup *)pRawMsg; + if(g_Config.m_ClAutoswitchWeapons) + m_InputData.m_WantedWeapon = pMsg->m_Weapon+1; } } -int CONTROLS::snapinput(int *data) +int CControls::SnapInput(int *pData) { - static NETOBJ_PLAYER_INPUT last_data = {0}; - static int64 last_send_time = 0; - bool send = false; + static CNetObj_PlayerInput LastData = {0}; + static int64 LastSendTime = 0; + bool Send = false; // update player state - if(gameclient.chat->is_active()) - input_data.player_state = PLAYERSTATE_CHATTING; - else if(gameclient.menus->is_active()) - input_data.player_state = PLAYERSTATE_IN_MENU; + if(m_pClient->m_pChat->IsActive()) + m_InputData.m_PlayerState = PLAYERSTATE_CHATTING; + else if(m_pClient->m_pMenus->IsActive()) + m_InputData.m_PlayerState = PLAYERSTATE_IN_MENU; else - input_data.player_state = PLAYERSTATE_PLAYING; + m_InputData.m_PlayerState = PLAYERSTATE_PLAYING; - if(last_data.player_state != input_data.player_state) - send = true; + if(LastData.m_PlayerState != m_InputData.m_PlayerState) + Send = true; - last_data.player_state = input_data.player_state; + LastData.m_PlayerState = m_InputData.m_PlayerState; // we freeze the input if chat or menu is activated - if(input_data.player_state != PLAYERSTATE_PLAYING) + if(m_InputData.m_PlayerState != PLAYERSTATE_PLAYING) { - last_data.direction = 0; - last_data.hook = 0; - last_data.jump = 0; - input_data = last_data; + LastData.m_Direction = 0; + LastData.m_Hook = 0; + LastData.m_Jump = 0; + m_InputData = LastData; - input_direction_left = 0; - input_direction_right = 0; + m_InputDirectionLeft = 0; + m_InputDirectionRight = 0; - mem_copy(data, &input_data, sizeof(input_data)); + mem_copy(pData, &m_InputData, sizeof(m_InputData)); // send once a second just to be sure - if(time_get() > last_send_time + time_freq()) - send = true; + if(time_get() > LastSendTime + time_freq()) + Send = true; } else { - input_data.target_x = (int)mouse_pos.x; - input_data.target_y = (int)mouse_pos.y; - if(!input_data.target_x && !input_data.target_y) - input_data.target_y = 1; + m_InputData.m_TargetX = (int)m_MousePos.x; + m_InputData.m_TargetY = (int)m_MousePos.y; + if(!m_InputData.m_TargetX && !m_InputData.m_TargetY) + { + m_InputData.m_TargetX = 1; + m_MousePos.x = 1; + } // set direction - input_data.direction = 0; - if(input_direction_left && !input_direction_right) - input_data.direction = -1; - if(!input_direction_left && input_direction_right) - input_data.direction = 1; + m_InputData.m_Direction = 0; + if(m_InputDirectionLeft && !m_InputDirectionRight) + m_InputData.m_Direction = -1; + if(!m_InputDirectionLeft && m_InputDirectionRight) + m_InputData.m_Direction = 1; // stress testing - if(config.dbg_stress) + if(g_Config.m_DbgStress) { - float t = client_localtime(); - mem_zero(&input_data, sizeof(input_data)); - - input_data.direction = ((int)t/2)&1; - input_data.jump = ((int)t); - input_data.fire = ((int)(t*10)); - input_data.hook = ((int)(t*2))&1; - input_data.wanted_weapon = ((int)t)%NUM_WEAPONS; - input_data.target_x = (int)(sinf(t*3)*100.0f); - input_data.target_y = (int)(cosf(t*3)*100.0f); + float t = Client()->LocalTime(); + mem_zero(&m_InputData, sizeof(m_InputData)); + + m_InputData.m_Direction = ((int)t/2)&1; + m_InputData.m_Jump = ((int)t); + m_InputData.m_Fire = ((int)(t*10)); + m_InputData.m_Hook = ((int)(t*2))&1; + m_InputData.m_WantedWeapon = ((int)t)%NUM_WEAPONS; + m_InputData.m_TargetX = (int)(sinf(t*3)*100.0f); + m_InputData.m_TargetY = (int)(cosf(t*3)*100.0f); } // check if we need to send input - if(input_data.direction != last_data.direction) send = true; - else if(input_data.jump != last_data.jump) send = true; - else if(input_data.fire != last_data.fire) send = true; - else if(input_data.hook != last_data.hook) send = true; - else if(input_data.player_state != last_data.player_state) send = true; - else if(input_data.wanted_weapon != last_data.wanted_weapon) send = true; - else if(input_data.next_weapon != last_data.next_weapon) send = true; - else if(input_data.prev_weapon != last_data.prev_weapon) send = true; + if(m_InputData.m_Direction != LastData.m_Direction) Send = true; + else if(m_InputData.m_Jump != LastData.m_Jump) Send = true; + else if(m_InputData.m_Fire != LastData.m_Fire) Send = true; + else if(m_InputData.m_Hook != LastData.m_Hook) Send = true; + else if(m_InputData.m_PlayerState != LastData.m_PlayerState) Send = true; + else if(m_InputData.m_WantedWeapon != LastData.m_WantedWeapon) Send = true; + else if(m_InputData.m_NextWeapon != LastData.m_NextWeapon) Send = true; + else if(m_InputData.m_PrevWeapon != LastData.m_PrevWeapon) Send = true; // send at at least 10hz - if(time_get() > last_send_time + time_freq()/25) - send = true; + if(time_get() > LastSendTime + time_freq()/25) + Send = true; } // copy and return size - last_data = input_data; + LastData = m_InputData; - if(!send) + if(!Send) return 0; - last_send_time = time_get(); - mem_copy(data, &input_data, sizeof(input_data)); - return sizeof(input_data); + LastSendTime = time_get(); + mem_copy(pData, &m_InputData, sizeof(m_InputData)); + return sizeof(m_InputData); } -void CONTROLS::on_render() +void CControls::OnRender() { // update target pos - if(gameclient.snap.gameobj && !(gameclient.snap.gameobj->paused || gameclient.snap.spectate)) - target_pos = gameclient.local_character_pos + mouse_pos; + if(m_pClient->m_Snap.m_pGameobj && !(m_pClient->m_Snap.m_pGameobj->m_Paused || m_pClient->m_Snap.m_Spectate)) + m_TargetPos = m_pClient->m_LocalCharacterPos + m_MousePos; } -bool CONTROLS::on_mousemove(float x, float y) +bool CControls::OnMouseMove(float x, float y) { - if(gameclient.snap.gameobj && gameclient.snap.gameobj->paused) + if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Paused) return false; - mouse_pos += vec2(x, y); // TODO: ugly + m_MousePos += vec2(x, y); // TODO: ugly // - float camera_max_distance = 200.0f; - float follow_factor = config.cl_mouse_followfactor/100.0f; - float deadzone = config.cl_mouse_deadzone; - float mouse_max = min(camera_max_distance/follow_factor + deadzone, (float)config.cl_mouse_max_distance); + float CameraMaxDistance = 200.0f; + float FollowFactor = g_Config.m_ClMouseFollowfactor/100.0f; + float DeadZone = g_Config.m_ClMouseDeadzone; + float MouseMax = min(CameraMaxDistance/FollowFactor + DeadZone, (float)g_Config.m_ClMouseMaxDistance); //vec2 camera_offset(0, 0); - if(gameclient.snap.spectate) + if(m_pClient->m_Snap.m_Spectate) { - if(mouse_pos.x < 200.0f) mouse_pos.x = 200.0f; - if(mouse_pos.y < 200.0f) mouse_pos.y = 200.0f; - if(mouse_pos.x > col_width()*32-200.0f) mouse_pos.x = col_width()*32-200.0f; - if(mouse_pos.y > col_height()*32-200.0f) mouse_pos.y = col_height()*32-200.0f; + if(m_MousePos.x < 200.0f) m_MousePos.x = 200.0f; + if(m_MousePos.y < 200.0f) m_MousePos.y = 200.0f; + if(m_MousePos.x > Collision()->GetWidth()*32-200.0f) m_MousePos.x = Collision()->GetWidth()*32-200.0f; + if(m_MousePos.y > Collision()->GetHeight()*32-200.0f) m_MousePos.y = Collision()->GetHeight()*32-200.0f; - target_pos = mouse_pos; + m_TargetPos = m_MousePos; } else { - float l = length(mouse_pos); + float l = length(m_MousePos); - if(l > mouse_max) + if(l > MouseMax) { - mouse_pos = normalize(mouse_pos)*mouse_max; - l = mouse_max; + m_MousePos = normalize(m_MousePos)*MouseMax; + l = MouseMax; } //float offset_amount = max(l-deadzone, 0.0f) * follow_factor; diff --git a/src/game/client/components/controls.h b/src/game/client/components/controls.h new file mode 100644 index 00000000..7453d5d7 --- /dev/null +++ b/src/game/client/components/controls.h @@ -0,0 +1,25 @@ +#ifndef GAME_CLIENT_COMPONENTS_CONTROLS_H +#define GAME_CLIENT_COMPONENTS_CONTROLS_H +#include <base/vmath.h> +#include <game/client/component.h> + +class CControls : public CComponent +{ +public: + vec2 m_MousePos; + vec2 m_TargetPos; + + CNetObj_PlayerInput m_InputData; + int m_InputDirectionLeft; + int m_InputDirectionRight; + + CControls(); + + virtual void OnRender(); + virtual void OnMessage(int MsgType, void *pRawMsg); + virtual bool OnMouseMove(float x, float y); + virtual void OnConsoleInit(); + + int SnapInput(int *pData); +}; +#endif diff --git a/src/game/client/components/controls.hpp b/src/game/client/components/controls.hpp deleted file mode 100644 index e33d24f5..00000000 --- a/src/game/client/components/controls.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#include <base/vmath.hpp> -#include <game/client/component.hpp> - -class CONTROLS : public COMPONENT -{ -public: - vec2 mouse_pos; - vec2 target_pos; - - NETOBJ_PLAYER_INPUT input_data; - int input_direction_left; - int input_direction_right; - - CONTROLS(); - - virtual void on_render(); - virtual void on_message(int msg, void *rawmsg); - virtual bool on_mousemove(float x, float y); - virtual void on_console_init(); - - int snapinput(int *data); -}; diff --git a/src/game/client/components/damageind.cpp b/src/game/client/components/damageind.cpp index 7f1991dc..8dfbf022 100644 --- a/src/game/client/components/damageind.cpp +++ b/src/game/client/components/damageind.cpp @@ -1,65 +1,64 @@ -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <engine/graphics.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/gamecore.hpp> // get_angle -#include <game/client/ui.hpp> -#include <game/client/render.hpp> -#include "damageind.hpp" +#include <game/gamecore.h> // get_angle +#include <game/client/ui.h> +#include <game/client/render.h> +#include "damageind.h" -DAMAGEIND::DAMAGEIND() +CDamageInd::CDamageInd() { - lastupdate = 0; - num_items = 0; + m_Lastupdate = 0; + m_NumItems = 0; } -DAMAGEIND::ITEM *DAMAGEIND::create_i() +CDamageInd::CItem *CDamageInd::CreateI() { - if (num_items < MAX_ITEMS) + if (m_NumItems < MAX_ITEMS) { - ITEM *p = &items[num_items]; - num_items++; + CItem *p = &m_aItems[m_NumItems]; + m_NumItems++; return p; } return 0; } -void DAMAGEIND::destroy_i(DAMAGEIND::ITEM *i) +void CDamageInd::DestroyI(CDamageInd::CItem *i) { - num_items--; - *i = items[num_items]; + m_NumItems--; + *i = m_aItems[m_NumItems]; } -void DAMAGEIND::create(vec2 pos, vec2 dir) +void CDamageInd::Create(vec2 Pos, vec2 Dir) { - ITEM *i = create_i(); + CItem *i = CreateI(); if (i) { - i->pos = pos; - i->life = 0.75f; - i->dir = dir*-1; - i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; + i->m_Pos = Pos; + i->m_Life = 0.75f; + i->m_Dir = Dir*-1; + i->m_StartAngle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; } } -void DAMAGEIND::on_render() +void CDamageInd::OnRender() { - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - for(int i = 0; i < num_items;) + for(int i = 0; i < m_NumItems;) { - vec2 pos = mix(items[i].pos+items[i].dir*75.0f, items[i].pos, clamp((items[i].life-0.60f)/0.15f, 0.0f, 1.0f)); + vec2 Pos = mix(m_aItems[i].m_Pos+m_aItems[i].m_Dir*75.0f, m_aItems[i].m_Pos, clamp((m_aItems[i].m_Life-0.60f)/0.15f, 0.0f, 1.0f)); - items[i].life -= client_frametime(); - if(items[i].life < 0.0f) - destroy_i(&items[i]); + m_aItems[i].m_Life -= Client()->FrameTime(); + if(m_aItems[i].m_Life < 0.0f) + DestroyI(&m_aItems[i]); else { - Graphics()->SetColor(1.0f,1.0f,1.0f, items[i].life/0.1f); - Graphics()->QuadsSetRotation(items[i].startangle + items[i].life * 2.0f); - RenderTools()->select_sprite(SPRITE_STAR1); - RenderTools()->draw_sprite(pos.x, pos.y, 48.0f); + Graphics()->SetColor(1.0f,1.0f,1.0f, m_aItems[i].m_Life/0.1f); + Graphics()->QuadsSetRotation(m_aItems[i].m_StartAngle + m_aItems[i].m_Life * 2.0f); + RenderTools()->SelectSprite(SPRITE_STAR1); + RenderTools()->DrawSprite(Pos.x, Pos.y, 48.0f); i++; } } diff --git a/src/game/client/components/damageind.h b/src/game/client/components/damageind.h new file mode 100644 index 00000000..b6e0bb47 --- /dev/null +++ b/src/game/client/components/damageind.h @@ -0,0 +1,34 @@ +#ifndef GAME_CLIENT_COMPONENTS_DAMAGEIND_H +#define GAME_CLIENT_COMPONENTS_DAMAGEIND_H +#include <base/vmath.h> +#include <game/client/component.h> + +class CDamageInd : public CComponent +{ + int64 m_Lastupdate; + struct CItem + { + vec2 m_Pos; + vec2 m_Dir; + float m_Life; + float m_StartAngle; + }; + + enum + { + MAX_ITEMS=64, + }; + + CItem m_aItems[MAX_ITEMS]; + int m_NumItems; + + CItem *CreateI(); + void DestroyI(CItem *i); + +public: + CDamageInd(); + + void Create(vec2 Pos, vec2 Dir); + virtual void OnRender(); +}; +#endif diff --git a/src/game/client/components/damageind.hpp b/src/game/client/components/damageind.hpp deleted file mode 100644 index c74af9ca..00000000 --- a/src/game/client/components/damageind.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#include <base/vmath.hpp> -#include <game/client/component.hpp> - -class DAMAGEIND : public COMPONENT -{ - int64 lastupdate; - struct ITEM - { - vec2 pos; - vec2 dir; - float life; - float startangle; - }; - - enum - { - MAX_ITEMS=64, - }; - - ITEM items[MAX_ITEMS]; - int num_items; - - ITEM *create_i(); - void destroy_i(ITEM *i); - -public: - DAMAGEIND(); - - void create(vec2 pos, vec2 dir); - virtual void on_render(); -}; diff --git a/src/game/client/components/debughud.cpp b/src/game/client/components/debughud.cpp index c7cc559b..0fa004cb 100644 --- a/src/game/client/components/debughud.cpp +++ b/src/game/client/components/debughud.cpp @@ -1,27 +1,23 @@ -#include <memory.h> // memcmp +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/textrender.h> -extern "C" { - #include <engine/e_config.h> -} - -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/layers.hpp> +#include <game/layers.h> -#include <game/client/gameclient.hpp> -#include <game/client/animstate.hpp> -#include <game/client/render.hpp> +#include <game/client/gameclient.h> +#include <game/client/animstate.h> +#include <game/client/render.h> -//#include "controls.hpp" -//#include "camera.hpp" -#include "debughud.hpp" +//#include "controls.h" +//#include "camera.h" +#include "debughud.h" -void DEBUGHUD::render_netcorrections() +void CDebugHud::RenderNetCorrections() { - if(!config.debug || !gameclient.snap.local_character || !gameclient.snap.local_prev_character) + if(!g_Config.m_Debug || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter) return; Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); @@ -29,7 +25,8 @@ void DEBUGHUD::render_netcorrections() /*float speed = distance(vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y), vec2(netobjects.local_character->x, netobjects.local_character->y));*/ - float velspeed = length(vec2(gameclient.snap.local_character->vx/256.0f, gameclient.snap.local_character->vy/256.0f))*50; +/* + float velspeed = length(vec2(gameclient.snap.local_character->m_VelX/256.0f, gameclient.snap.local_character->m_VelY/256.0f))*50; float ramp = velocity_ramp(velspeed, gameclient.tuning.velramp_start, gameclient.tuning.velramp_range, gameclient.tuning.velramp_curvature); @@ -37,77 +34,79 @@ void DEBUGHUD::render_netcorrections() str_format(buf, sizeof(buf), "%.0f\n%.0f\n%.2f\n%d %s\n%d %d", velspeed, velspeed*ramp, ramp, netobj_num_corrections(), netobj_corrected_on(), - gameclient.snap.local_character->x, - gameclient.snap.local_character->y + gameclient.snap.local_character->m_X, + gameclient.snap.local_character->m_Y ); - gfx_text(0, 150, 50, 12, buf, -1); + TextRender()->Text(0, 150, 50, 12, buf, -1);*/ } -void DEBUGHUD::render_tuning() +void CDebugHud::RenderTuning() { // render tuning debugging - if(!config.dbg_tuning) + if(!g_Config.m_DbgTuning) return; - TUNING_PARAMS standard_tuning; + CTuningParams StandardTuning; Graphics()->MapScreen(0, 0, 300*Graphics()->ScreenAspect(), 300); float y = 50.0f; - int count = 0; - for(int i = 0; i < gameclient.tuning.num(); i++) + int Count = 0; + for(int i = 0; i < m_pClient->m_Tuning.Num(); i++) { - char buf[128]; - float current, standard; - gameclient.tuning.get(i, ¤t); - standard_tuning.get(i, &standard); + char aBuf[128]; + float Current, Standard; + m_pClient->m_Tuning.Get(i, &Current); + StandardTuning.Get(i, &Standard); - if(standard == current) - gfx_text_color(1,1,1,1.0f); + if(Standard == Current) + TextRender()->TextColor(1,1,1,1.0f); else - gfx_text_color(1,0.25f,0.25f,1.0f); + TextRender()->TextColor(1,0.25f,0.25f,1.0f); float w; float x = 5.0f; - str_format(buf, sizeof(buf), "%.2f", standard); + str_format(aBuf, sizeof(aBuf), "%.2f", Standard); x += 20.0f; - w = gfx_text_width(0, 5, buf, -1); - gfx_text(0x0, x-w, y+count*6, 5, buf, -1); + w = TextRender()->TextWidth(0, 5, aBuf, -1); + TextRender()->Text(0x0, x-w, y+Count*6, 5, aBuf, -1); - str_format(buf, sizeof(buf), "%.2f", current); + str_format(aBuf, sizeof(aBuf), "%.2f", Current); x += 20.0f; - w = gfx_text_width(0, 5, buf, -1); - gfx_text(0x0, x-w, y+count*6, 5, buf, -1); + w = TextRender()->TextWidth(0, 5, aBuf, -1); + TextRender()->Text(0x0, x-w, y+Count*6, 5, aBuf, -1); x += 5.0f; - gfx_text(0x0, x, y+count*6, 5, gameclient.tuning.names[i], -1); + TextRender()->Text(0x0, x, y+Count*6, 5, m_pClient->m_Tuning.m_apNames[i], -1); - count++; + Count++; } - y = y+count*6; + y = y+Count*6; Graphics()->TextureSet(-1); Graphics()->BlendNormal(); Graphics()->LinesBegin(); - float height = 50.0f; + float Height = 50.0f; float pv = 1; + IGraphics::CLineItem Array[100]; for(int i = 0; i < 100; i++) { - float speed = i/100.0f * 3000; - float ramp = velocity_ramp(speed, gameclient.tuning.velramp_start, gameclient.tuning.velramp_range, gameclient.tuning.velramp_curvature); - float rampedspeed = (speed * ramp)/1000.0f; - Graphics()->LinesDraw((i-1)*2, y+height-pv*height, i*2, y+height-rampedspeed*height); + float Speed = i/100.0f * 3000; + float Ramp = VelocityRamp(Speed, m_pClient->m_Tuning.m_VelrampStart, m_pClient->m_Tuning.m_VelrampRange, m_pClient->m_Tuning.m_VelrampCurvature); + float RampedSpeed = (Speed * Ramp)/1000.0f; + Array[i] = IGraphics::CLineItem((i-1)*2, y+Height-pv*Height, i*2, y+Height-RampedSpeed*Height); //Graphics()->LinesDraw((i-1)*2, 200, i*2, 200); - pv = rampedspeed; + pv = RampedSpeed; } + Graphics()->LinesDraw(Array, 100); Graphics()->LinesEnd(); - gfx_text_color(1,1,1,1); + TextRender()->TextColor(1,1,1,1); } -void DEBUGHUD::on_render() +void CDebugHud::OnRender() { - render_tuning(); - render_netcorrections(); + RenderTuning(); + RenderNetCorrections(); } diff --git a/src/game/client/components/debughud.h b/src/game/client/components/debughud.h new file mode 100644 index 00000000..ae1c17ef --- /dev/null +++ b/src/game/client/components/debughud.h @@ -0,0 +1,13 @@ +#ifndef GAME_CLIENT_COMPONENTS_DEBUGHUD_H +#define GAME_CLIENT_COMPONENTS_DEBUGHUD_H +#include <game/client/component.h> + +class CDebugHud : public CComponent +{ + void RenderNetCorrections(); + void RenderTuning(); +public: + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/debughud.hpp b/src/game/client/components/debughud.hpp deleted file mode 100644 index 473b2ce2..00000000 --- a/src/game/client/components/debughud.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#include <game/client/component.hpp> - -class DEBUGHUD : public COMPONENT -{ - void render_netcorrections(); - void render_tuning(); -public: - virtual void on_render(); -}; - diff --git a/src/game/client/components/effects.cpp b/src/game/client/components/effects.cpp index c9c47a8d..91cea107 100644 --- a/src/game/client/components/effects.cpp +++ b/src/game/client/components/effects.cpp @@ -1,183 +1,181 @@ -#include <engine/e_client_interface.h> -//#include <gc_client.hpp> -#include <game/generated/gc_data.hpp> +#include <game/generated/client_data.h> -#include <game/client/components/particles.hpp> -#include <game/client/components/skins.hpp> -#include <game/client/components/flow.hpp> -#include <game/client/components/damageind.hpp> -#include <game/client/components/sounds.hpp> -#include <game/client/gameclient.hpp> +#include <game/client/components/particles.h> +#include <game/client/components/skins.h> +#include <game/client/components/flow.h> +#include <game/client/components/damageind.h> +#include <game/client/components/sounds.h> +#include <game/client/gameclient.h> -#include "effects.hpp" +#include "effects.h" -inline vec2 random_dir() { return normalize(vec2(frandom()-0.5f, frandom()-0.5f)); } +inline vec2 RandomDir() { return normalize(vec2(frandom()-0.5f, frandom()-0.5f)); } -EFFECTS::EFFECTS() +CEffects::CEffects() { - add_50hz = false; - add_100hz = false; + m_Add50hz = false; + m_Add100hz = false; } -void EFFECTS::air_jump(vec2 pos) +void CEffects::AirJump(vec2 Pos) { - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_AIRJUMP; - p.pos = pos + vec2(-6.0f, 16.0f); - p.vel = vec2(0, -200); - p.life_span = 0.5f; - p.start_size = 48.0f; - p.end_size = 0; - p.rot = frandom()*pi*2; - p.rotspeed = pi*2; - p.gravity = 500; - p.friction = 0.7f; - p.flow_affected = 0.0f; - gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_AIRJUMP; + p.m_Pos = Pos + vec2(-6.0f, 16.0f); + p.m_Vel = vec2(0, -200); + p.m_LifeSpan = 0.5f; + p.m_StartSize = 48.0f; + p.m_EndSize = 0; + p.m_Rot = frandom()*pi*2; + p.m_Rotspeed = pi*2; + p.m_Gravity = 500; + p.m_Friction = 0.7f; + p.m_FlowAffected = 0.0f; + m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); - p.pos = pos + vec2(6.0f, 16.0f); - gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + p.m_Pos = Pos + vec2(6.0f, 16.0f); + m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); - gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos); + m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, Pos); } -void EFFECTS::damage_indicator(vec2 pos, vec2 dir) +void CEffects::DamageIndicator(vec2 Pos, vec2 Dir) { - gameclient.damageind->create(pos, dir); + m_pClient->m_pDamageind->Create(Pos, Dir); } -void EFFECTS::powerupshine(vec2 pos, vec2 size) +void CEffects::PowerupShine(vec2 Pos, vec2 size) { - if(!add_50hz) + if(!m_Add50hz) return; - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_SLICE; - p.pos = pos + vec2((frandom()-0.5f)*size.x, (frandom()-0.5f)*size.y); - p.vel = vec2(0, 0); - p.life_span = 0.5f; - p.start_size = 16.0f; - p.end_size = 0; - p.rot = frandom()*pi*2; - p.rotspeed = pi*2; - p.gravity = 500; - p.friction = 0.9f; - p.flow_affected = 0.0f; - gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_SLICE; + p.m_Pos = Pos + vec2((frandom()-0.5f)*size.x, (frandom()-0.5f)*size.y); + p.m_Vel = vec2(0, 0); + p.m_LifeSpan = 0.5f; + p.m_StartSize = 16.0f; + p.m_EndSize = 0; + p.m_Rot = frandom()*pi*2; + p.m_Rotspeed = pi*2; + p.m_Gravity = 500; + p.m_Friction = 0.9f; + p.m_FlowAffected = 0.0f; + m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); } -void EFFECTS::smoketrail(vec2 pos, vec2 vel) +void CEffects::SmokeTrail(vec2 Pos, vec2 Vel) { - if(!add_50hz) + if(!m_Add50hz) return; - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_SMOKE; - p.pos = pos; - p.vel = vel + random_dir()*50.0f; - p.life_span = 0.5f + frandom()*0.5f; - p.start_size = 12.0f + frandom()*8; - p.end_size = 0; - p.friction = 0.7; - p.gravity = frandom()*-500.0f; - gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_SMOKE; + p.m_Pos = Pos; + p.m_Vel = Vel + RandomDir()*50.0f; + p.m_LifeSpan = 0.5f + frandom()*0.5f; + p.m_StartSize = 12.0f + frandom()*8; + p.m_EndSize = 0; + p.m_Friction = 0.7; + p.m_Gravity = frandom()*-500.0f; + m_pClient->m_pParticles->Add(CParticles::GROUP_PROJECTILE_TRAIL, &p); } -void EFFECTS::skidtrail(vec2 pos, vec2 vel) +void CEffects::SkidTrail(vec2 Pos, vec2 Vel) { - if(!add_100hz) + if(!m_Add100hz) return; - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_SMOKE; - p.pos = pos; - p.vel = vel + random_dir()*50.0f; - p.life_span = 0.5f + frandom()*0.5f; - p.start_size = 24.0f + frandom()*12; - p.end_size = 0; - p.friction = 0.7f; - p.gravity = frandom()*-500.0f; - p.color = vec4(0.75f,0.75f,0.75f,1.0f); - gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_SMOKE; + p.m_Pos = Pos; + p.m_Vel = Vel + RandomDir()*50.0f; + p.m_LifeSpan = 0.5f + frandom()*0.5f; + p.m_StartSize = 24.0f + frandom()*12; + p.m_EndSize = 0; + p.m_Friction = 0.7f; + p.m_Gravity = frandom()*-500.0f; + p.m_Color = vec4(0.75f,0.75f,0.75f,1.0f); + m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); } -void EFFECTS::bullettrail(vec2 pos) +void CEffects::BulletTrail(vec2 Pos) { - if(!add_100hz) + if(!m_Add100hz) return; - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_BALL; - p.pos = pos; - p.life_span = 0.25f + frandom()*0.25f; - p.start_size = 8.0f; - p.end_size = 0; - p.friction = 0.7f; - gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_BALL; + p.m_Pos = Pos; + p.m_LifeSpan = 0.25f + frandom()*0.25f; + p.m_StartSize = 8.0f; + p.m_EndSize = 0; + p.m_Friction = 0.7f; + m_pClient->m_pParticles->Add(CParticles::GROUP_PROJECTILE_TRAIL, &p); } -void EFFECTS::playerspawn(vec2 pos) +void CEffects::PlayerSpawn(vec2 Pos) { for(int i = 0; i < 32; i++) { - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_SHELL; - p.pos = pos; - p.vel = random_dir() * (pow(frandom(), 3)*600.0f); - p.life_span = 0.3f + frandom()*0.3f; - p.start_size = 64.0f + frandom()*32; - p.end_size = 0; - p.rot = frandom()*pi*2; - p.rotspeed = frandom(); - p.gravity = frandom()*-400.0f; - p.friction = 0.7f; - p.color = vec4(0xb5/255.0f, 0x50/255.0f, 0xcb/255.0f, 1.0f); - gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_SHELL; + p.m_Pos = Pos; + p.m_Vel = RandomDir() * (powf(frandom(), 3)*600.0f); + p.m_LifeSpan = 0.3f + frandom()*0.3f; + p.m_StartSize = 64.0f + frandom()*32; + p.m_EndSize = 0; + p.m_Rot = frandom()*pi*2; + p.m_Rotspeed = frandom(); + p.m_Gravity = frandom()*-400.0f; + p.m_Friction = 0.7f; + p.m_Color = vec4(0xb5/255.0f, 0x50/255.0f, 0xcb/255.0f, 1.0f); + m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); } - gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_SPAWN, 1.0f, pos); + m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_SPAWN, 1.0f, Pos); } -void EFFECTS::playerdeath(vec2 pos, int cid) +void CEffects::PlayerDeath(vec2 Pos, int Cid) { - vec3 blood_color(1.0f,1.0f,1.0f); + vec3 BloodColor(1.0f,1.0f,1.0f); - if(cid >= 0) + if(Cid >= 0) { - const SKINS::SKIN *s = gameclient.skins->get(gameclient.clients[cid].skin_id); + const CSkins::CSkin *s = m_pClient->m_pSkins->Get(m_pClient->m_aClients[Cid].m_SkinId); if(s) - blood_color = s->blood_color; + BloodColor = s->m_BloodColor; } for(int i = 0; i < 64; i++) { - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_SPLAT01 + (rand()%3); - p.pos = pos; - p.vel = random_dir() * ((frandom()+0.1f)*900.0f); - p.life_span = 0.3f + frandom()*0.3f; - p.start_size = 24.0f + frandom()*16; - p.end_size = 0; - p.rot = frandom()*pi*2; - p.rotspeed = (frandom()-0.5f) * pi; - p.gravity = 800.0f; - p.friction = 0.8f; - vec3 c = blood_color * (0.75f + frandom()*0.25f); - p.color = vec4(c.r, c.g, c.b, 0.75f); - gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_SPLAT01 + (rand()%3); + p.m_Pos = Pos; + p.m_Vel = RandomDir() * ((frandom()+0.1f)*900.0f); + p.m_LifeSpan = 0.3f + frandom()*0.3f; + p.m_StartSize = 24.0f + frandom()*16; + p.m_EndSize = 0; + p.m_Rot = frandom()*pi*2; + p.m_Rotspeed = (frandom()-0.5f) * pi; + p.m_Gravity = 800.0f; + p.m_Friction = 0.8f; + vec3 c = BloodColor * (0.75f + frandom()*0.25f); + p.m_Color = vec4(c.r, c.g, c.b, 0.75f); + m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); } } -void EFFECTS::explosion(vec2 pos) +void CEffects::Explosion(vec2 Pos) { // add to flow for(int y = -8; y <= 8; y++) @@ -187,75 +185,75 @@ void EFFECTS::explosion(vec2 pos) continue; float a = 1 - (length(vec2(x,y)) / length(vec2(8,8))); - gameclient.flow->add(pos+vec2(x,y)*16, normalize(vec2(x,y))*5000.0f*a, 10.0f); + m_pClient->m_pFlow->Add(Pos+vec2(x,y)*16, normalize(vec2(x,y))*5000.0f*a, 10.0f); } // add the explosion - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_EXPL01; - p.pos = pos; - p.life_span = 0.4f; - p.start_size = 150.0f; - p.end_size = 0; - p.rot = frandom()*pi*2; - gameclient.particles->add(PARTICLES::GROUP_EXPLOSIONS, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_EXPL01; + p.m_Pos = Pos; + p.m_LifeSpan = 0.4f; + p.m_StartSize = 150.0f; + p.m_EndSize = 0; + p.m_Rot = frandom()*pi*2; + m_pClient->m_pParticles->Add(CParticles::GROUP_EXPLOSIONS, &p); // add the smoke for(int i = 0; i < 24; i++) { - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_SMOKE; - p.pos = pos; - p.vel = random_dir() * ((1.0f + frandom()*0.2f) * 1000.0f); - p.life_span = 0.5f + frandom()*0.4f; - p.start_size = 32.0f + frandom()*8; - p.end_size = 0; - p.gravity = frandom()*-800.0f; - p.friction = 0.4f; - p.color = mix(vec4(0.75f,0.75f,0.75f,1.0f), vec4(0.5f,0.5f,0.5f,1.0f), frandom()); - gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_SMOKE; + p.m_Pos = Pos; + p.m_Vel = RandomDir() * ((1.0f + frandom()*0.2f) * 1000.0f); + p.m_LifeSpan = 0.5f + frandom()*0.4f; + p.m_StartSize = 32.0f + frandom()*8; + p.m_EndSize = 0; + p.m_Gravity = frandom()*-800.0f; + p.m_Friction = 0.4f; + p.m_Color = mix(vec4(0.75f,0.75f,0.75f,1.0f), vec4(0.5f,0.5f,0.5f,1.0f), frandom()); + m_pClient->m_pParticles->Add(CParticles::GROUP_GENERAL, &p); } } -void EFFECTS::hammerhit(vec2 pos) +void CEffects::HammerHit(vec2 Pos) { // add the explosion - PARTICLE p; - p.set_default(); - p.spr = SPRITE_PART_EXPL01; - p.pos = pos; - p.life_span = 0.4f; - p.start_size = 150.0f; - p.end_size = 0; - p.rot = frandom()*pi*2; - gameclient.particles->add(PARTICLES::GROUP_EXPLOSIONS, &p); - gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_HAMMER_HIT, 1.0f, pos); + CParticle p; + p.SetDefault(); + p.m_Spr = SPRITE_PART_EXPL01; + p.m_Pos = Pos; + p.m_LifeSpan = 0.4f; + p.m_StartSize = 150.0f; + p.m_EndSize = 0; + p.m_Rot = frandom()*pi*2; + m_pClient->m_pParticles->Add(CParticles::GROUP_EXPLOSIONS, &p); + m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_HAMMER_HIT, 1.0f, Pos); } -void EFFECTS::on_render() +void CEffects::OnRender() { - static int64 last_update_100hz = 0; - static int64 last_update_50hz = 0; + static int64 LastUpdate100hz = 0; + static int64 LastUpdate50hz = 0; - if(time_get()-last_update_100hz > time_freq()/100) + if(time_get()-LastUpdate100hz > time_freq()/100) { - add_100hz = true; - last_update_100hz = time_get(); + m_Add100hz = true; + LastUpdate100hz = time_get(); } else - add_100hz = false; + m_Add100hz = false; - if(time_get()-last_update_50hz > time_freq()/100) + if(time_get()-LastUpdate50hz > time_freq()/100) { - add_50hz = true; - last_update_50hz = time_get(); + m_Add50hz = true; + LastUpdate50hz = time_get(); } else - add_50hz = false; + m_Add50hz = false; - if(add_50hz) - gameclient.flow->update(); + if(m_Add50hz) + m_pClient->m_pFlow->Update(); } diff --git a/src/game/client/components/effects.h b/src/game/client/components/effects.h new file mode 100644 index 00000000..e8345500 --- /dev/null +++ b/src/game/client/components/effects.h @@ -0,0 +1,27 @@ +#ifndef GAME_CLIENT_COMPONENTS_EFFECTS_H +#define GAME_CLIENT_COMPONENTS_EFFECTS_H +#include <game/client/component.h> + +class CEffects : public CComponent +{ + bool m_Add50hz; + bool m_Add100hz; +public: + CEffects(); + + virtual void OnRender(); + + void BulletTrail(vec2 Pos); + void SmokeTrail(vec2 Pos, vec2 Vel); + void SkidTrail(vec2 Pos, vec2 Vel); + void Explosion(vec2 Pos); + void HammerHit(vec2 Pos); + void AirJump(vec2 Pos); + void DamageIndicator(vec2 Pos, vec2 Dir); + void PlayerSpawn(vec2 Pos); + void PlayerDeath(vec2 Pos, int ClientId); + void PowerupShine(vec2 Pos, vec2 Size); + + void Update(); +}; +#endif diff --git a/src/game/client/components/effects.hpp b/src/game/client/components/effects.hpp deleted file mode 100644 index 8574bf60..00000000 --- a/src/game/client/components/effects.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <game/client/component.hpp> - -class EFFECTS : public COMPONENT -{ - bool add_50hz; - bool add_100hz; -public: - EFFECTS(); - - virtual void on_render(); - - void bullettrail(vec2 pos); - void smoketrail(vec2 pos, vec2 vel); - void skidtrail(vec2 pos, vec2 vel); - void explosion(vec2 pos); - void hammerhit(vec2 pos); - void air_jump(vec2 pos); - void damage_indicator(vec2 pos, vec2 dir); - void playerspawn(vec2 pos); - void playerdeath(vec2 pos, int cid); - void powerupshine(vec2 pos, vec2 size); - - void update(); -}; diff --git a/src/game/client/components/emoticon.cpp b/src/game/client/components/emoticon.cpp index 8001a306..6d03f88d 100644 --- a/src/game/client/components/emoticon.cpp +++ b/src/game/client/components/emoticon.cpp @@ -1,157 +1,161 @@ -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> - -#include <game/gamecore.hpp> // get_angle -#include <game/client/gameclient.hpp> -#include <game/client/ui.hpp> -#include <game/client/render.hpp> -#include "emoticon.hpp" - -EMOTICON::EMOTICON() +#include <engine/graphics.h> +#include <engine/shared/config.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> + +#include <game/gamecore.h> // get_angle +#include <game/client/gameclient.h> +#include <game/client/ui.h> +#include <game/client/render.h> +#include "emoticon.h" + +CEmoticon::CEmoticon() { - on_reset(); + OnReset(); } -void EMOTICON::con_key_emoticon(void *result, void *user_data) +void CEmoticon::ConKeyEmoticon(IConsole::IResult *pResult, void *pUserData) { - ((EMOTICON *)user_data)->active = console_arg_int(result, 0) != 0; + ((CEmoticon *)pUserData)->m_Active = pResult->GetInteger(0) != 0; } -void EMOTICON::con_emote(void *result, void *user_data) +void CEmoticon::ConEmote(IConsole::IResult *pResult, void *pUserData) { - ((EMOTICON *)user_data)->emote(console_arg_int(result, 0)); + ((CEmoticon *)pUserData)->Emote(pResult->GetInteger(0)); } -void EMOTICON::on_console_init() +void CEmoticon::OnConsoleInit() { - MACRO_REGISTER_COMMAND("+emote", "", CFGFLAG_CLIENT, con_key_emoticon, this, "Open emote selector"); - MACRO_REGISTER_COMMAND("emote", "i", CFGFLAG_CLIENT, con_emote, this, "Use emote"); + Console()->Register("+emote", "", CFGFLAG_CLIENT, ConKeyEmoticon, this, "Open emote selector"); + Console()->Register("emote", "i", CFGFLAG_CLIENT, ConEmote, this, "Use emote"); } -void EMOTICON::on_reset() +void CEmoticon::OnReset() { - was_active = false; - active = false; - selected_emote = -1; + m_WasActive = false; + m_Active = false; + m_SelectedEmote = -1; } -void EMOTICON::on_message(int msgtype, void *rawmsg) +void CEmoticon::OnMessage(int MsgType, void *pRawMsg) { - if(msgtype == NETMSGTYPE_SV_EMOTICON) - { - NETMSG_SV_EMOTICON *msg = (NETMSG_SV_EMOTICON *)rawmsg; - gameclient.clients[msg->cid].emoticon = msg->emoticon; - gameclient.clients[msg->cid].emoticon_start = client_tick(); - } } -bool EMOTICON::on_mousemove(float x, float y) +bool CEmoticon::OnMouseMove(float x, float y) { - if(!active) + if(!m_Active) return false; - selector_mouse += vec2(x,y); + m_SelectorMouse += vec2(x,y); return true; } -void EMOTICON::draw_circle(float x, float y, float r, int segments) +void CEmoticon::DrawCircle(float x, float y, float r, int Segments) { - float f_segments = (float)segments; - for(int i = 0; i < segments; i+=2) + IGraphics::CFreeformItem Array[32]; + int NumItems = 0; + float FSegments = (float)Segments; + for(int i = 0; i < Segments; i+=2) { - float a1 = i/f_segments * 2*pi; - float a2 = (i+1)/f_segments * 2*pi; - float a3 = (i+2)/f_segments * 2*pi; - float ca1 = cosf(a1); - float ca2 = cosf(a2); - float ca3 = cosf(a3); - float sa1 = sinf(a1); - float sa2 = sinf(a2); - float sa3 = sinf(a3); - - client->Graphics()->QuadsDrawFreeform( + float a1 = i/FSegments * 2*pi; + float a2 = (i+1)/FSegments * 2*pi; + float a3 = (i+2)/FSegments * 2*pi; + float Ca1 = cosf(a1); + float Ca2 = cosf(a2); + float Ca3 = cosf(a3); + float Sa1 = sinf(a1); + float Sa2 = sinf(a2); + float Sa3 = sinf(a3); + + Array[NumItems++] = IGraphics::CFreeformItem( x, y, - x+ca1*r, y+sa1*r, - x+ca3*r, y+sa3*r, - x+ca2*r, y+sa2*r); + x+Ca1*r, y+Sa1*r, + x+Ca3*r, y+Sa3*r, + x+Ca2*r, y+Sa2*r); + if(NumItems == 32) + { + m_pClient->Graphics()->QuadsDrawFreeform(Array, 32); + NumItems = 0; + } } + if(NumItems) + m_pClient->Graphics()->QuadsDrawFreeform(Array, NumItems); } -void EMOTICON::on_render() +void CEmoticon::OnRender() { - if(!active) + if(!m_Active) { - if(was_active && selected_emote != -1) - emote(selected_emote); - was_active = false; + if(m_WasActive && m_SelectedEmote != -1) + Emote(m_SelectedEmote); + m_WasActive = false; return; } - was_active = true; + m_WasActive = true; int x, y; - inp_mouse_relative(&x, &y); + Input()->MouseRelative(&x, &y); - selector_mouse.x += x; - selector_mouse.y += y; + m_SelectorMouse.x += x; + m_SelectorMouse.y += y; - if (length(selector_mouse) > 140) - selector_mouse = normalize(selector_mouse) * 140; + if (length(m_SelectorMouse) > 140) + m_SelectorMouse = normalize(m_SelectorMouse) * 140; - float selected_angle = get_angle(selector_mouse) + 2*pi/24; - if (selected_angle < 0) - selected_angle += 2*pi; + float SelectedAngle = GetAngle(m_SelectorMouse) + 2*pi/24; + if (SelectedAngle < 0) + SelectedAngle += 2*pi; - if (length(selector_mouse) > 100) - selected_emote = (int)(selected_angle / (2*pi) * 12.0f); + if (length(m_SelectorMouse) > 100) + m_SelectedEmote = (int)(SelectedAngle / (2*pi) * 12.0f); - CUIRect screen = *UI()->Screen(); + CUIRect Screen = *UI()->Screen(); - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.3f); - draw_circle(screen.w/2, screen.h/2, 160, 64); + DrawCircle(Screen.w/2, Screen.h/2, 160, 64); Graphics()->QuadsEnd(); - Graphics()->TextureSet(data->images[IMAGE_EMOTICONS].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_EMOTICONS].m_Id); Graphics()->QuadsBegin(); for (int i = 0; i < 12; i++) { - float angle = 2*pi*i/12.0; - if (angle > pi) - angle -= 2*pi; + float Angle = 2*pi*i/12.0; + if (Angle > pi) + Angle -= 2*pi; - bool selected = selected_emote == i; + bool Selected = m_SelectedEmote == i; - float size = selected ? 96 : 64; + float Size = Selected ? 96 : 64; - float nudge_x = 120 * cos(angle); - float nudge_y = 120 * sin(angle); - RenderTools()->select_sprite(SPRITE_OOP + i); - Graphics()->QuadsDraw(screen.w/2 + nudge_x, screen.h/2 + nudge_y, size, size); + float NudgeX = 120 * cosf(Angle); + float NudgeY = 120 * sinf(Angle); + RenderTools()->SelectSprite(SPRITE_OOP + i); + IGraphics::CQuadItem QuadItem(Screen.w/2 + NudgeX, Screen.h/2 + NudgeY, Size, Size); + Graphics()->QuadsDraw(&QuadItem, 1); } Graphics()->QuadsEnd(); - Graphics()->TextureSet(data->images[IMAGE_CURSOR].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CURSOR].m_Id); Graphics()->QuadsBegin(); Graphics()->SetColor(1,1,1,1); - Graphics()->QuadsDrawTL(selector_mouse.x+screen.w/2,selector_mouse.y+screen.h/2,24,24); + IGraphics::CQuadItem QuadItem(m_SelectorMouse.x+Screen.w/2,m_SelectorMouse.y+Screen.h/2,24,24); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } -void EMOTICON::emote(int emoticon) +void CEmoticon::Emote(int Emoticon) { - NETMSG_CL_EMOTICON msg; - msg.emoticon = emoticon; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); + CNetMsg_Cl_Emoticon Msg; + Msg.m_Emoticon = Emoticon; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } diff --git a/src/game/client/components/emoticon.h b/src/game/client/components/emoticon.h new file mode 100644 index 00000000..e10b57da --- /dev/null +++ b/src/game/client/components/emoticon.h @@ -0,0 +1,31 @@ +#ifndef GAME_CLIENT_COMPONENTS_EMOTICON_H +#define GAME_CLIENT_COMPONENTS_EMOTICON_H +#include <base/vmath.h> +#include <game/client/component.h> + +class CEmoticon : public CComponent +{ + void DrawCircle(float x, float y, float r, int Segments); + + bool m_WasActive; + bool m_Active; + + vec2 m_SelectorMouse; + int m_SelectedEmote; + + static void ConKeyEmoticon(IConsole::IResult *pResult, void *pUserData); + static void ConEmote(IConsole::IResult *pResult, void *pUserData); + +public: + CEmoticon(); + + virtual void OnReset(); + virtual void OnConsoleInit(); + virtual void OnRender(); + virtual void OnMessage(int MsgType, void *pRawMsg); + virtual bool OnMouseMove(float x, float y); + + void Emote(int Emoticon); +}; + +#endif diff --git a/src/game/client/components/emoticon.hpp b/src/game/client/components/emoticon.hpp deleted file mode 100644 index 446b4b00..00000000 --- a/src/game/client/components/emoticon.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#include <base/vmath.hpp> -#include <game/client/component.hpp> - -class EMOTICON : public COMPONENT -{ - void draw_circle(float x, float y, float r, int segments); - - bool was_active; - bool active; - - vec2 selector_mouse; - int selected_emote; - - static void con_key_emoticon(void *result, void *user_data); - static void con_emote(void *result, void *user_data); - -public: - EMOTICON(); - - virtual void on_reset(); - virtual void on_console_init(); - virtual void on_render(); - virtual void on_message(int msgtype, void *rawmsg); - virtual bool on_mousemove(float x, float y); - - void emote(int emoticon); -}; - diff --git a/src/game/client/components/flow.cpp b/src/game/client/components/flow.cpp index 9ecd4b5c..d2ba704c 100644 --- a/src/game/client/components/flow.cpp +++ b/src/game/client/components/flow.cpp @@ -1,85 +1,94 @@ -#include <engine/client/graphics.h> -#include <game/mapitems.hpp> -#include <game/layers.hpp> -#include "flow.hpp" +#include <engine/graphics.h> +#include <game/mapitems.h> +#include <game/layers.h> +#include "flow.h" -FLOW::FLOW() +CFlow::CFlow() { - cells = 0; - height = 0; - width = 0; - spacing = 16; + m_pCells = 0; + m_Height = 0; + m_Width = 0; + m_Spacing = 16; } -void FLOW::dbg_render() +void CFlow::DbgRender() { - if(!cells) + if(!m_pCells) return; + IGraphics::CLineItem Array[1024]; + int NumItems = 0; Graphics()->TextureSet(-1); Graphics()->LinesBegin(); - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) { - vec2 pos(x*spacing, y*spacing); - vec2 vel = cells[y*width+x].vel * 0.01f; - Graphics()->LinesDraw(pos.x, pos.y, pos.x+vel.x, pos.y+vel.y); + vec2 Pos(x*m_Spacing, y*m_Spacing); + vec2 Vel = m_pCells[y*m_Width+x].m_Vel * 0.01f; + Array[NumItems++] = IGraphics::CLineItem(Pos.x, Pos.y, Pos.x+Vel.x, Pos.y+Vel.y); + if(NumItems == 1024) + { + Graphics()->LinesDraw(Array, 1024); + NumItems = 0; + } } - + + if(NumItems) + Graphics()->LinesDraw(Array, NumItems); Graphics()->LinesEnd(); } -void FLOW::init() +void CFlow::Init() { - if(cells) + if(m_pCells) { - mem_free(cells); - cells = 0; + mem_free(m_pCells); + m_pCells = 0; } - MAPITEM_LAYER_TILEMAP *tilemap = layers_game_layer(); - width = tilemap->width*32/spacing; - height = tilemap->height*32/spacing; + CMapItemLayerTilemap *pTilemap = Layers()->GameLayer(); + m_Width = pTilemap->m_Width*32/m_Spacing; + m_Height = pTilemap->m_Height*32/m_Spacing; // allocate and clear - cells = (CELL *)mem_alloc(sizeof(CELL)*width*height, 1); - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - cells[y*width+x].vel = vec2(0.0f, 0.0f); + m_pCells = (CCell *)mem_alloc(sizeof(CCell)*m_Width*m_Height, 1); + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) + m_pCells[y*m_Width+x].m_Vel = vec2(0.0f, 0.0f); } -void FLOW::update() +void CFlow::Update() { - if(!cells) + if(!m_pCells) return; - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - cells[y*width+x].vel *= 0.85f; + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) + m_pCells[y*m_Width+x].m_Vel *= 0.85f; } -vec2 FLOW::get(vec2 pos) +vec2 CFlow::Get(vec2 Pos) { - if(!cells) + if(!m_pCells) return vec2(0,0); - int x = (int)(pos.x / spacing); - int y = (int)(pos.y / spacing); - if(x < 0 || y < 0 || x >= width || y >= height) + int x = (int)(Pos.x / m_Spacing); + int y = (int)(Pos.y / m_Spacing); + if(x < 0 || y < 0 || x >= m_Width || y >= m_Height) return vec2(0,0); - return cells[y*width+x].vel; + return m_pCells[y*m_Width+x].m_Vel; } -void FLOW::add(vec2 pos, vec2 vel, float size) +void CFlow::Add(vec2 Pos, vec2 Vel, float Size) { - if(!cells) + if(!m_pCells) return; - int x = (int)(pos.x / spacing); - int y = (int)(pos.y / spacing); - if(x < 0 || y < 0 || x >= width || y >= height) + int x = (int)(Pos.x / m_Spacing); + int y = (int)(Pos.y / m_Spacing); + if(x < 0 || y < 0 || x >= m_Width || y >= m_Height) return; - cells[y*width+x].vel += vel; + m_pCells[y*m_Width+x].m_Vel += Vel; } diff --git a/src/game/client/components/flow.h b/src/game/client/components/flow.h new file mode 100644 index 00000000..e8134797 --- /dev/null +++ b/src/game/client/components/flow.h @@ -0,0 +1,28 @@ +#ifndef GAME_CLIENT_COMPONENTS_FLOW_H +#define GAME_CLIENT_COMPONENTS_FLOW_H +#include <base/vmath.h> +#include <game/client/component.h> + +class CFlow : public CComponent +{ + struct CCell + { + vec2 m_Vel; + }; + + CCell *m_pCells; + int m_Height; + int m_Width; + int m_Spacing; + + void DbgRender(); + void Init(); +public: + CFlow(); + + vec2 Get(vec2 Pos); + void Add(vec2 Pos, vec2 Vel, float Size); + void Update(); +}; + +#endif diff --git a/src/game/client/components/flow.hpp b/src/game/client/components/flow.hpp deleted file mode 100644 index 351b1f69..00000000 --- a/src/game/client/components/flow.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#include <base/vmath.hpp> -#include <game/client/component.hpp> - -class FLOW : public COMPONENT -{ - struct CELL - { - vec2 vel; - }; - - CELL *cells; - int height; - int width; - int spacing; - - void dbg_render(); - void init(); -public: - FLOW(); - - vec2 get(vec2 pos); - void add(vec2 pos, vec2 vel, float size); - void update(); -}; - diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 837322fb..f4a24384 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -1,32 +1,31 @@ -#include <memory.h> // memcmp - -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> - -#include <game/layers.hpp> - -#include <game/client/gameclient.hpp> -#include <game/client/animstate.hpp> -#include <game/client/render.hpp> - -#include "controls.hpp" -#include "camera.hpp" -#include "hud.hpp" -#include "voting.hpp" -#include "binds.hpp" - -HUD::HUD() +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/shared/config.h> + +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> +#include <game/layers.h> +#include <game/client/gameclient.h> +#include <game/client/animstate.h> +#include <game/client/render.h> + +#include "controls.h" +#include "camera.h" +#include "hud.h" +#include "voting.h" +#include "binds.h" + +CHud::CHud() { - + // won't work if zero + m_AverageFPS = 1.0f; } -void HUD::on_reset() +void CHud::OnReset() { } -void HUD::render_goals() +void CHud::RenderGoals() { // TODO: split this up into these: // render_gametimer @@ -34,41 +33,41 @@ void HUD::render_goals() // render_scorehud // render_warmuptimer - int gameflags = gameclient.snap.gameobj->flags; + int GameFlags = m_pClient->m_Snap.m_pGameobj->m_Flags; - float whole = 300*Graphics()->ScreenAspect(); - float half = whole/2.0f; + float Whole = 300*Graphics()->ScreenAspect(); + float Half = Whole/2.0f; Graphics()->MapScreen(0,0,300*Graphics()->ScreenAspect(),300); - if(!gameclient.snap.gameobj->sudden_death) + if(!m_pClient->m_Snap.m_pGameobj->m_SuddenDeath) { - char buf[32]; - int time = 0; - if(gameclient.snap.gameobj->time_limit) + char Buf[32]; + int Time = 0; + if(m_pClient->m_Snap.m_pGameobj->m_TimeLimit) { - time = gameclient.snap.gameobj->time_limit*60 - ((client_tick()-gameclient.snap.gameobj->round_start_tick)/client_tickspeed()); + Time = m_pClient->m_Snap.m_pGameobj->m_TimeLimit*60 - ((Client()->GameTick()-m_pClient->m_Snap.m_pGameobj->m_RoundStartTick)/Client()->GameTickSpeed()); - if(gameclient.snap.gameobj->game_over) - time = 0; + if(m_pClient->m_Snap.m_pGameobj->m_GameOver) + Time = 0; } else - time = (client_tick()-gameclient.snap.gameobj->round_start_tick)/client_tickspeed(); + Time = (Client()->GameTick()-m_pClient->m_Snap.m_pGameobj->m_RoundStartTick)/Client()->GameTickSpeed(); - str_format(buf, sizeof(buf), "%d:%02d", time /60, time %60); - float w = gfx_text_width(0, 16, buf, -1); - gfx_text(0, half-w/2, 2, 16, buf, -1); + str_format(Buf, sizeof(Buf), "%d:%02d", Time /60, Time %60); + float w = TextRender()->TextWidth(0, 16, Buf, -1); + TextRender()->Text(0, Half-w/2, 2, 16, Buf, -1); } - if(gameclient.snap.gameobj->sudden_death) + if(m_pClient->m_Snap.m_pGameobj->m_SuddenDeath) { - const char *text = "Sudden Death"; - float w = gfx_text_width(0, 16, text, -1); - gfx_text(0, half-w/2, 2, 16, text, -1); + const char *pText = "Sudden Death"; + float w = TextRender()->TextWidth(0, 16, pText, -1); + TextRender()->Text(0, Half-w/2, 2, 16, pText, -1); } // render small score hud - if(!(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over) && (gameflags&GAMEFLAG_TEAMS)) + if(!(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver) && (GameFlags&GAMEFLAG_TEAMS)) { for(int t = 0; t < 2; t++) { @@ -79,166 +78,171 @@ void HUD::render_goals() Graphics()->SetColor(1,0,0,0.25f); else Graphics()->SetColor(0,0,1,0.25f); - RenderTools()->draw_round_rect(whole-40, 300-40-15+t*20, 50, 18, 5.0f); + RenderTools()->DrawRoundRect(Whole-45, 300-40-15+t*20, 50, 18, 5.0f); Graphics()->QuadsEnd(); - char buf[32]; - str_format(buf, sizeof(buf), "%d", t?gameclient.snap.gameobj->teamscore_blue:gameclient.snap.gameobj->teamscore_red); - float w = gfx_text_width(0, 14, buf, -1); + char Buf[32]; + str_format(Buf, sizeof(Buf), "%d", t?m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue : m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed); + float w = TextRender()->TextWidth(0, 14, Buf, -1); - if(gameflags&GAMEFLAG_FLAGS) + if(GameFlags&GAMEFLAG_FLAGS) { - gfx_text(0, whole-20-w/2+5, 300-40-15+t*20, 14, buf, -1); - if(gameclient.snap.flags[t]) + TextRender()->Text(0, Whole-20-w/2+5, 300-40-15+t*20, 14, Buf, -1); + if(m_pClient->m_Snap.m_paFlags[t]) { - if(gameclient.snap.flags[t]->carried_by == -2 || (gameclient.snap.flags[t]->carried_by == -1 && ((client_tick()/10)&1))) + if(m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy == -2 || (m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy == -1 && ((Client()->GameTick()/10)&1))) { Graphics()->BlendNormal(); - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - if(t == 0) RenderTools()->select_sprite(SPRITE_FLAG_RED); - else RenderTools()->select_sprite(SPRITE_FLAG_BLUE); + if(t == 0) RenderTools()->SelectSprite(SPRITE_FLAG_RED); + else RenderTools()->SelectSprite(SPRITE_FLAG_BLUE); - float size = 16; - Graphics()->QuadsDrawTL(whole-40+5, 300-40-15+t*20+1, size/2, size); + float Size = 16; + IGraphics::CQuadItem QuadItem(Whole-40+2, 300-40-15+t*20+1, Size/2, Size); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } - else if(gameclient.snap.flags[t]->carried_by >= 0) + else if(m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy >= 0) { - int id = gameclient.snap.flags[t]->carried_by%MAX_CLIENTS; - const char *name = gameclient.clients[id].name; - float w = gfx_text_width(0, 10, name, -1); - gfx_text(0, whole-40-5-w, 300-40-15+t*20+2, 10, name, -1); - TEE_RENDER_INFO info = gameclient.clients[id].render_info; - info.size = 18.0f; + int Id = m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy%MAX_CLIENTS; + const char *pName = m_pClient->m_aClients[Id].m_aName; + float w = TextRender()->TextWidth(0, 10, pName, -1); + TextRender()->Text(0, Whole-40-7-w, 300-40-15+t*20+2, 10, pName, -1); + CTeeRenderInfo Info = m_pClient->m_aClients[Id].m_RenderInfo; + Info.m_Size = 18.0f; - RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), - vec2(whole-40+10, 300-40-15+9+t*20+1)); + RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), + vec2(Whole-40+5, 300-40-15+9+t*20+1)); } } } else - gfx_text(0, whole-20-w/2, 300-40-15+t*20, 14, buf, -1); + TextRender()->Text(0, Whole-20-w/2, 300-40-15+t*20, 14, Buf, -1); } } // render warmup timer - if(gameclient.snap.gameobj->warmup) + if(m_pClient->m_Snap.m_pGameobj->m_Warmup) { - char buf[256]; - float w = gfx_text_width(0, 24, "Warmup", -1); - gfx_text(0, 150*Graphics()->ScreenAspect()+-w/2, 50, 24, "Warmup", -1); + char Buf[256]; + float w = TextRender()->TextWidth(0, 24, "Warmup", -1); + TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 50, 24, "Warmup", -1); - int seconds = gameclient.snap.gameobj->warmup/SERVER_TICK_SPEED; - if(seconds < 5) - str_format(buf, sizeof(buf), "%d.%d", seconds, (gameclient.snap.gameobj->warmup*10/SERVER_TICK_SPEED)%10); + int Seconds = m_pClient->m_Snap.m_pGameobj->m_Warmup/SERVER_TICK_SPEED; + if(Seconds < 5) + str_format(Buf, sizeof(Buf), "%d.%d", Seconds, (m_pClient->m_Snap.m_pGameobj->m_Warmup*10/SERVER_TICK_SPEED)%10); else - str_format(buf, sizeof(buf), "%d", seconds); - w = gfx_text_width(0, 24, buf, -1); - gfx_text(0, 150*Graphics()->ScreenAspect()+-w/2, 75, 24, buf, -1); + str_format(Buf, sizeof(Buf), "%d", Seconds); + w = TextRender()->TextWidth(0, 24, Buf, -1); + TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 75, 24, Buf, -1); } } -void HUD::mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) +void CHud::MapscreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup) { - float points[4]; - RenderTools()->mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, - group->offset_x, group->offset_y, Graphics()->ScreenAspect(), 1.0f, points); - Graphics()->MapScreen(points[0], points[1], points[2], points[3]); + float Points[4]; + RenderTools()->MapscreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX/100.0f, pGroup->m_ParallaxY/100.0f, + pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), 1.0f, Points); + Graphics()->MapScreen(Points[0], Points[1], Points[2], Points[3]); } -void HUD::render_fps() +void CHud::RenderFps() { - if(config.cl_showfps) + if(g_Config.m_ClShowfps) { - char buf[512]; - str_format(buf, sizeof(buf), "%d", (int)(1.0f/client_frametime())); - gfx_text(0, width-10-gfx_text_width(0,12,buf,-1), 5, 12, buf, -1); + // calculate avg. fps + float FPS = 1.0f / Client()->FrameTime(); + m_AverageFPS = (m_AverageFPS*(1.0f-(1.0f/m_AverageFPS))) + (FPS*(1.0f/m_AverageFPS)); + char Buf[512]; + str_format(Buf, sizeof(Buf), "%d", (int)m_AverageFPS); + TextRender()->Text(0, m_Width-10-TextRender()->TextWidth(0,12,Buf,-1), 5, 12, Buf, -1); } } -void HUD::render_connectionwarning() +void CHud::RenderConnectionWarning() { - if(client_connection_problems()) + if(Client()->ConnectionProblems()) { - const char *text = "Connection Problems..."; - float w = gfx_text_width(0, 24, text, -1); - gfx_text(0, 150*Graphics()->ScreenAspect()-w/2, 50, 24, text, -1); + const char *pText = "Connection Problems..."; + float w = TextRender()->TextWidth(0, 24, pText, -1); + TextRender()->Text(0, 150*Graphics()->ScreenAspect()-w/2, 50, 24, pText, -1); } } -void HUD::render_teambalancewarning() +void CHud::RenderTeambalanceWarning() { // render prompt about team-balance - bool flash = time_get()/(time_freq()/2)%2 == 0; - if (gameclient.snap.gameobj && (gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) != 0) + bool Flash = time_get()/(time_freq()/2)%2 == 0; + if (m_pClient->m_Snap.m_pGameobj && (m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) != 0) { - if (config.cl_warning_teambalance && abs(gameclient.snap.team_size[0]-gameclient.snap.team_size[1]) >= 2) + int TeamDiff = m_pClient->m_Snap.m_aTeamSize[0]-m_pClient->m_Snap.m_aTeamSize[1]; + if (g_Config.m_ClWarningTeambalance && (TeamDiff >= 2 || TeamDiff <= -2)) { - const char *text = "Please balance teams!"; - if(flash) - gfx_text_color(1,1,0.5f,1); + const char *pText = "Please balance teams!"; + if(Flash) + TextRender()->TextColor(1,1,0.5f,1); else - gfx_text_color(0.7f,0.7f,0.2f,1.0f); - gfx_text(0x0, 5, 50, 6, text, -1); - gfx_text_color(1,1,1,1); + TextRender()->TextColor(0.7f,0.7f,0.2f,1.0f); + TextRender()->Text(0x0, 5, 50, 6, pText, -1); + TextRender()->TextColor(1,1,1,1); } } } -void HUD::render_voting() +void CHud::RenderVoting() { - if(!gameclient.voting->is_voting()) + if(!m_pClient->m_pVoting->IsVoting()) return; Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.40f); - RenderTools()->draw_round_rect(-10, 60-2, 100+10+4+5, 28, 5.0f); + RenderTools()->DrawRoundRect(-10, 60-2, 100+10+4+5, 28, 5.0f); Graphics()->QuadsEnd(); - gfx_text_color(1,1,1,1); + TextRender()->TextColor(1,1,1,1); - char buf[512]; - gfx_text(0x0, 5, 60, 6, gameclient.voting->vote_description(), -1); + char Buf[512]; + TextRender()->Text(0x0, 5, 60, 6, m_pClient->m_pVoting->VoteDescription(), -1); - str_format(buf, sizeof(buf), "%ds left", gameclient.voting->seconds_left()); - float tw = gfx_text_width(0x0, 6, buf, -1); - gfx_text(0x0, 5+100-tw, 60, 6, buf, -1); + str_format(Buf, sizeof(Buf), "%ds left", m_pClient->m_pVoting->SecondsLeft()); + float tw = TextRender()->TextWidth(0x0, 6, Buf, -1); + TextRender()->Text(0x0, 5+100-tw, 60, 6, Buf, -1); - CUIRect base = {5, 70, 100, 4}; - gameclient.voting->render_bars(base, false); + CUIRect Base = {5, 70, 100, 4}; + m_pClient->m_pVoting->RenderBars(Base, false); - const char *yes_key = gameclient.binds->get_key("vote yes"); - const char *no_key = gameclient.binds->get_key("vote no"); - str_format(buf, sizeof(buf), "%s - Vote Yes", yes_key); - base.y += base.h+1; - UI()->DoLabel(&base, buf, 6.0f, -1); - - str_format(buf, sizeof(buf), "Vote No - %s", no_key); - UI()->DoLabel(&base, buf, 6.0f, 1); + const char *pYesKey = m_pClient->m_pBinds->GetKey("vote yes"); + const char *pNoKey = m_pClient->m_pBinds->GetKey("vote no"); + str_format(Buf, sizeof(Buf), "%s - Vote Yes", pYesKey); + Base.y += Base.h+1; + UI()->DoLabel(&Base, Buf, 6.0f, -1); + + str_format(Buf, sizeof(Buf), "Vote No - %s", pNoKey); + UI()->DoLabel(&Base, Buf, 6.0f, 1); } -void HUD::render_cursor() +void CHud::RenderCursor() { - if(!gameclient.snap.local_character) + if(!m_pClient->m_Snap.m_pLocalCharacter) return; - mapscreen_to_group(gameclient.camera->center.x, gameclient.camera->center.y, layers_game_group()); - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + MapscreenToGroup(m_pClient->m_pCamera->m_Center.x, m_pClient->m_pCamera->m_Center.y, Layers()->GameGroup()); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); // render cursor - RenderTools()->select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_cursor); - float cursorsize = 64; - RenderTools()->draw_sprite(gameclient.controls->target_pos.x, gameclient.controls->target_pos.y, cursorsize); + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[m_pClient->m_Snap.m_pLocalCharacter->m_Weapon%NUM_WEAPONS].m_pSpriteCursor); + float CursorSize = 64; + RenderTools()->DrawSprite(m_pClient->m_pControls->m_TargetPos.x, m_pClient->m_pControls->m_TargetPos.y, CursorSize); Graphics()->QuadsEnd(); } -void HUD::render_healthandammo() +void CHud::RenderHealthAndAmmo() { //mapscreen_to_group(gacenter_x, center_y, layers_game_group()); @@ -248,61 +252,69 @@ void HUD::render_healthandammo() // render ammo count // render gui stuff - Graphics()->TextureSet(data->images[IMAGE_GAME].id); - Graphics()->MapScreen(0,0,width,300); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); + Graphics()->MapScreen(0,0,m_Width,300); Graphics()->QuadsBegin(); // if weaponstage is active, put a "glow" around the stage ammo - RenderTools()->select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_proj); - for (int i = 0; i < min(gameclient.snap.local_character->ammocount, 10); i++) - Graphics()->QuadsDrawTL(x+i*12,y+24,10,10); - + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[m_pClient->m_Snap.m_pLocalCharacter->m_Weapon%NUM_WEAPONS].m_pSpriteProj); + IGraphics::CQuadItem Array[10]; + int i; + for (i = 0; i < min(m_pClient->m_Snap.m_pLocalCharacter->m_AmmoCount, 10); i++) + Array[i] = IGraphics::CQuadItem(x+i*12,y+24,10,10); + Graphics()->QuadsDrawTL(Array, i); Graphics()->QuadsEnd(); Graphics()->QuadsBegin(); int h = 0; // render health - RenderTools()->select_sprite(SPRITE_HEALTH_FULL); - for(; h < gameclient.snap.local_character->health; h++) - Graphics()->QuadsDrawTL(x+h*12,y,10,10); + RenderTools()->SelectSprite(SPRITE_HEALTH_FULL); + for(; h < min(m_pClient->m_Snap.m_pLocalCharacter->m_Health, 10); h++) + Array[h] = IGraphics::CQuadItem(x+h*12,y,10,10); + Graphics()->QuadsDrawTL(Array, h); - RenderTools()->select_sprite(SPRITE_HEALTH_EMPTY); + i = 0; + RenderTools()->SelectSprite(SPRITE_HEALTH_EMPTY); for(; h < 10; h++) - Graphics()->QuadsDrawTL(x+h*12,y,10,10); + Array[i++] = IGraphics::CQuadItem(x+h*12,y,10,10); + Graphics()->QuadsDrawTL(Array, i); // render armor meter h = 0; - RenderTools()->select_sprite(SPRITE_ARMOR_FULL); - for(; h < gameclient.snap.local_character->armor; h++) - Graphics()->QuadsDrawTL(x+h*12,y+12,10,10); + RenderTools()->SelectSprite(SPRITE_ARMOR_FULL); + for(; h < min(m_pClient->m_Snap.m_pLocalCharacter->m_Armor, 10); h++) + Array[h] = IGraphics::CQuadItem(x+h*12,y+12,10,10); + Graphics()->QuadsDrawTL(Array, h); - RenderTools()->select_sprite(SPRITE_ARMOR_EMPTY); + i = 0; + RenderTools()->SelectSprite(SPRITE_ARMOR_EMPTY); for(; h < 10; h++) - Graphics()->QuadsDrawTL(x+h*12,y+12,10,10); + Array[i++] = IGraphics::CQuadItem(x+h*12,y+12,10,10); + Graphics()->QuadsDrawTL(Array, i); Graphics()->QuadsEnd(); } -void HUD::on_render() +void CHud::OnRender() { - if(!gameclient.snap.gameobj) + if(!m_pClient->m_Snap.m_pGameobj) return; - width = 300*Graphics()->ScreenAspect(); + m_Width = 300*Graphics()->ScreenAspect(); - bool spectate = false; - if(gameclient.snap.local_info && gameclient.snap.local_info->team == -1) - spectate = true; + bool Spectate = false; + if(m_pClient->m_Snap.m_pLocalInfo && m_pClient->m_Snap.m_pLocalInfo->m_Team == -1) + Spectate = true; - if(gameclient.snap.local_character && !spectate && !(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)) - render_healthandammo(); - - render_goals(); - render_fps(); - if(client_state() != CLIENTSTATE_DEMOPLAYBACK) - render_connectionwarning(); - render_teambalancewarning(); - render_voting(); - render_cursor(); + if(m_pClient->m_Snap.m_pLocalCharacter && !Spectate && !(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver)) + RenderHealthAndAmmo(); + + RenderGoals(); + RenderFps(); + if(Client()->State() != IClient::STATE_DEMOPLAYBACK) + RenderConnectionWarning(); + RenderTeambalanceWarning(); + RenderVoting(); + RenderCursor(); } diff --git a/src/game/client/components/hud.h b/src/game/client/components/hud.h new file mode 100644 index 00000000..43f0e3a8 --- /dev/null +++ b/src/game/client/components/hud.h @@ -0,0 +1,27 @@ +#ifndef GAME_CLIENT_COMPONENTS_HUD_H +#define GAME_CLIENT_COMPONENTS_HUD_H +#include <game/client/component.h> + +class CHud : public CComponent +{ + float m_Width; + float m_AverageFPS; + + void RenderCursor(); + + void RenderFps(); + void RenderConnectionWarning(); + void RenderTeambalanceWarning(); + void RenderVoting(); + void RenderHealthAndAmmo(); + void RenderGoals(); + + void MapscreenToGroup(float CenterX, float CenterY, struct CMapItemGroup *PGroup); +public: + CHud(); + + virtual void OnReset(); + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/hud.hpp b/src/game/client/components/hud.hpp deleted file mode 100644 index 92ff0122..00000000 --- a/src/game/client/components/hud.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <game/client/component.hpp> - -class HUD : public COMPONENT -{ - float width; - - void render_cursor(); - - void render_fps(); - void render_connectionwarning(); - void render_teambalancewarning(); - void render_voting(); - void render_healthandammo(); - void render_goals(); - - - void mapscreen_to_group(float center_x, float center_y, struct MAPITEM_GROUP *group); -public: - HUD(); - - virtual void on_reset(); - virtual void on_render(); -}; - diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp index 3c9e1b79..70479e53 100644 --- a/src/game/client/components/items.cpp +++ b/src/game/client/components/items.cpp @@ -1,94 +1,94 @@ -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <engine/graphics.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/gamecore.hpp> // get_angle -#include <game/client/gameclient.hpp> -#include <game/client/ui.hpp> -#include <game/client/render.hpp> +#include <game/gamecore.h> // get_angle +#include <game/client/gameclient.h> +#include <game/client/ui.h> +#include <game/client/render.h> -#include <game/client/components/flow.hpp> -#include <game/client/components/effects.hpp> +#include <game/client/components/flow.h> +#include <game/client/components/effects.h> -#include "items.hpp" +#include "items.h" -void ITEMS::render_projectile(const NETOBJ_PROJECTILE *current, int itemid) +void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemId) { // get positions - float curvature = 0; - float speed = 0; - if(current->type == WEAPON_GRENADE) + float Curvature = 0; + float Speed = 0; + if(pCurrent->m_Type == WEAPON_GRENADE) { - curvature = gameclient.tuning.grenade_curvature; - speed = gameclient.tuning.grenade_speed; + Curvature = m_pClient->m_Tuning.m_GrenadeCurvature; + Speed = m_pClient->m_Tuning.m_GrenadeSpeed; } - else if(current->type == WEAPON_SHOTGUN) + else if(pCurrent->m_Type == WEAPON_SHOTGUN) { - curvature = gameclient.tuning.shotgun_curvature; - speed = gameclient.tuning.shotgun_speed; + Curvature = m_pClient->m_Tuning.m_ShotgunCurvature; + Speed = m_pClient->m_Tuning.m_ShotgunSpeed; } - else if(current->type == WEAPON_GUN) + else if(pCurrent->m_Type == WEAPON_GUN) { - curvature = gameclient.tuning.gun_curvature; - speed = gameclient.tuning.gun_speed; + Curvature = m_pClient->m_Tuning.m_GunCurvature; + Speed = m_pClient->m_Tuning.m_GunSpeed; } - float ct = (client_prevtick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime(); - if(ct < 0) + float Ct = (Client()->PrevGameTick()-pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + if(Ct < 0) return; // projectile havn't been shot yet - vec2 startpos(current->x, current->y); - vec2 startvel(current->vx/100.0f, current->vy/100.0f); - vec2 pos = calc_pos(startpos, startvel, curvature, speed, ct); - vec2 prevpos = calc_pos(startpos, startvel, curvature, speed, ct-0.001f); + vec2 StartPos(pCurrent->m_X, pCurrent->m_Y); + vec2 StartVel(pCurrent->m_VelX/100.0f, pCurrent->m_VelY/100.0f); + vec2 Pos = CalcPos(StartPos, StartVel, Curvature, Speed, Ct); + vec2 PrevPos = CalcPos(StartPos, StartVel, Curvature, Speed, Ct-0.001f); - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - RenderTools()->select_sprite(data->weapons.id[clamp(current->type, 0, NUM_WEAPONS-1)].sprite_proj); - vec2 vel = pos-prevpos; - //vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[clamp(pCurrent->m_Type, 0, NUM_WEAPONS-1)].m_pSpriteProj); + vec2 Vel = Pos-PrevPos; + //vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), Client()->IntraGameTick()); // add particle for this projectile - if(current->type == WEAPON_GRENADE) + if(pCurrent->m_Type == WEAPON_GRENADE) { - gameclient.effects->smoketrail(pos, vel*-1); - gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f); - Graphics()->QuadsSetRotation(client_localtime()*pi*2*2 + itemid); + m_pClient->m_pEffects->SmokeTrail(Pos, Vel*-1); + m_pClient->m_pFlow->Add(Pos, Vel*1000*Client()->FrameTime(), 10.0f); + Graphics()->QuadsSetRotation(Client()->LocalTime()*pi*2*2 + ItemId); } else { - gameclient.effects->bullettrail(pos); - gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f); + m_pClient->m_pEffects->BulletTrail(Pos); + m_pClient->m_pFlow->Add(Pos, Vel*1000*Client()->FrameTime(), 10.0f); - if(length(vel) > 0.00001f) - Graphics()->QuadsSetRotation(get_angle(vel)); + if(length(Vel) > 0.00001f) + Graphics()->QuadsSetRotation(GetAngle(Vel)); else Graphics()->QuadsSetRotation(0); } - Graphics()->QuadsDraw(pos.x, pos.y, 32, 32); + IGraphics::CQuadItem QuadItem(Pos.x, Pos.y, 32, 32); + Graphics()->QuadsDraw(&QuadItem, 1); Graphics()->QuadsSetRotation(0); Graphics()->QuadsEnd(); } -void ITEMS::render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *current) +void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent) { - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); - float angle = 0.0f; - float size = 64.0f; - if (current->type == POWERUP_WEAPON) + vec2 Pos = mix(vec2(pPrev->m_X, pPrev->m_Y), vec2(pCurrent->m_X, pCurrent->m_Y), Client()->IntraGameTick()); + float Angle = 0.0f; + float Size = 64.0f; + if (pCurrent->m_Type == POWERUP_WEAPON) { - angle = 0; //-pi/6;//-0.25f * pi * 2.0f; - RenderTools()->select_sprite(data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].sprite_body); - size = data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].visual_size; + Angle = 0; //-pi/6;//-0.25f * pi * 2.0f; + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[clamp(pCurrent->m_Subtype, 0, NUM_WEAPONS-1)].m_pSpriteBody); + Size = g_pData->m_Weapons.m_aId[clamp(pCurrent->m_Subtype, 0, NUM_WEAPONS-1)].m_VisualSize; } else { @@ -98,69 +98,70 @@ void ITEMS::render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *curren SPRITE_PICKUP_WEAPON, SPRITE_PICKUP_NINJA }; - RenderTools()->select_sprite(c[current->type]); + RenderTools()->SelectSprite(c[pCurrent->m_Type]); - if(c[current->type] == SPRITE_PICKUP_NINJA) + if(c[pCurrent->m_Type] == SPRITE_PICKUP_NINJA) { - gameclient.effects->powerupshine(pos, vec2(96,18)); - size *= 2.0f; - pos.x += 10.0f; + m_pClient->m_pEffects->PowerupShine(Pos, vec2(96,18)); + Size *= 2.0f; + Pos.x += 10.0f; } } - Graphics()->QuadsSetRotation(angle); + Graphics()->QuadsSetRotation(Angle); - float offset = pos.y/32.0f + pos.x/32.0f; - pos.x += cosf(client_localtime()*2.0f+offset)*2.5f; - pos.y += sinf(client_localtime()*2.0f+offset)*2.5f; - RenderTools()->draw_sprite(pos.x, pos.y, size); + float Offset = Pos.y/32.0f + Pos.x/32.0f; + Pos.x += cosf(Client()->LocalTime()*2.0f+Offset)*2.5f; + Pos.y += sinf(Client()->LocalTime()*2.0f+Offset)*2.5f; + RenderTools()->DrawSprite(Pos.x, Pos.y, Size); Graphics()->QuadsEnd(); } -void ITEMS::render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current) +void CItems::RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent) { - float angle = 0.0f; - float size = 42.0f; + float Angle = 0.0f; + float Size = 42.0f; Graphics()->BlendNormal(); - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - if(current->team == 0) // red team - RenderTools()->select_sprite(SPRITE_FLAG_RED); + if(pCurrent->m_Team == 0) // red team + RenderTools()->SelectSprite(SPRITE_FLAG_RED); else - RenderTools()->select_sprite(SPRITE_FLAG_BLUE); + RenderTools()->SelectSprite(SPRITE_FLAG_BLUE); - Graphics()->QuadsSetRotation(angle); + Graphics()->QuadsSetRotation(Angle); - vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); + vec2 Pos = mix(vec2(pPrev->m_X, pPrev->m_Y), vec2(pCurrent->m_X, pCurrent->m_Y), Client()->IntraGameTick()); // make sure that the flag isn't interpolated between capture and return - if(prev->carried_by != current->carried_by) - pos = vec2(current->x, current->y); + if(pPrev->m_CarriedBy != pCurrent->m_CarriedBy) + Pos = vec2(pCurrent->m_X, pCurrent->m_Y); // make sure to use predicted position if we are the carrier - if(gameclient.snap.local_info && current->carried_by == gameclient.snap.local_info->cid) - pos = gameclient.local_character_pos; + if(m_pClient->m_Snap.m_pLocalInfo && pCurrent->m_CarriedBy == m_pClient->m_Snap.m_pLocalInfo->m_ClientId) + Pos = m_pClient->m_LocalCharacterPos; - Graphics()->QuadsDraw(pos.x, pos.y-size*0.75f, size, size*2); + IGraphics::CQuadItem QuadItem(Pos.x, Pos.y-Size*0.75f, Size, Size*2); + Graphics()->QuadsDraw(&QuadItem, 1); Graphics()->QuadsEnd(); } -void ITEMS::render_laser(const struct NETOBJ_LASER *current) +void CItems::RenderLaser(const struct CNetObj_Laser *pCurrent) { - vec2 pos = vec2(current->x, current->y); - vec2 from = vec2(current->from_x, current->from_y); - vec2 dir = normalize(pos-from); + vec2 Pos = vec2(pCurrent->m_X, pCurrent->m_Y); + vec2 From = vec2(pCurrent->m_FromX, pCurrent->m_FromY); + vec2 Dir = normalize(Pos-From); - float ticks = client_tick() + client_intratick() - current->start_tick; - float ms = (ticks/50.0f) * 1000.0f; - float a = ms / gameclient.tuning.laser_bounce_delay; + float Ticks = Client()->GameTick() + Client()->IntraGameTick() - pCurrent->m_StartTick; + float Ms = (Ticks/50.0f) * 1000.0f; + float a = Ms / m_pClient->m_Tuning.m_LaserBounceDelay; a = clamp(a, 0.0f, 1.0f); - float ia = 1-a; + float Ia = 1-a; - vec2 out, border; + vec2 Out, Border; Graphics()->BlendNormal(); Graphics()->TextureSet(-1); @@ -170,77 +171,87 @@ void ITEMS::render_laser(const struct NETOBJ_LASER *current) //vec4 outer_color(0.65f,0.85f,1.0f,1.0f); // do outline - vec4 outer_color(0.075f,0.075f,0.25f,1.0f); - Graphics()->SetColor(outer_color.r,outer_color.g,outer_color.b,1.0f); - out = vec2(dir.y, -dir.x) * (7.0f*ia); + vec4 OuterColor(0.075f, 0.075f, 0.25f, 1.0f); + Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f); + Out = vec2(Dir.y, -Dir.x) * (7.0f*Ia); - Graphics()->QuadsDrawFreeform( - from.x-out.x, from.y-out.y, - from.x+out.x, from.y+out.y, - pos.x-out.x, pos.y-out.y, - pos.x+out.x, pos.y+out.y - ); + IGraphics::CFreeformItem Freeform( + From.x-Out.x, From.y-Out.y, + From.x+Out.x, From.y+Out.y, + Pos.x-Out.x, Pos.y-Out.y, + Pos.x+Out.x, Pos.y+Out.y); + Graphics()->QuadsDrawFreeform(&Freeform, 1); // do inner - vec4 inner_color(0.5f,0.5f,1.0f,1.0f); - out = vec2(dir.y, -dir.x) * (5.0f*ia); - Graphics()->SetColor(inner_color.r, inner_color.g, inner_color.b, 1.0f); // center + vec4 InnerColor(0.5f, 0.5f, 1.0f, 1.0f); + Out = vec2(Dir.y, -Dir.x) * (5.0f*Ia); + Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f); // center - Graphics()->QuadsDrawFreeform( - from.x-out.x, from.y-out.y, - from.x+out.x, from.y+out.y, - pos.x-out.x, pos.y-out.y, - pos.x+out.x, pos.y+out.y - ); + Freeform = IGraphics::CFreeformItem( + From.x-Out.x, From.y-Out.y, + From.x+Out.x, From.y+Out.y, + Pos.x-Out.x, Pos.y-Out.y, + Pos.x+Out.x, Pos.y+Out.y); + Graphics()->QuadsDrawFreeform(&Freeform, 1); Graphics()->QuadsEnd(); // render head { Graphics()->BlendNormal(); - Graphics()->TextureSet(data->images[IMAGE_PARTICLES].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_PARTICLES].m_Id); Graphics()->QuadsBegin(); - int sprites[] = {SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03}; - RenderTools()->select_sprite(sprites[client_tick()%3]); - Graphics()->QuadsSetRotation(client_tick()); - Graphics()->SetColor(outer_color.r,outer_color.g,outer_color.b,1.0f); - Graphics()->QuadsDraw(pos.x, pos.y, 24,24); - Graphics()->SetColor(inner_color.r, inner_color.g, inner_color.b, 1.0f); - Graphics()->QuadsDraw(pos.x, pos.y, 20,20); + int Sprites[] = {SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03}; + RenderTools()->SelectSprite(Sprites[Client()->GameTick()%3]); + Graphics()->QuadsSetRotation(Client()->GameTick()); + Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f); + IGraphics::CQuadItem QuadItem(Pos.x, Pos.y, 24, 24); + Graphics()->QuadsDraw(&QuadItem, 1); + Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f); + QuadItem = IGraphics::CQuadItem(Pos.x, Pos.y, 20, 20); + Graphics()->QuadsDraw(&QuadItem, 1); Graphics()->QuadsEnd(); } Graphics()->BlendNormal(); } -void ITEMS::on_render() +void CItems::OnRender() { - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) + int Num = Client()->SnapNumItems(IClient::SNAP_CURRENT); + for(int i = 0; i < Num; i++) { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); - if(item.type == NETOBJTYPE_PROJECTILE) + if(Item.m_Type == NETOBJTYPE_PROJECTILE) { - render_projectile((const NETOBJ_PROJECTILE *)data, item.id); + RenderProjectile((const CNetObj_Projectile *)pData, Item.m_Id); } - else if(item.type == NETOBJTYPE_PICKUP) + else if(Item.m_Type == NETOBJTYPE_PICKUP) { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if(prev) - render_pickup((const NETOBJ_PICKUP *)prev, (const NETOBJ_PICKUP *)data); + const void *pPrev = Client()->SnapFindItem(IClient::SNAP_PREV, Item.m_Type, Item.m_Id); + if(pPrev) + RenderPickup((const CNetObj_Pickup *)pPrev, (const CNetObj_Pickup *)pData); } - else if(item.type == NETOBJTYPE_LASER) + else if(Item.m_Type == NETOBJTYPE_LASER) { - render_laser((const NETOBJ_LASER *)data); + RenderLaser((const CNetObj_Laser *)pData); } - else if(item.type == NETOBJTYPE_FLAG) + } + + // render flag + for(int i = 0; i < Num; i++) + { + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); + + if(Item.m_Type == NETOBJTYPE_FLAG) { - const void *prev = snap_find_item(SNAP_PREV, item.type, item.id); - if (prev) - render_flag((const NETOBJ_FLAG *)prev, (const NETOBJ_FLAG *)data); + const void *pPrev = Client()->SnapFindItem(IClient::SNAP_PREV, Item.m_Type, Item.m_Id); + if (pPrev) + RenderFlag((const CNetObj_Flag *)pPrev, (const CNetObj_Flag *)pData); } } @@ -248,7 +259,7 @@ void ITEMS::on_render() /* for(int i = 0; i < extraproj_num; i++) { - if(extraproj_projectiles[i].start_tick < client_tick()) + if(extraproj_projectiles[i].start_tick < Client()->GameTick()) { extraproj_projectiles[i] = extraproj_projectiles[extraproj_num-1]; extraproj_num--; diff --git a/src/game/client/components/items.h b/src/game/client/components/items.h new file mode 100644 index 00000000..e4525546 --- /dev/null +++ b/src/game/client/components/items.h @@ -0,0 +1,16 @@ +#ifndef GAME_CLIENT_COMPONENTS_ITEMS_H +#define GAME_CLIENT_COMPONENTS_ITEMS_H +#include <game/client/component.h> + +class CItems : public CComponent +{ + void RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemId); + void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent); + void RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent); + void RenderLaser(const struct CNetObj_Laser *pCurrent); + +public: + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/items.hpp b/src/game/client/components/items.hpp deleted file mode 100644 index 2f33c8c4..00000000 --- a/src/game/client/components/items.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#include <game/client/component.hpp> - -class ITEMS : public COMPONENT -{ - void render_projectile(const NETOBJ_PROJECTILE *current, int itemid); - void render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *current); - void render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current); - void render_laser(const struct NETOBJ_LASER *current); - -public: - virtual void on_render(); -}; - diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index e6232b7d..d18dd965 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -1,127 +1,129 @@ -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/client/gameclient.hpp> -#include <game/client/animstate.hpp> -#include "killmessages.hpp" +#include <game/client/gameclient.h> +#include <game/client/animstate.h> +#include "killmessages.h" -void KILLMESSAGES::on_reset() +void CKillMessages::OnReset() { - killmsg_current = 0; - for(int i = 0; i < killmsg_max; i++) - killmsgs[i].tick = -100000; + m_KillmsgCurrent = 0; + for(int i = 0; i < MAX_KILLMSGS; i++) + m_aKillmsgs[i].m_Tick = -100000; } -void KILLMESSAGES::on_message(int msgtype, void *rawmsg) +void CKillMessages::OnMessage(int MsgType, void *pRawMsg) { - if(msgtype == NETMSGTYPE_SV_KILLMSG) + if(MsgType == NETMSGTYPE_SV_KILLMSG) { - NETMSG_SV_KILLMSG *msg = (NETMSG_SV_KILLMSG *)rawmsg; + CNetMsg_Sv_KillMsg *pMsg = (CNetMsg_Sv_KillMsg *)pRawMsg; // unpack messages - KILLMSG kill; - kill.killer = msg->killer; - kill.victim = msg->victim; - kill.weapon = msg->weapon; - kill.mode_special = msg->mode_special; - kill.tick = client_tick(); + CKillMsg Kill; + Kill.m_Killer = pMsg->m_Killer; + Kill.m_Victim = pMsg->m_Victim; + Kill.m_Weapon = pMsg->m_Weapon; + Kill.m_ModeSpecial = pMsg->m_ModeSpecial; + Kill.m_Tick = Client()->GameTick(); // add the message - killmsg_current = (killmsg_current+1)%killmsg_max; - killmsgs[killmsg_current] = kill; + m_KillmsgCurrent = (m_KillmsgCurrent+1)%MAX_KILLMSGS; + m_aKillmsgs[m_KillmsgCurrent] = Kill; } } -void KILLMESSAGES::on_render() +void CKillMessages::OnRender() { - float width = 400*3.0f*Graphics()->ScreenAspect(); - float height = 400*3.0f; + float Width = 400*3.0f*Graphics()->ScreenAspect(); + float Height = 400*3.0f; - Graphics()->MapScreen(0, 0, width*1.5f, height*1.5f); - float startx = width*1.5f-10.0f; + Graphics()->MapScreen(0, 0, Width*1.5f, Height*1.5f); + float StartX = Width*1.5f-10.0f; float y = 20.0f; - for(int i = 0; i < killmsg_max; i++) + for(int i = 0; i < MAX_KILLMSGS; i++) { - int r = (killmsg_current+i+1)%killmsg_max; - if(client_tick() > killmsgs[r].tick+50*10) + int r = (m_KillmsgCurrent+i+1)%MAX_KILLMSGS; + if(Client()->GameTick() > m_aKillmsgs[r].m_Tick+50*10) continue; - float font_size = 36.0f; - float killername_w = gfx_text_width(0, font_size, gameclient.clients[killmsgs[r].killer].name, -1); - float victimname_w = gfx_text_width(0, font_size, gameclient.clients[killmsgs[r].victim].name, -1); + float FontSize = 36.0f; + float KillerNameW = TextRender()->TextWidth(0, FontSize, m_pClient->m_aClients[m_aKillmsgs[r].m_Killer].m_aName, -1); + float VictimNameW = TextRender()->TextWidth(0, FontSize, m_pClient->m_aClients[m_aKillmsgs[r].m_Victim].m_aName, -1); - float x = startx; + float x = StartX; // render victim name - x -= victimname_w; - gfx_text(0, x, y, font_size, gameclient.clients[killmsgs[r].victim].name, -1); + x -= VictimNameW; + TextRender()->Text(0, x, y, FontSize, m_pClient->m_aClients[m_aKillmsgs[r].m_Victim].m_aName, -1); // render victim tee x -= 24.0f; - if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_FLAGS) + if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_FLAGS) { - if(killmsgs[r].mode_special&1) + if(m_aKillmsgs[r].m_ModeSpecial&1) { Graphics()->BlendNormal(); - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - if(gameclient.clients[killmsgs[r].victim].team == 0) RenderTools()->select_sprite(SPRITE_FLAG_BLUE); - else RenderTools()->select_sprite(SPRITE_FLAG_RED); + if(m_pClient->m_aClients[m_aKillmsgs[r].m_Victim].m_Team == 0) RenderTools()->SelectSprite(SPRITE_FLAG_BLUE); + else RenderTools()->SelectSprite(SPRITE_FLAG_RED); - float size = 56.0f; - Graphics()->QuadsDrawTL(x, y-16, size/2, size); + float Size = 56.0f; + IGraphics::CQuadItem QuadItem(x, y-16, Size/2, Size); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } } - RenderTools()->RenderTee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].victim].render_info, EMOTE_PAIN, vec2(-1,0), vec2(x, y+28)); + RenderTools()->RenderTee(CAnimState::GetIdle(), &m_pClient->m_aClients[m_aKillmsgs[r].m_Victim].m_RenderInfo, EMOTE_PAIN, vec2(-1,0), vec2(x, y+28)); x -= 32.0f; // render weapon x -= 44.0f; - if (killmsgs[r].weapon >= 0) + if (m_aKillmsgs[r].m_Weapon >= 0) { - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - RenderTools()->select_sprite(data->weapons.id[killmsgs[r].weapon].sprite_body); - RenderTools()->draw_sprite(x, y+28, 96); + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[m_aKillmsgs[r].m_Weapon].m_pSpriteBody); + RenderTools()->DrawSprite(x, y+28, 96); Graphics()->QuadsEnd(); } x -= 52.0f; - if(killmsgs[r].victim != killmsgs[r].killer) + if(m_aKillmsgs[r].m_Victim != m_aKillmsgs[r].m_Killer) { - if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_FLAGS) + if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_FLAGS) { - if(killmsgs[r].mode_special&2) + if(m_aKillmsgs[r].m_ModeSpecial&2) { Graphics()->BlendNormal(); - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - if(gameclient.clients[killmsgs[r].killer].team == 0) RenderTools()->select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); - else RenderTools()->select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + if(m_pClient->m_aClients[m_aKillmsgs[r].m_Killer].m_Team == 0) RenderTools()->SelectSprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); + else RenderTools()->SelectSprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); - float size = 56.0f; - Graphics()->QuadsDrawTL(x-56, y-16, size/2, size); + float Size = 56.0f; + IGraphics::CQuadItem QuadItem(x-56, y-16, Size/2, Size); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } } // render killer tee x -= 24.0f; - RenderTools()->RenderTee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].killer].render_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); + RenderTools()->RenderTee(CAnimState::GetIdle(), &m_pClient->m_aClients[m_aKillmsgs[r].m_Killer].m_RenderInfo, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28)); x -= 32.0f; // render killer name - x -= killername_w; - gfx_text(0, x, y, font_size, gameclient.clients[killmsgs[r].killer].name, -1); + x -= KillerNameW; + TextRender()->Text(0, x, y, FontSize, m_pClient->m_aClients[m_aKillmsgs[r].m_Killer].m_aName, -1); } y += 44; diff --git a/src/game/client/components/killmessages.h b/src/game/client/components/killmessages.h new file mode 100644 index 00000000..720b10ae --- /dev/null +++ b/src/game/client/components/killmessages.h @@ -0,0 +1,31 @@ +#ifndef GAME_CLIENT_COMPONENTS_KILLMESSAGES_H +#define GAME_CLIENT_COMPONENTS_KILLMESSAGES_H +#include <game/client/component.h> + +class CKillMessages : public CComponent +{ +public: + // kill messages + struct CKillMsg + { + int m_Weapon; + int m_Victim; + int m_Killer; + int m_ModeSpecial; // for CTF, if the guy is carrying a flag for example + int m_Tick; + }; + + enum + { + MAX_KILLMSGS = 5, + }; + + CKillMsg m_aKillmsgs[MAX_KILLMSGS]; + int m_KillmsgCurrent; + + virtual void OnReset(); + virtual void OnRender(); + virtual void OnMessage(int MsgType, void *pRawMsg); +}; + +#endif diff --git a/src/game/client/components/killmessages.hpp b/src/game/client/components/killmessages.hpp deleted file mode 100644 index f29e0bdf..00000000 --- a/src/game/client/components/killmessages.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <game/client/component.hpp> - -class KILLMESSAGES : public COMPONENT -{ -public: - // kill messages - struct KILLMSG - { - int weapon; - int victim; - int killer; - int mode_special; // for CTF, if the guy is carrying a flag for example - int tick; - }; - - static const int killmsg_max = 5; - KILLMSG killmsgs[killmsg_max]; - int killmsg_current; - - virtual void on_reset(); - virtual void on_render(); - virtual void on_message(int msgtype, void *rawmsg); -}; - diff --git a/src/game/client/components/mapimages.cpp b/src/game/client/components/mapimages.cpp index 51194853..9be450d1 100644 --- a/src/game/client/components/mapimages.cpp +++ b/src/game/client/components/mapimages.cpp @@ -1,45 +1,48 @@ -#include <engine/client/graphics.h> -#include <game/client/component.hpp> -#include <game/mapitems.hpp> +#include <engine/graphics.h> +#include <engine/map.h> +#include <game/client/component.h> +#include <game/mapitems.h> -#include "mapimages.hpp" +#include "mapimages.h" -MAPIMAGES::MAPIMAGES() +CMapImages::CMapImages() { - count = 0; + m_Count = 0; } -void MAPIMAGES::on_mapload() +void CMapImages::OnMapLoad() { + IMap *pMap = Kernel()->RequestInterface<IMap>(); + // unload all textures - for(int i = 0; i < count; i++) + for(int i = 0; i < m_Count; i++) { - Graphics()->UnloadTexture(textures[i]); - textures[i] = -1; + Graphics()->UnloadTexture(m_aTextures[i]); + m_aTextures[i] = -1; } - count = 0; + m_Count = 0; - int start; - map_get_type(MAPITEMTYPE_IMAGE, &start, &count); + int Start; + pMap->GetType(MAPITEMTYPE_IMAGE, &Start, &m_Count); // load new textures - for(int i = 0; i < count; i++) + for(int i = 0; i < m_Count; i++) { - textures[i] = 0; + m_aTextures[i] = 0; - MAPITEM_IMAGE *img = (MAPITEM_IMAGE *)map_get_item(start+i, 0, 0); - if(img->external) + CMapItemImage *pImg = (CMapItemImage *)pMap->GetItem(Start+i, 0, 0); + if(pImg->m_External) { - char buf[256]; - char *name = (char *)map_get_data(img->image_name); - str_format(buf, sizeof(buf), "mapres/%s.png", name); - textures[i] = Graphics()->LoadTexture(buf, IMG_AUTO, 0); + char Buf[256]; + char *pName = (char *)pMap->GetData(pImg->m_ImageName); + str_format(Buf, sizeof(Buf), "mapres/%s.png", pName); + m_aTextures[i] = Graphics()->LoadTexture(Buf, CImageInfo::FORMAT_AUTO, 0); } else { - void *data = map_get_data(img->image_data); - textures[i] = Graphics()->LoadTextureRaw(img->width, img->height, IMG_RGBA, data, IMG_RGBA, 0); - map_unload_data(img->image_data); + void *pData = pMap->GetData(pImg->m_ImageData); + m_aTextures[i] = Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, CImageInfo::FORMAT_RGBA, pData, CImageInfo::FORMAT_RGBA, 0); + pMap->UnloadData(pImg->m_ImageData); } } } diff --git a/src/game/client/components/mapimages.h b/src/game/client/components/mapimages.h new file mode 100644 index 00000000..2ef5fc32 --- /dev/null +++ b/src/game/client/components/mapimages.h @@ -0,0 +1,18 @@ +#ifndef GAME_CLIENT_COMPONENTS_MAPIMAGES_H +#define GAME_CLIENT_COMPONENTS_MAPIMAGES_H +#include <game/client/component.h> + +class CMapImages : public CComponent +{ + int m_aTextures[64]; + int m_Count; +public: + CMapImages(); + + int Get(int Index) const { return m_aTextures[Index]; } + int Num() const { return m_Count; } + + virtual void OnMapLoad(); +}; + +#endif diff --git a/src/game/client/components/mapimages.hpp b/src/game/client/components/mapimages.hpp deleted file mode 100644 index cba46033..00000000 --- a/src/game/client/components/mapimages.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#include <game/client/component.hpp> - -class MAPIMAGES : public COMPONENT -{ - int textures[64]; - int count; -public: - MAPIMAGES(); - - int get(int index) const { return textures[index]; } - int num() const { return count; } - - virtual void on_mapload(); -}; - diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp index 75f91521..202ea2da 100644 --- a/src/game/client/components/maplayers.cpp +++ b/src/game/client/components/maplayers.cpp @@ -1,167 +1,192 @@ -#include <engine/client/graphics.h> +#include <stdio.h> +#include <engine/graphics.h> +#include <engine/keys.h> //temp +#include <engine/shared/config.h> -#include <game/layers.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/component.hpp> -#include <game/client/render.hpp> +#include <game/layers.h> +#include <game/client/gameclient.h> +#include <game/client/component.h> +#include <game/client/render.h> -#include <game/client/components/camera.hpp> -#include <game/client/components/mapimages.hpp> +#include <game/client/components/camera.h> +#include <game/client/components/mapimages.h> -#include "maplayers.hpp" +#include "maplayers.h" -MAPLAYERS::MAPLAYERS(int t) +CMapLayers::CMapLayers(int t) { - type = t; + m_Type = t; + m_pLayers = 0; } +void CMapLayers::OnInit() +{ + m_pLayers = Layers(); +} -void MAPLAYERS::mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group) + +void CMapLayers::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup) { - float points[4]; - RenderTools()->mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f, - group->offset_x, group->offset_y, Graphics()->ScreenAspect(), 1.0f, points); - Graphics()->MapScreen(points[0], points[1], points[2], points[3]); + float Points[4]; + RenderTools()->MapscreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX/100.0f, pGroup->m_ParallaxY/100.0f, + pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), 1.0f, Points); + Graphics()->MapScreen(Points[0], Points[1], Points[2], Points[3]); } -void MAPLAYERS::envelope_eval(float time_offset, int env, float *channels, void *user) +void CMapLayers::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser) { - MAPLAYERS *pThis = (MAPLAYERS *)user; - channels[0] = 0; - channels[1] = 0; - channels[2] = 0; - channels[3] = 0; + CMapLayers *pThis = (CMapLayers *)pUser; + pChannels[0] = 0; + pChannels[1] = 0; + pChannels[2] = 0; + pChannels[3] = 0; - ENVPOINT *points; + CEnvPoint *pPoints; { - int start, num; - map_get_type(MAPITEMTYPE_ENVPOINTS, &start, &num); - if(num) - points = (ENVPOINT *)map_get_item(start, 0, 0); + int Start, Num; + pThis->m_pLayers->Map()->GetType(MAPITEMTYPE_ENVPOINTS, &Start, &Num); + if(Num) + pPoints = (CEnvPoint *)pThis->m_pLayers->Map()->GetItem(Start, 0, 0); } - int start, num; - map_get_type(MAPITEMTYPE_ENVELOPE, &start, &num); + int Start, Num; + pThis->m_pLayers->Map()->GetType(MAPITEMTYPE_ENVELOPE, &Start, &Num); - if(env >= num) + if(Env >= Num) return; - MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)map_get_item(start+env, 0, 0); - pThis->RenderTools()->render_eval_envelope(points+item->start_point, item->num_points, 4, client_localtime()+time_offset, channels); + CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(Start+Env, 0, 0); + pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, pThis->Client()->LocalTime()+TimeOffset, pChannels); } -void MAPLAYERS::on_render() +void CMapLayers::OnRender() { - if(client_state() != CLIENTSTATE_ONLINE && client_state() != CLIENTSTATE_DEMOPLAYBACK) + if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK) return; - CUIRect screen; - Graphics()->GetScreen(&screen.x, &screen.y, &screen.w, &screen.h); + CUIRect Screen; + Graphics()->GetScreen(&Screen.x, &Screen.y, &Screen.w, &Screen.h); - vec2 center = gameclient.camera->center; + vec2 Center = m_pClient->m_pCamera->m_Center; //float center_x = gameclient.camera->center.x; //float center_y = gameclient.camera->center.y; - bool passed_gamelayer = false; + bool PassedGameLayer = false; - for(int g = 0; g < layers_num_groups(); g++) + for(int g = 0; g < m_pLayers->NumGroups(); g++) { - MAPITEM_GROUP *group = layers_get_group(g); + CMapItemGroup *pGroup = m_pLayers->GetGroup(g); - if(!config.gfx_noclip && group->version >= 2 && group->use_clipping) + if(!g_Config.m_GfxNoclip && pGroup->m_Version >= 2 && pGroup->m_UseClipping) { // set clipping - float points[4]; - mapscreen_to_group(center.x, center.y, layers_game_group()); - Graphics()->GetScreen(&points[0], &points[1], &points[2], &points[3]); - float x0 = (group->clip_x - points[0]) / (points[2]-points[0]); - float y0 = (group->clip_y - points[1]) / (points[3]-points[1]); - float x1 = ((group->clip_x+group->clip_w) - points[0]) / (points[2]-points[0]); - float y1 = ((group->clip_y+group->clip_h) - points[1]) / (points[3]-points[1]); + float Points[4]; + MapScreenToGroup(Center.x, Center.y, m_pLayers->GameGroup()); + Graphics()->GetScreen(&Points[0], &Points[1], &Points[2], &Points[3]); + float x0 = (pGroup->m_ClipX - Points[0]) / (Points[2]-Points[0]); + float y0 = (pGroup->m_ClipY - Points[1]) / (Points[3]-Points[1]); + float x1 = ((pGroup->m_ClipX+pGroup->m_ClipW) - Points[0]) / (Points[2]-Points[0]); + float y1 = ((pGroup->m_ClipY+pGroup->m_ClipH) - Points[1]) / (Points[3]-Points[1]); Graphics()->ClipEnable((int)(x0*Graphics()->ScreenWidth()), (int)(y0*Graphics()->ScreenHeight()), (int)((x1-x0)*Graphics()->ScreenWidth()), (int)((y1-y0)*Graphics()->ScreenHeight())); } - mapscreen_to_group(center.x, center.y, group); + MapScreenToGroup(Center.x, Center.y, pGroup); - for(int l = 0; l < group->num_layers; l++) + for(int l = 0; l < pGroup->m_NumLayers; l++) { - MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l); - bool render = false; - bool is_game_layer = false; + CMapItemLayer *pLayer = m_pLayers->GetLayer(pGroup->m_StartLayer+l); + bool Render = false; + bool IsGameLayer = false; - if(layer == (MAPITEM_LAYER*)layers_game_layer()) + if(pLayer == (CMapItemLayer*)m_pLayers->GameLayer()) { - is_game_layer = true; - passed_gamelayer = 1; + IsGameLayer = true; + PassedGameLayer = 1; } // skip rendering if detail layers if not wanted - if(layer->flags&LAYERFLAG_DETAIL && !config.gfx_high_detail && !is_game_layer) + if(pLayer->m_Flags&LAYERFLAG_DETAIL && !g_Config.m_GfxHighDetail && !IsGameLayer) continue; - if(type == -1) - render = true; - else if(type == 0) + if(m_Type == -1) + Render = true; + else if(m_Type == 0) { - if(passed_gamelayer) + if(PassedGameLayer) return; - render = true; + Render = true; } else { - if(passed_gamelayer && !is_game_layer) - render = true; + if(PassedGameLayer && !IsGameLayer) + Render = true; } - if(render && !is_game_layer) + if(pLayer->m_Type == LAYERTYPE_TILES && Input()->KeyPressed(KEY_KP0)) + { + CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; + CTile *pTiles = (CTile *)m_pLayers->Map()->GetData(pTMap->m_Data); + char buf[256]; + str_format(buf, sizeof(buf), "%d%d_%dx%d", g, l, pTMap->m_Width, pTMap->m_Height); + FILE *f = fopen(buf, "w"); + for(int y = 0; y < pTMap->m_Height; y++) + { + for(int x = 0; x < pTMap->m_Width; x++) + fprintf(f, "%d,", pTiles[y*pTMap->m_Width + x].m_Index); + fprintf(f, "\n"); + } + fclose(f); + } + + if(Render && !IsGameLayer) { //layershot_begin(); - if(layer->type == LAYERTYPE_TILES) + if(pLayer->m_Type == LAYERTYPE_TILES) { - MAPITEM_LAYER_TILEMAP *tmap = (MAPITEM_LAYER_TILEMAP *)layer; - if(tmap->image == -1) + CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer; + if(pTMap->m_Image == -1) Graphics()->TextureSet(-1); else - Graphics()->TextureSet(gameclient.mapimages->get(tmap->image)); + Graphics()->TextureSet(m_pClient->m_pMapimages->Get(pTMap->m_Image)); - TILE *tiles = (TILE *)map_get_data(tmap->data); + CTile *pTiles = (CTile *)m_pLayers->Map()->GetData(pTMap->m_Data); Graphics()->BlendNone(); - RenderTools()->render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); + RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE); Graphics()->BlendNormal(); - RenderTools()->render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); + RenderTools()->RenderTilemap(pTiles, pTMap->m_Width, pTMap->m_Height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT); } - else if(layer->type == LAYERTYPE_QUADS) + else if(pLayer->m_Type == LAYERTYPE_QUADS) { - MAPITEM_LAYER_QUADS *qlayer = (MAPITEM_LAYER_QUADS *)layer; - if(qlayer->image == -1) + CMapItemLayerQuads *pQLayer = (CMapItemLayerQuads *)pLayer; + if(pQLayer->m_Image == -1) Graphics()->TextureSet(-1); else - Graphics()->TextureSet(gameclient.mapimages->get(qlayer->image)); + Graphics()->TextureSet(m_pClient->m_pMapimages->Get(pQLayer->m_Image)); - QUAD *quads = (QUAD *)map_get_data_swapped(qlayer->data); + CQuad *pQuads = (CQuad *)m_pLayers->Map()->GetDataSwapped(pQLayer->m_Data); Graphics()->BlendNone(); - RenderTools()->render_quads(quads, qlayer->num_quads, LAYERRENDERFLAG_OPAQUE, envelope_eval, this); + RenderTools()->RenderQuads(pQuads, pQLayer->m_NumQuads, LAYERRENDERFLAG_OPAQUE, EnvelopeEval, this); Graphics()->BlendNormal(); - RenderTools()->render_quads(quads, qlayer->num_quads, LAYERRENDERFLAG_TRANSPARENT, envelope_eval, this); + RenderTools()->RenderQuads(pQuads, pQLayer->m_NumQuads, LAYERRENDERFLAG_TRANSPARENT, EnvelopeEval, this); } //layershot_end(); } } - if(!config.gfx_noclip) + if(!g_Config.m_GfxNoclip) Graphics()->ClipDisable(); } - if(!config.gfx_noclip) + if(!g_Config.m_GfxNoclip) Graphics()->ClipDisable(); // reset the screen like it was before - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); } diff --git a/src/game/client/components/maplayers.h b/src/game/client/components/maplayers.h new file mode 100644 index 00000000..9f70d9cb --- /dev/null +++ b/src/game/client/components/maplayers.h @@ -0,0 +1,24 @@ +#ifndef GAME_CLIENT_COMPONENTS_MAPLAYERS_H +#define GAME_CLIENT_COMPONENTS_MAPLAYERS_H +#include <game/client/component.h> + +class CMapLayers : public CComponent +{ + CLayers *m_pLayers; // todo refactor: maybe remove it and access it through client* + int m_Type; + + void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup); + static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser); +public: + enum + { + TYPE_BACKGROUND=0, + TYPE_FOREGROUND, + }; + + CMapLayers(int Type); + virtual void OnInit(); + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/maplayers.hpp b/src/game/client/components/maplayers.hpp deleted file mode 100644 index c8b154b2..00000000 --- a/src/game/client/components/maplayers.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#include <game/client/component.hpp> - -class MAPLAYERS : public COMPONENT -{ - int type; - - void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group); - static void envelope_eval(float time_offset, int env, float *channels, void *user); -public: - enum - { - TYPE_BACKGROUND=0, - TYPE_FOREGROUND, - }; - - MAPLAYERS(int type); - virtual void on_render(); -}; - diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 5f1bbf42..76ee2e5e 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1,45 +1,45 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdio.h> +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <math.h> -#include <string.h> -#include <stdlib.h> #include <base/system.h> -#include <base/math.hpp> -#include <base/vmath.hpp> +#include <base/math.h> +#include <base/vmath.h> -#include "menus.hpp" -#include "skins.hpp" +#include "menus.h" +#include "skins.h" -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/serverbrowser.h> +#include <engine/keys.h> +#include <engine/shared/config.h> -#include <game/version.hpp> -#include <game/generated/g_protocol.hpp> +#include <game/version.h> +#include <game/generated/protocol.h> -#include <game/generated/gc_data.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/lineinput.hpp> -#include <game/localization.hpp> +#include <game/generated/client_data.h> +#include <game/client/gameclient.h> +#include <game/client/lineinput.h> +#include <game/localization.h> #include <mastersrv/mastersrv.h> -vec4 MENUS::gui_color; -vec4 MENUS::color_tabbar_inactive_outgame; -vec4 MENUS::color_tabbar_active_outgame; -vec4 MENUS::color_tabbar_inactive; -vec4 MENUS::color_tabbar_active; -vec4 MENUS::color_tabbar_inactive_ingame; -vec4 MENUS::color_tabbar_active_ingame; +vec4 CMenus::ms_GuiColor; +vec4 CMenus::ms_ColorTabbarInactiveOutgame; +vec4 CMenus::ms_ColorTabbarActiveOutgame; +vec4 CMenus::ms_ColorTabbarInactive; +vec4 CMenus::ms_ColorTabbarActive; +vec4 CMenus::ms_ColorTabbarInactiveIngame; +vec4 CMenus::ms_ColorTabbarActiveIngame; -float MENUS::button_height = 25.0f; -float MENUS::listheader_height = 17.0f; -float MENUS::fontmod_height = 0.8f; +float CMenus::ms_ButtonHeight = 25.0f; +float CMenus::ms_ListheaderHeight = 17.0f; +float CMenus::ms_FontmodHeight = 0.8f; -INPUT_EVENT MENUS::inputevents[MAX_INPUTEVENTS]; -int MENUS::num_inputevents; +IInput::CEvent CMenus::m_aInputEvents[MAX_INPUTEVENTS]; +int CMenus::m_NumInputEvents; -inline float hue_to_rgb(float v1, float v2, float h) +inline float HueToRgb(float v1, float v2, float h) { if(h < 0) h += 1; if(h > 1) h -= 1; @@ -49,53 +49,54 @@ inline float hue_to_rgb(float v1, float v2, float h) return v1; } -inline vec3 hsl_to_rgb(vec3 in) +inline vec3 HslToRgb(vec3 In) { float v1, v2; - vec3 out; + vec3 Out; - if(in.s == 0) + if(In.s == 0) { - out.r = in.l; - out.g = in.l; - out.b = in.l; + Out.r = In.l; + Out.g = In.l; + Out.b = In.l; } else { - if(in.l < 0.5f) - v2 = in.l * (1 + in.s); + if(In.l < 0.5f) + v2 = In.l * (1 + In.s); else - v2 = (in.l+in.s) - (in.s*in.l); + v2 = (In.l+In.s) - (In.s*In.l); - v1 = 2 * in.l - v2; + v1 = 2 * In.l - v2; - out.r = hue_to_rgb(v1, v2, in.h + (1.0f/3.0f)); - out.g = hue_to_rgb(v1, v2, in.h); - out.b = hue_to_rgb(v1, v2, in.h - (1.0f/3.0f)); + Out.r = HueToRgb(v1, v2, In.h + (1.0f/3.0f)); + Out.g = HueToRgb(v1, v2, In.h); + Out.b = HueToRgb(v1, v2, In.h - (1.0f/3.0f)); } - return out; + return Out; } -MENUS::MENUS() +CMenus::CMenus() { - popup = POPUP_NONE; - active_page = PAGE_INTERNET; - game_page = PAGE_GAME; + m_Popup = POPUP_NONE; + m_ActivePage = PAGE_INTERNET; + m_GamePage = PAGE_GAME; - need_restart = false; - need_sendinfo = false; - menu_active = true; + m_NeedRestart = false; + m_NeedSendinfo = false; + m_MenuActive = true; + m_UseMouseButtons = true; - escape_pressed = false; - enter_pressed = false; - num_inputevents = 0; + m_EscapePressed = false; + m_EnterPressed = false; + m_NumInputEvents = 0; - last_input = time_get(); + m_LastInput = time_get(); } -vec4 MENUS::button_color_mul(const void *pID) +vec4 CMenus::ButtonColorMul(const void *pID) { if(UI()->ActiveItem() == pID) return vec4(1,1,1,0.5f); @@ -104,69 +105,69 @@ vec4 MENUS::button_color_mul(const void *pID) return vec4(1,1,1,1); } -int MENUS::DoButton_BrowseIcon(int What, const CUIRect *pRect) +int CMenus::DoButton_BrowseIcon(int What, const CUIRect *pRect) { - Graphics()->TextureSet(data->images[IMAGE_BROWSEICONS].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_BROWSEICONS].m_Id); Graphics()->QuadsBegin(); - RenderTools()->select_sprite(What); - Graphics()->QuadsDrawTL(pRect->x, pRect->y, pRect->w, pRect->h); + RenderTools()->SelectSprite(What); + IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); return 0; } -int MENUS::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +int CMenus::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f)*button_color_mul(pID), CUI::CORNER_ALL, 5.0f); - UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); + RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f)*ButtonColorMul(pID), CUI::CORNER_ALL, 5.0f); + UI()->DoLabel(pRect, pText, pRect->h*ms_FontmodHeight, 0); return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -int MENUS::DoButton_KeySelect(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +void CMenus::DoButton_KeySelect(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f)*button_color_mul(pID), CUI::CORNER_ALL, 5.0f); - UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); - return UI()->DoButtonLogic(pID, pText, Checked, pRect); + RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f)*ButtonColorMul(pID), CUI::CORNER_ALL, 5.0f); + UI()->DoLabel(pRect, pText, pRect->h*ms_FontmodHeight, 0); } -int MENUS::DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners) +int CMenus::DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners) { vec4 ColorMod(1,1,1,1); if(Checked) - RenderTools()->DrawUIRect(pRect, color_tabbar_active, Corners, 10.0f); + RenderTools()->DrawUIRect(pRect, ms_ColorTabbarActive, Corners, 10.0f); else - RenderTools()->DrawUIRect(pRect, color_tabbar_inactive, Corners, 10.0f); - UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); + RenderTools()->DrawUIRect(pRect, ms_ColorTabbarInactive, Corners, 10.0f); + UI()->DoLabel(pRect, pText, pRect->h*ms_FontmodHeight, 0); return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -int MENUS::DoButton_SettingsTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +int CMenus::DoButton_SettingsTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { if(Checked) - RenderTools()->DrawUIRect(pRect, color_tabbar_active, CUI::CORNER_R, 10.0f); + RenderTools()->DrawUIRect(pRect, ms_ColorTabbarActive, CUI::CORNER_R, 10.0f); else - RenderTools()->DrawUIRect(pRect, color_tabbar_inactive, CUI::CORNER_R, 10.0f); - UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, 0); + RenderTools()->DrawUIRect(pRect, ms_ColorTabbarInactive, CUI::CORNER_R, 10.0f); + UI()->DoLabel(pRect, pText, pRect->h*ms_FontmodHeight, 0); return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -int MENUS::DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect) -//void MENUS::ui_draw_grid_header(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) +int CMenus::DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +//void CMenus::ui_draw_grid_header(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) { if(Checked) RenderTools()->DrawUIRect(pRect, vec4(1,1,1,0.5f), CUI::CORNER_T, 5.0f); CUIRect t; pRect->VSplitLeft(5.0f, 0, &t); - UI()->DoLabel(&t, pText, pRect->h*fontmod_height, -1); + UI()->DoLabel(&t, pText, pRect->h*ms_FontmodHeight, -1); return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -int MENUS::DoButton_ListRow(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +int CMenus::DoButton_ListRow(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { if(Checked) { @@ -174,12 +175,12 @@ int MENUS::DoButton_ListRow(const void *pID, const char *pText, int Checked, con sr.Margin(1.5f, &sr); RenderTools()->DrawUIRect(&sr, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f); } - UI()->DoLabel(pRect, pText, pRect->h*fontmod_height, -1); + UI()->DoLabel(pRect, pText, pRect->h*ms_FontmodHeight, -1); return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -int MENUS::DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect) -//void MENUS::ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const CUIRect *r, const void *extra) +int CMenus::DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect) +//void CMenus::ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const CUIRect *r, const void *extra) { CUIRect c = *pRect; CUIRect t = *pRect; @@ -189,57 +190,59 @@ int MENUS::DoButton_CheckBox_Common(const void *pID, const char *pText, const ch t.VSplitLeft(5.0f, 0, &t); c.Margin(2.0f, &c); - RenderTools()->DrawUIRect(&c, vec4(1,1,1,0.25f)*button_color_mul(pID), CUI::CORNER_ALL, 3.0f); + RenderTools()->DrawUIRect(&c, vec4(1,1,1,0.25f)*ButtonColorMul(pID), CUI::CORNER_ALL, 3.0f); c.y += 2; - UI()->DoLabel(&c, pBoxText, pRect->h*fontmod_height*0.6f, 0); - UI()->DoLabel(&t, pText, pRect->h*fontmod_height*0.8f, -1); + UI()->DoLabel(&c, pBoxText, pRect->h*ms_FontmodHeight*0.6f, 0); + UI()->DoLabel(&t, pText, pRect->h*ms_FontmodHeight*0.8f, -1); return UI()->DoButtonLogic(pID, pText, 0, pRect); } -int MENUS::DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +int CMenus::DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { return DoButton_CheckBox_Common(pID, pText, Checked?"X":"", pRect); } -int MENUS::DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +int CMenus::DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - char buf[16]; - str_format(buf, sizeof(buf), "%d", Checked); - return DoButton_CheckBox_Common(pID, pText, buf, pRect); + char aBuf[16]; + str_format(aBuf, sizeof(aBuf), "%d", Checked); + return DoButton_CheckBox_Common(pID, pText, aBuf, pRect); } -int MENUS::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) +int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden, int Corners) { int Inside = UI()->MouseInside(pRect); - int ReturnValue = 0; - static int AtIndex = 0; + bool ReturnValue = false; + static int s_AtIndex = 0; if(UI()->LastActiveItem() == pID) { - int Len = strlen(pStr); + int Len = str_length(pStr); + if(Len == 0) + s_AtIndex = 0; if(Inside && UI()->MouseButton(0)) { - int mx_rel = (int)(UI()->MouseX() - pRect->x); + int MxRel = (int)(UI()->MouseX() - pRect->x); - for (int i = 1; i <= Len; i++) + for(int i = 1; i <= Len; i++) { - if (gfx_text_width(0, FontSize, pStr, i) + 10 > mx_rel) + if(TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel) { - AtIndex = i - 1; + s_AtIndex = i - 1; break; } - if (i == Len) - AtIndex = Len; + if(i == Len) + s_AtIndex = Len; } } - for(int i = 0; i < num_inputevents; i++) + for(int i = 0; i < m_NumInputEvents; i++) { - Len = strlen(pStr); - LINEINPUT::manipulate(inputevents[i], pStr, StrSize, &Len, &AtIndex); + Len = str_length(pStr); + ReturnValue |= CLineInput::Manipulate(m_aInputEvents[i], pStr, StrSize, &Len, &s_AtIndex); } } @@ -264,35 +267,41 @@ int MENUS::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSi UI()->SetHotItem(pID); CUIRect Textbox = *pRect; - RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); - Textbox.VMargin(5.0f, &Textbox); + RenderTools()->DrawUIRect(&Textbox, vec4(1, 1, 1, 0.5f), Corners, 3.0f); + Textbox.VMargin(3.0f, &Textbox); const char *pDisplayStr = pStr; char aStars[128]; if(Hidden) { - unsigned s = strlen(pStr); + unsigned s = str_length(pStr); if(s >= sizeof(aStars)) s = sizeof(aStars)-1; - memset(aStars, '*', s); + for(unsigned int i = 0; i < s; ++i) + aStars[i] = '*'; aStars[s] = 0; pDisplayStr = aStars; } UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1); - if (UI()->LastActiveItem() == pID && !JustGotActive) + //TODO: make it blink + if(UI()->LastActiveItem() == pID && !JustGotActive) { - float w = gfx_text_width(0, FontSize, pDisplayStr, AtIndex); + float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); + Textbox = *pRect; + Textbox.VSplitLeft(2.0f, 0, &Textbox); Textbox.x += w*UI()->Scale(); - UI()->DoLabel(&Textbox, "_", FontSize, -1); + Textbox.y -= FontSize/10.f; + + UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1); } return ReturnValue; } -float MENUS::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) +float CMenus::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) { CUIRect Handle; static float OffsetY; @@ -300,7 +309,7 @@ float MENUS::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) Handle.y += (pRect->h-Handle.h)*Current; - /* logic */ + // logic float ReturnValue = Current; int Inside = UI()->MouseInside(&Handle); @@ -309,10 +318,10 @@ float MENUS::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) if(!UI()->MouseButton(0)) UI()->SetActiveItem(0); - float min = pRect->y; - float max = pRect->h-Handle.h; - float cur = UI()->MouseY()-OffsetY; - ReturnValue = (cur-min)/max; + float Min = pRect->y; + float Max = pRect->h-Handle.h; + float Cur = UI()->MouseY()-OffsetY; + ReturnValue = (Cur-Min)/Max; if(ReturnValue < 0.0f) ReturnValue = 0.0f; if(ReturnValue > 1.0f) ReturnValue = 1.0f; } @@ -341,14 +350,14 @@ float MENUS::DoScrollbarV(const void *pID, const CUIRect *pRect, float Current) Slider = Handle; Slider.Margin(5.0f, &Slider); - RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*button_color_mul(pID), CUI::CORNER_ALL, 2.5f); + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pID), CUI::CORNER_ALL, 2.5f); return ReturnValue; } -float MENUS::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) +float CMenus::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) { CUIRect Handle; static float OffsetX; @@ -356,7 +365,7 @@ float MENUS::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) Handle.x += (pRect->w-Handle.w)*Current; - /* logic */ + // logic float ReturnValue = Current; int Inside = UI()->MouseInside(&Handle); @@ -365,10 +374,10 @@ float MENUS::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) if(!UI()->MouseButton(0)) UI()->SetActiveItem(0); - float min = pRect->x; - float max = pRect->w-Handle.w; - float cur = UI()->MouseX()-OffsetX; - ReturnValue = (cur-min)/max; + float Min = pRect->x; + float Max = pRect->w-Handle.w; + float Cur = UI()->MouseX()-OffsetX; + ReturnValue = (Cur-Min)/Max; if(ReturnValue < 0.0f) ReturnValue = 0.0f; if(ReturnValue > 1.0f) ReturnValue = 1.0f; } @@ -397,12 +406,12 @@ float MENUS::DoScrollbarH(const void *pID, const CUIRect *pRect, float Current) Slider = Handle; Slider.Margin(5.0f, &Slider); - RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*button_color_mul(pID), CUI::CORNER_ALL, 2.5f); + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pID), CUI::CORNER_ALL, 2.5f); return ReturnValue; } -int MENUS::DoKeyReader(void *pID, const CUIRect *pRect, int Key) +int CMenus::DoKeyReader(void *pID, const CUIRect *pRect, int Key) { // process static void *pGrabbedID = 0; @@ -415,10 +424,10 @@ int MENUS::DoKeyReader(void *pID, const CUIRect *pRect, int Key) if(UI()->ActiveItem() == pID) { - if(binder.got_key) + if(m_Binder.m_GotKey) { - NewKey = binder.key.key; - binder.got_key = false; + NewKey = m_Binder.m_Key.m_Key; + m_Binder.m_GotKey = false; UI()->SetActiveItem(0); MouseReleased = false; pGrabbedID = pID; @@ -428,8 +437,8 @@ int MENUS::DoKeyReader(void *pID, const CUIRect *pRect, int Key) { if(UI()->MouseButton(0) && MouseReleased) { - binder.take_key = true; - binder.got_key = false; + m_Binder.m_TakeKey = true; + m_Binder.m_GotKey = false; UI()->SetActiveItem(pID); } } @@ -445,185 +454,185 @@ int MENUS::DoKeyReader(void *pID, const CUIRect *pRect, int Key) if(Key == 0) DoButton_KeySelect(pID, "", 0, pRect); else - DoButton_KeySelect(pID, inp_key_name(Key), 0, pRect); + DoButton_KeySelect(pID, Input()->KeyName(Key), 0, pRect); } return NewKey; } -int MENUS::render_menubar(CUIRect r) +int CMenus::RenderMenubar(CUIRect r) { - CUIRect box = r; - CUIRect button; + CUIRect Box = r; + CUIRect Button; - int active_page = config.ui_page; - int new_page = -1; + m_ActivePage = g_Config.m_UiPage; + int NewPage = -1; - if(client_state() != CLIENTSTATE_OFFLINE) - active_page = game_page; + if(Client()->State() != IClient::STATE_OFFLINE) + m_ActivePage = m_GamePage; - if(client_state() == CLIENTSTATE_OFFLINE) + if(Client()->State() == IClient::STATE_OFFLINE) { - /* offline menus */ + // offline menus if(0) // this is not done yet { - box.VSplitLeft(90.0f, &button, &box); - static int news_button=0; - if (DoButton_MenuTab(&news_button, localize("News"), active_page==PAGE_NEWS, &button, 0)) - new_page = PAGE_NEWS; - box.VSplitLeft(30.0f, 0, &box); + Box.VSplitLeft(90.0f, &Button, &Box); + static int s_NewsButton=0; + if (DoButton_MenuTab(&s_NewsButton, Localize("News"), m_ActivePage==PAGE_NEWS, &Button, 0)) + NewPage = PAGE_NEWS; + Box.VSplitLeft(30.0f, 0, &Box); } - box.VSplitLeft(100.0f, &button, &box); - static int internet_button=0; - if(DoButton_MenuTab(&internet_button, localize("Internet"), active_page==PAGE_INTERNET, &button, CUI::CORNER_TL)) + Box.VSplitLeft(100.0f, &Button, &Box); + static int s_InternetButton=0; + if(DoButton_MenuTab(&s_InternetButton, Localize("Internet"), m_ActivePage==PAGE_INTERNET, &Button, CUI::CORNER_TL)) { - client_serverbrowse_refresh(BROWSETYPE_INTERNET); - new_page = PAGE_INTERNET; + ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); + NewPage = PAGE_INTERNET; } - //box.VSplitLeft(4.0f, 0, &box); - box.VSplitLeft(80.0f, &button, &box); - static int lan_button=0; - if(DoButton_MenuTab(&lan_button, localize("LAN"), active_page==PAGE_LAN, &button, 0)) + //Box.VSplitLeft(4.0f, 0, &Box); + Box.VSplitLeft(80.0f, &Button, &Box); + static int s_LanButton=0; + if(DoButton_MenuTab(&s_LanButton, Localize("LAN"), m_ActivePage==PAGE_LAN, &Button, 0)) { - client_serverbrowse_refresh(BROWSETYPE_LAN); - new_page = PAGE_LAN; + ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); + NewPage = PAGE_LAN; } //box.VSplitLeft(4.0f, 0, &box); - box.VSplitLeft(110.0f, &button, &box); - static int favorites_button=0; - if(DoButton_MenuTab(&favorites_button, localize("Favorites"), active_page==PAGE_FAVORITES, &button, CUI::CORNER_TR)) + Box.VSplitLeft(110.0f, &Button, &Box); + static int s_FavoritesButton=0; + if(DoButton_MenuTab(&s_FavoritesButton, Localize("Favorites"), m_ActivePage==PAGE_FAVORITES, &Button, CUI::CORNER_TR)) { - client_serverbrowse_refresh(BROWSETYPE_FAVORITES); - new_page = PAGE_FAVORITES; + ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); + NewPage = PAGE_FAVORITES; } - box.VSplitLeft(4.0f*5, 0, &box); - box.VSplitLeft(100.0f, &button, &box); - static int demos_button=0; - if(DoButton_MenuTab(&demos_button, localize("Demos"), active_page==PAGE_DEMOS, &button, 0)) + Box.VSplitLeft(4.0f*5, 0, &Box); + Box.VSplitLeft(100.0f, &Button, &Box); + static int s_DemosButton=0; + if(DoButton_MenuTab(&s_DemosButton, Localize("Demos"), m_ActivePage==PAGE_DEMOS, &Button, CUI::CORNER_T)) { - demolist_populate(); - new_page = PAGE_DEMOS; + DemolistPopulate(); + NewPage = PAGE_DEMOS; } } else { - /* online menus */ - box.VSplitLeft(90.0f, &button, &box); - static int game_button=0; - if(DoButton_MenuTab(&game_button, localize("Game"), active_page==PAGE_GAME, &button, 0)) - new_page = PAGE_GAME; - - box.VSplitLeft(4.0f, 0, &box); - box.VSplitLeft(140.0f, &button, &box); - static int server_info_button=0; - if(DoButton_MenuTab(&server_info_button, localize("Server info"), active_page==PAGE_SERVER_INFO, &button, 0)) - new_page = PAGE_SERVER_INFO; - - box.VSplitLeft(4.0f, 0, &box); - box.VSplitLeft(140.0f, &button, &box); - static int callvote_button=0; - if(DoButton_MenuTab(&callvote_button, localize("Call vote"), active_page==PAGE_CALLVOTE, &button, 0)) - new_page = PAGE_CALLVOTE; + // online menus + Box.VSplitLeft(90.0f, &Button, &Box); + static int s_GameButton=0; + if(DoButton_MenuTab(&s_GameButton, Localize("Game"), m_ActivePage==PAGE_GAME, &Button, CUI::CORNER_T)) + NewPage = PAGE_GAME; + + Box.VSplitLeft(4.0f, 0, &Box); + Box.VSplitLeft(140.0f, &Button, &Box); + static int s_ServerInfoButton=0; + if(DoButton_MenuTab(&s_ServerInfoButton, Localize("Server info"), m_ActivePage==PAGE_SERVER_INFO, &Button, CUI::CORNER_T)) + NewPage = PAGE_SERVER_INFO; + + Box.VSplitLeft(4.0f, 0, &Box); + Box.VSplitLeft(140.0f, &Button, &Box); + static int s_CallVoteButton=0; + if(DoButton_MenuTab(&s_CallVoteButton, Localize("Call vote"), m_ActivePage==PAGE_CALLVOTE, &Button, CUI::CORNER_T)) + NewPage = PAGE_CALLVOTE; - box.VSplitLeft(30.0f, 0, &box); + Box.VSplitLeft(30.0f, 0, &Box); } /* box.VSplitRight(110.0f, &box, &button); static int system_button=0; - if (UI()->DoButton(&system_button, "System", config.ui_page==PAGE_SYSTEM, &button)) - config.ui_page = PAGE_SYSTEM; + if (UI()->DoButton(&system_button, "System", g_Config.m_UiPage==PAGE_SYSTEM, &button)) + g_Config.m_UiPage = PAGE_SYSTEM; box.VSplitRight(30.0f, &box, 0); */ - box.VSplitRight(90.0f, &box, &button); - static int quit_button=0; - if(DoButton_MenuTab(&quit_button, localize("Quit"), 0, &button, 0)) - popup = POPUP_QUIT; - - box.VSplitRight(10.0f, &box, &button); - box.VSplitRight(130.0f, &box, &button); - static int settings_button=0; - if(DoButton_MenuTab(&settings_button, localize("Settings"), active_page==PAGE_SETTINGS, &button, 0)) - new_page = PAGE_SETTINGS; + Box.VSplitRight(90.0f, &Box, &Button); + static int s_QuitButton=0; + if(DoButton_MenuTab(&s_QuitButton, Localize("Quit"), 0, &Button, CUI::CORNER_T)) + m_Popup = POPUP_QUIT; + + Box.VSplitRight(10.0f, &Box, &Button); + Box.VSplitRight(130.0f, &Box, &Button); + static int s_SettingsButton=0; + if(DoButton_MenuTab(&s_SettingsButton, Localize("Settings"), m_ActivePage==PAGE_SETTINGS, &Button, CUI::CORNER_T)) + NewPage = PAGE_SETTINGS; - if(new_page != -1) + if(NewPage != -1) { - if(client_state() == CLIENTSTATE_OFFLINE) - config.ui_page = new_page; + if(Client()->State() == IClient::STATE_OFFLINE) + g_Config.m_UiPage = NewPage; else - game_page = new_page; + m_GamePage = NewPage; } return 0; } -void MENUS::render_loading(float percent) +void CMenus::RenderLoading(float Percent) { - static int64 last_load_render = 0; + static int64 LastLoadRender = 0; // make sure that we don't render for each little thing we load // because that will slow down loading if we have vsync - if(time_get()-last_load_render < time_freq()/60) + if(time_get()-LastLoadRender < time_freq()/60) return; - last_load_render = time_get(); + LastLoadRender = time_get(); // need up date this here to get correct - vec3 rgb = hsl_to_rgb(vec3(config.ui_color_hue/255.0f, config.ui_color_sat/255.0f, config.ui_color_lht/255.0f)); - gui_color = vec4(rgb.r, rgb.g, rgb.b, config.ui_color_alpha/255.0f); + vec3 Rgb = HslToRgb(vec3(g_Config.m_UiColorHue/255.0f, g_Config.m_UiColorSat/255.0f, g_Config.m_UiColorLht/255.0f)); + ms_GuiColor = vec4(Rgb.r, Rgb.g, Rgb.b, g_Config.m_UiColorAlpha/255.0f); - CUIRect screen = *UI()->Screen(); - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); + CUIRect Screen = *UI()->Screen(); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); - render_background(); + RenderBackground(); float tw; float w = 700; float h = 200; - float x = screen.w/2-w/2; - float y = screen.h/2-h/2; + float x = Screen.w/2-w/2; + float y = Screen.h/2-h/2; Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.50f); - RenderTools()->draw_round_rect(x, y, w, h, 40.0f); + RenderTools()->DrawRoundRect(x, y, w, h, 40.0f); Graphics()->QuadsEnd(); - const char *caption = localize("Loading"); + const char *pCaption = Localize("Loading"); - tw = gfx_text_width(0, 48.0f, caption, -1); + tw = TextRender()->TextWidth(0, 48.0f, pCaption, -1); CUIRect r; r.x = x; r.y = y+20; r.w = w; r.h = h; - UI()->DoLabel(&r, caption, 48.0f, 0, -1); + UI()->DoLabel(&r, pCaption, 48.0f, 0, -1); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(1,1,1,0.75f); - RenderTools()->draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f); + RenderTools()->DrawRoundRect(x+40, y+h-75, (w-80)*Percent, 25, 5.0f); Graphics()->QuadsEnd(); - gfx_swap(); + Graphics()->Swap(); } -void MENUS::render_news(CUIRect main_view) +void CMenus::RenderNews(CUIRect MainView) { - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); } -void MENUS::on_init() +void CMenus::OnInit() { /* @@ -669,261 +678,263 @@ void MENUS::on_init() exit(-1); // */ - if(config.cl_show_welcome) - popup = POPUP_FIRST_LAUNCH; - config.cl_show_welcome = 0; + if(g_Config.m_ClShowWelcome) + m_Popup = POPUP_FIRST_LAUNCH; + g_Config.m_ClShowWelcome = 0; } -void MENUS::popup_message(const char *topic, const char *body, const char *button) +void CMenus::PopupMessage(const char *pTopic, const char *pBody, const char *pButton) { - str_copy(message_topic, topic, sizeof(message_topic)); - str_copy(message_body, body, sizeof(message_body)); - str_copy(message_button, button, sizeof(message_button)); - popup = POPUP_MESSAGE; + str_copy(m_aMessageTopic, pTopic, sizeof(m_aMessageTopic)); + str_copy(m_aMessageBody, pBody, sizeof(m_aMessageBody)); + str_copy(m_aMessageButton, pButton, sizeof(m_aMessageButton)); + m_Popup = POPUP_MESSAGE; } -int MENUS::render() +int CMenus::Render() { - CUIRect screen = *UI()->Screen(); - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); + CUIRect Screen = *UI()->Screen(); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); - static bool first = true; - if(first) + static bool s_First = true; + if(s_First) { - if(config.ui_page == PAGE_INTERNET) - client_serverbrowse_refresh(0); - else if(config.ui_page == PAGE_LAN) - client_serverbrowse_refresh(1); - first = false; + if(g_Config.m_UiPage == PAGE_INTERNET) + ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); + else if(g_Config.m_UiPage == PAGE_LAN) + ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); + else if(g_Config.m_UiPage == PAGE_FAVORITES) + ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); + s_First = false; } - if(client_state() == CLIENTSTATE_ONLINE) + if(Client()->State() == IClient::STATE_ONLINE) { - color_tabbar_inactive = color_tabbar_inactive_ingame; - color_tabbar_active = color_tabbar_active_ingame; + ms_ColorTabbarInactive = ms_ColorTabbarInactiveIngame; + ms_ColorTabbarActive = ms_ColorTabbarActiveIngame; } else { - render_background(); - color_tabbar_inactive = color_tabbar_inactive_outgame; - color_tabbar_active = color_tabbar_active_outgame; + RenderBackground(); + ms_ColorTabbarInactive = ms_ColorTabbarInactiveOutgame; + ms_ColorTabbarActive = ms_ColorTabbarActiveOutgame; } - CUIRect tab_bar; - CUIRect main_view; + CUIRect TabBar; + CUIRect MainView; // some margin around the screen - screen.Margin(10.0f, &screen); + Screen.Margin(10.0f, &Screen); - if(popup == POPUP_NONE) + if(m_Popup == POPUP_NONE) { // do tab bar - screen.HSplitTop(24.0f, &tab_bar, &main_view); - tab_bar.VMargin(20.0f, &tab_bar); - render_menubar(tab_bar); + Screen.HSplitTop(24.0f, &TabBar, &MainView); + TabBar.VMargin(20.0f, &TabBar); + RenderMenubar(TabBar); // news is not implemented yet - if(config.ui_page <= PAGE_NEWS || config.ui_page > PAGE_SETTINGS || (client_state() == CLIENTSTATE_OFFLINE && config.ui_page >= PAGE_GAME && config.ui_page <= PAGE_CALLVOTE)) + if(g_Config.m_UiPage <= PAGE_NEWS || g_Config.m_UiPage > PAGE_SETTINGS || (Client()->State() == IClient::STATE_OFFLINE && g_Config.m_UiPage >= PAGE_GAME && g_Config.m_UiPage <= PAGE_CALLVOTE)) { - client_serverbrowse_refresh(BROWSETYPE_INTERNET); - config.ui_page = PAGE_INTERNET; + ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); + g_Config.m_UiPage = PAGE_INTERNET; } // render current page - if(client_state() != CLIENTSTATE_OFFLINE) + if(Client()->State() != IClient::STATE_OFFLINE) { - if(game_page == PAGE_GAME) - render_game(main_view); - else if(game_page == PAGE_SERVER_INFO) - render_serverinfo(main_view); - else if(game_page == PAGE_CALLVOTE) - render_servercontrol(main_view); - else if(game_page == PAGE_SETTINGS) - render_settings(main_view); + if(m_GamePage == PAGE_GAME) + RenderGame(MainView); + else if(m_GamePage == PAGE_SERVER_INFO) + RenderServerInfo(MainView); + else if(m_GamePage == PAGE_CALLVOTE) + RenderServerControl(MainView); + else if(m_GamePage == PAGE_SETTINGS) + RenderSettings(MainView); } - else if(config.ui_page == PAGE_NEWS) - render_news(main_view); - else if(config.ui_page == PAGE_INTERNET) - render_serverbrowser(main_view); - else if(config.ui_page == PAGE_LAN) - render_serverbrowser(main_view); - else if(config.ui_page == PAGE_DEMOS) - render_demolist(main_view); - else if(config.ui_page == PAGE_FAVORITES) - render_serverbrowser(main_view); - else if(config.ui_page == PAGE_SETTINGS) - render_settings(main_view); + else if(g_Config.m_UiPage == PAGE_NEWS) + RenderNews(MainView); + else if(g_Config.m_UiPage == PAGE_INTERNET) + RenderServerbrowser(MainView); + else if(g_Config.m_UiPage == PAGE_LAN) + RenderServerbrowser(MainView); + else if(g_Config.m_UiPage == PAGE_DEMOS) + RenderDemoList(MainView); + else if(g_Config.m_UiPage == PAGE_FAVORITES) + RenderServerbrowser(MainView); + else if(g_Config.m_UiPage == PAGE_SETTINGS) + RenderSettings(MainView); } else { // make sure that other windows doesn't do anything funnay! //UI()->SetHotItem(0); //UI()->SetActiveItem(0); - char buf[128]; - const char *title = ""; - const char *extra_text = ""; - const char *button_text = ""; - int extra_align = 0; + char aBuf[128]; + const char *pTitle = ""; + const char *pExtraText = ""; + const char *pButtonText = ""; + int ExtraAlign = 0; - if(popup == POPUP_MESSAGE) + if(m_Popup == POPUP_MESSAGE) { - title = message_topic; - extra_text = message_body; - button_text = message_button; + pTitle = m_aMessageTopic; + pExtraText = m_aMessageBody; + pButtonText = m_aMessageButton; } - else if(popup == POPUP_CONNECTING) + else if(m_Popup == POPUP_CONNECTING) { - title = localize("Connecting to"); - extra_text = config.ui_server_address; // TODO: query the client about the address - button_text = localize("Abort"); - if(client_mapdownload_totalsize() > 0) + pTitle = Localize("Connecting to"); + pExtraText = g_Config.m_UiServerAddress; // TODO: query the client about the address + pButtonText = Localize("Abort"); + if(Client()->MapDownloadTotalsize() > 0) { - title = localize("Downloading map"); - str_format(buf, sizeof(buf), "%d/%d KiB", client_mapdownload_amount()/1024, client_mapdownload_totalsize()/1024); - extra_text = buf; + pTitle = Localize("Downloading map"); + str_format(aBuf, sizeof(aBuf), "%d/%d KiB", Client()->MapDownloadAmount()/1024, Client()->MapDownloadTotalsize()/1024); + pExtraText = aBuf; } } - else if(popup == POPUP_DISCONNECTED) + else if(m_Popup == POPUP_DISCONNECTED) { - title = localize("Disconnected"); - extra_text = client_error_string(); - button_text = localize("Ok"); - extra_align = -1; + pTitle = Localize("Disconnected"); + pExtraText = Client()->ErrorString(); + pButtonText = Localize("Ok"); + ExtraAlign = -1; } - else if(popup == POPUP_PURE) + else if(m_Popup == POPUP_PURE) { - title = localize("Disconnected"); - extra_text = localize("The server is running a non-standard tuning on a pure game type."); - button_text = localize("Ok"); - extra_align = -1; + pTitle = Localize("Disconnected"); + pExtraText = Localize("The server is running a non-standard tuning on a pure game type."); + pButtonText = Localize("Ok"); + ExtraAlign = -1; } - else if(popup == POPUP_PASSWORD) + else if(m_Popup == POPUP_PASSWORD) { - title = localize("Password Incorrect"); - extra_text = client_error_string(); - button_text = localize("Try again"); + pTitle = Localize("Password Incorrect"); + pExtraText = Client()->ErrorString(); + pButtonText = Localize("Try again"); } - else if(popup == POPUP_QUIT) + else if(m_Popup == POPUP_QUIT) { - title = localize("Quit"); - extra_text = localize("Are you sure that you want to quit?"); + pTitle = Localize("Quit"); + pExtraText = Localize("Are you sure that you want to quit?"); } - else if(popup == POPUP_FIRST_LAUNCH) + else if(m_Popup == POPUP_FIRST_LAUNCH) { - title = localize("Welcome to Teeworlds"); - extra_text = localize("As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server."); - button_text = localize("Ok"); - extra_align = -1; + pTitle = Localize("Welcome to Teeworlds"); + pExtraText = Localize("As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server."); + pButtonText = Localize("Ok"); + ExtraAlign = -1; } - CUIRect box, part; - box = screen; - box.VMargin(150.0f, &box); - box.HMargin(150.0f, &box); + CUIRect Box, Part; + Box = Screen; + Box.VMargin(150.0f, &Box); + Box.HMargin(150.0f, &Box); // render the box - RenderTools()->DrawUIRect(&box, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 15.0f); + RenderTools()->DrawUIRect(&Box, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 15.0f); - box.HSplitTop(20.f, &part, &box); - box.HSplitTop(24.f, &part, &box); - UI()->DoLabel(&part, title, 24.f, 0); - box.HSplitTop(20.f, &part, &box); - box.HSplitTop(24.f, &part, &box); - part.VMargin(20.f, &part); + Box.HSplitTop(20.f, &Part, &Box); + Box.HSplitTop(24.f, &Part, &Box); + UI()->DoLabel(&Part, pTitle, 24.f, 0); + Box.HSplitTop(20.f, &Part, &Box); + Box.HSplitTop(24.f, &Part, &Box); + Part.VMargin(20.f, &Part); - if(extra_align == -1) - UI()->DoLabel(&part, extra_text, 20.f, -1, (int)part.w); + if(ExtraAlign == -1) + UI()->DoLabel(&Part, pExtraText, 20.f, -1, (int)Part.w); else - UI()->DoLabel(&part, extra_text, 20.f, 0, -1); + UI()->DoLabel(&Part, pExtraText, 20.f, 0, -1); - if(popup == POPUP_QUIT) + if(m_Popup == POPUP_QUIT) { - CUIRect yes, no; - box.HSplitBottom(20.f, &box, &part); - box.HSplitBottom(24.f, &box, &part); - part.VMargin(80.0f, &part); + CUIRect Yes, No; + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Part.VMargin(80.0f, &Part); - part.VSplitMid(&no, &yes); + Part.VSplitMid(&No, &Yes); - yes.VMargin(20.0f, &yes); - no.VMargin(20.0f, &no); + Yes.VMargin(20.0f, &Yes); + No.VMargin(20.0f, &No); - static int button_abort = 0; - if(DoButton_Menu(&button_abort, localize("No"), 0, &no) || escape_pressed) - popup = POPUP_NONE; + static int s_ButtonAbort = 0; + if(DoButton_Menu(&s_ButtonAbort, Localize("No"), 0, &No) || m_EscapePressed) + m_Popup = POPUP_NONE; - static int button_tryagain = 0; - if(DoButton_Menu(&button_tryagain, localize("Yes"), 0, &yes) || enter_pressed) - client_quit(); + static int s_ButtonTryAgain = 0; + if(DoButton_Menu(&s_ButtonTryAgain, Localize("Yes"), 0, &Yes) || m_EnterPressed) + Client()->Quit(); } - else if(popup == POPUP_PASSWORD) + else if(m_Popup == POPUP_PASSWORD) { - CUIRect label, textbox, tryagain, abort; + CUIRect Label, TextBox, TryAgain, Abort; - box.HSplitBottom(20.f, &box, &part); - box.HSplitBottom(24.f, &box, &part); - part.VMargin(80.0f, &part); + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Part.VMargin(80.0f, &Part); - part.VSplitMid(&abort, &tryagain); + Part.VSplitMid(&Abort, &TryAgain); - tryagain.VMargin(20.0f, &tryagain); - abort.VMargin(20.0f, &abort); + TryAgain.VMargin(20.0f, &TryAgain); + Abort.VMargin(20.0f, &Abort); - static int button_abort = 0; - if(DoButton_Menu(&button_abort, localize("Abort"), 0, &abort) || escape_pressed) - popup = POPUP_NONE; + static int s_ButtonAbort = 0; + if(DoButton_Menu(&s_ButtonAbort, Localize("Abort"), 0, &Abort) || m_EscapePressed) + m_Popup = POPUP_NONE; - static int button_tryagain = 0; - if(DoButton_Menu(&button_tryagain, localize("Try again"), 0, &tryagain) || enter_pressed) + static int s_ButtonTryAgain = 0; + if(DoButton_Menu(&s_ButtonTryAgain, Localize("Try again"), 0, &TryAgain) || m_EnterPressed) { - client_connect(config.ui_server_address); + Client()->Connect(g_Config.m_UiServerAddress); } - box.HSplitBottom(60.f, &box, &part); - box.HSplitBottom(24.f, &box, &part); + Box.HSplitBottom(60.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); - part.VSplitLeft(60.0f, 0, &label); - label.VSplitLeft(100.0f, 0, &textbox); - textbox.VSplitLeft(20.0f, 0, &textbox); - textbox.VSplitRight(60.0f, &textbox, 0); - UI()->DoLabel(&label, localize("Password"), 20, -1); - DoEditBox(&config.password, &textbox, config.password, sizeof(config.password), 14.0f, true); + Part.VSplitLeft(60.0f, 0, &Label); + Label.VSplitLeft(100.0f, 0, &TextBox); + TextBox.VSplitLeft(20.0f, 0, &TextBox); + TextBox.VSplitRight(60.0f, &TextBox, 0); + UI()->DoLabel(&Label, Localize("Password"), 18.0f, -1); + DoEditBox(&g_Config.m_Password, &TextBox, g_Config.m_Password, sizeof(g_Config.m_Password), 12.0f, true); } - else if(popup == POPUP_FIRST_LAUNCH) + else if(m_Popup == POPUP_FIRST_LAUNCH) { - CUIRect label, textbox; + CUIRect Label, TextBox; - box.HSplitBottom(20.f, &box, &part); - box.HSplitBottom(24.f, &box, &part); - part.VMargin(80.0f, &part); + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Part.VMargin(80.0f, &Part); - static int enter_button = 0; - if(DoButton_Menu(&enter_button, localize("Enter"), 0, &part) || enter_pressed) - popup = POPUP_NONE; + static int s_EnterButton = 0; + if(DoButton_Menu(&s_EnterButton, Localize("Enter"), 0, &Part) || m_EnterPressed) + m_Popup = POPUP_NONE; - box.HSplitBottom(40.f, &box, &part); - box.HSplitBottom(24.f, &box, &part); + Box.HSplitBottom(40.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); - part.VSplitLeft(60.0f, 0, &label); - label.VSplitLeft(100.0f, 0, &textbox); - textbox.VSplitLeft(20.0f, 0, &textbox); - textbox.VSplitRight(60.0f, &textbox, 0); - UI()->DoLabel(&label, localize("Nickname"), 20, -1); - DoEditBox(&config.player_name, &textbox, config.player_name, sizeof(config.player_name), 14.0f); + Part.VSplitLeft(60.0f, 0, &Label); + Label.VSplitLeft(100.0f, 0, &TextBox); + TextBox.VSplitLeft(20.0f, 0, &TextBox); + TextBox.VSplitRight(60.0f, &TextBox, 0); + UI()->DoLabel(&Label, Localize("Nickname"), 18.0f, -1); + DoEditBox(&g_Config.m_PlayerName, &TextBox, g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName), 12.0f); } else { - box.HSplitBottom(20.f, &box, &part); - box.HSplitBottom(24.f, &box, &part); - part.VMargin(120.0f, &part); + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Part.VMargin(120.0f, &Part); - static int button = 0; - if(DoButton_Menu(&button, button_text, 0, &part) || escape_pressed || enter_pressed) + static int s_Button = 0; + if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || m_EscapePressed || m_EnterPressed) { - if(popup == POPUP_CONNECTING) - client_disconnect(); - popup = POPUP_NONE; + if(m_Popup == POPUP_CONNECTING) + Client()->Disconnect(); + m_Popup = POPUP_NONE; } } } @@ -932,109 +943,113 @@ int MENUS::render() } -void MENUS::set_active(bool active) +void CMenus::SetActive(bool Active) { - menu_active = active; - if(!menu_active && need_sendinfo) + m_MenuActive = Active; + if(!m_MenuActive && m_NeedSendinfo) { - gameclient.send_info(false); - need_sendinfo = false; + m_pClient->SendInfo(false); + m_NeedSendinfo = false; } } -void MENUS::on_reset() +void CMenus::OnReset() { } -bool MENUS::on_mousemove(float x, float y) +bool CMenus::OnMouseMove(float x, float y) { - last_input = time_get(); + m_LastInput = time_get(); - if(!menu_active) + if(!m_MenuActive) return false; - mouse_pos.x += x; - mouse_pos.y += y; - if(mouse_pos.x < 0) mouse_pos.x = 0; - if(mouse_pos.y < 0) mouse_pos.y = 0; - if(mouse_pos.x > Graphics()->ScreenWidth()) mouse_pos.x = Graphics()->ScreenWidth(); - if(mouse_pos.y > Graphics()->ScreenHeight()) mouse_pos.y = Graphics()->ScreenHeight(); + m_MousePos.x += x; + m_MousePos.y += y; + if(m_MousePos.x < 0) m_MousePos.x = 0; + if(m_MousePos.y < 0) m_MousePos.y = 0; + if(m_MousePos.x > Graphics()->ScreenWidth()) m_MousePos.x = Graphics()->ScreenWidth(); + if(m_MousePos.y > Graphics()->ScreenHeight()) m_MousePos.y = Graphics()->ScreenHeight(); return true; } -bool MENUS::on_input(INPUT_EVENT e) +bool CMenus::OnInput(IInput::CEvent e) { - last_input = time_get(); + m_LastInput = time_get(); // special handle esc and enter for popup purposes - if(e.flags&INPFLAG_PRESS) + if(e.m_Flags&IInput::FLAG_PRESS) { - if(e.key == KEY_ESCAPE) + if(e.m_Key == KEY_ESCAPE) { - escape_pressed = true; - set_active(!is_active()); + m_EscapePressed = true; + SetActive(!IsActive()); return true; } } - if(is_active()) + if(IsActive()) { // special for popups - if(e.flags&INPFLAG_PRESS && e.key == KEY_RETURN) - enter_pressed = true; + if(e.m_Flags&IInput::FLAG_PRESS && e.m_Key == KEY_RETURN) + m_EnterPressed = true; - if(num_inputevents < MAX_INPUTEVENTS) - inputevents[num_inputevents++] = e; + if(m_NumInputEvents < MAX_INPUTEVENTS) + m_aInputEvents[m_NumInputEvents++] = e; return true; } return false; } -void MENUS::on_statechange(int new_state, int old_state) +void CMenus::OnStateChange(int NewState, int OldState) { - if(new_state == CLIENTSTATE_OFFLINE) + // reset active item + UI()->SetActiveItem(0); + + if(NewState == IClient::STATE_OFFLINE) { - popup = POPUP_NONE; - if(client_error_string() && client_error_string()[0] != 0) + m_Popup = POPUP_NONE; + if(Client()->ErrorString() && Client()->ErrorString()[0] != 0) { - if(strstr(client_error_string(), "password")) + if(str_find(Client()->ErrorString(), "password")) { - popup = POPUP_PASSWORD; - UI()->SetHotItem(&config.password); - UI()->SetActiveItem(&config.password); + m_Popup = POPUP_PASSWORD; + UI()->SetHotItem(&g_Config.m_Password); + UI()->SetActiveItem(&g_Config.m_Password); } else - popup = POPUP_DISCONNECTED; - } } - else if(new_state == CLIENTSTATE_LOADING) + m_Popup = POPUP_DISCONNECTED; + } + } + else if(NewState == IClient::STATE_LOADING) { - popup = POPUP_CONNECTING; - client_serverinfo_request(); + m_Popup = POPUP_CONNECTING; + //client_serverinfo_request(); } - else if(new_state == CLIENTSTATE_CONNECTING) - popup = POPUP_CONNECTING; - else if (new_state == CLIENTSTATE_ONLINE || new_state == CLIENTSTATE_DEMOPLAYBACK) + else if(NewState == IClient::STATE_CONNECTING) + m_Popup = POPUP_CONNECTING; + else if (NewState == IClient::STATE_ONLINE || NewState == IClient::STATE_DEMOPLAYBACK) { - popup = POPUP_NONE; - set_active(false); + m_Popup = POPUP_NONE; + SetActive(false); } } extern "C" void font_debug_render(); -void MENUS::on_render() +void CMenus::OnRender() { /* // text rendering test stuff render_background(); - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, 10, 10, 20, TEXTFLAG_RENDER); - gfx_text_ex(&cursor, "ようこそ - ガイド", -1); + CTextCursor cursor; + TextRender()->SetCursor(&cursor, 10, 10, 20, TEXTFLAG_RENDER); + TextRender()->TextEx(&cursor, "ようこそ - ガイド", -1); - gfx_text_set_cursor(&cursor, 10, 30, 15, TEXTFLAG_RENDER); - gfx_text_ex(&cursor, "ようこそ - ガイド", -1); + TextRender()->SetCursor(&cursor, 10, 30, 15, TEXTFLAG_RENDER); + TextRender()->TextEx(&cursor, "ようこそ - ガイド", -1); //Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); @@ -1042,101 +1057,105 @@ void MENUS::on_render() Graphics()->QuadsEnd(); return;*/ - if(client_state() != CLIENTSTATE_ONLINE && client_state() != CLIENTSTATE_DEMOPLAYBACK) - set_active(true); + if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK) + SetActive(true); - if(client_state() == CLIENTSTATE_DEMOPLAYBACK) + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) { - CUIRect screen = *UI()->Screen(); - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); - render_demoplayer(screen); + CUIRect Screen = *UI()->Screen(); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); + RenderDemoPlayer(Screen); } - if(client_state() == CLIENTSTATE_ONLINE && gameclient.servermode == gameclient.SERVERMODE_PUREMOD) + if(Client()->State() == IClient::STATE_ONLINE && m_pClient->m_ServerMode == m_pClient->SERVERMODE_PUREMOD) { - client_disconnect(); - set_active(true); - popup = POPUP_PURE; + Client()->Disconnect(); + SetActive(true); + m_Popup = POPUP_PURE; } - if(!is_active()) + if(!IsActive()) { - escape_pressed = false; - enter_pressed = false; - num_inputevents = 0; + m_EscapePressed = false; + m_EnterPressed = false; + m_NumInputEvents = 0; return; } // update colors - vec3 rgb = hsl_to_rgb(vec3(config.ui_color_hue/255.0f, config.ui_color_sat/255.0f, config.ui_color_lht/255.0f)); - gui_color = vec4(rgb.r, rgb.g, rgb.b, config.ui_color_alpha/255.0f); - - color_tabbar_inactive_outgame = vec4(0,0,0,0.25f); - color_tabbar_active_outgame = vec4(0,0,0,0.5f); - - float color_ingame_scale_i = 0.5f; - float color_ingame_scale_a = 0.2f; - color_tabbar_inactive_ingame = vec4( - gui_color.r*color_ingame_scale_i, - gui_color.g*color_ingame_scale_i, - gui_color.b*color_ingame_scale_i, - gui_color.a*0.8f); + vec3 Rgb = HslToRgb(vec3(g_Config.m_UiColorHue/255.0f, g_Config.m_UiColorSat/255.0f, g_Config.m_UiColorLht/255.0f)); + ms_GuiColor = vec4(Rgb.r, Rgb.g, Rgb.b, g_Config.m_UiColorAlpha/255.0f); + + ms_ColorTabbarInactiveOutgame = vec4(0,0,0,0.25f); + ms_ColorTabbarActiveOutgame = vec4(0,0,0,0.5f); + + float ColorIngameScaleI = 0.5f; + float ColorIngameAcaleA = 0.2f; + ms_ColorTabbarInactiveIngame = vec4( + ms_GuiColor.r*ColorIngameScaleI, + ms_GuiColor.g*ColorIngameScaleI, + ms_GuiColor.b*ColorIngameScaleI, + ms_GuiColor.a*0.8f); - color_tabbar_active_ingame = vec4( - gui_color.r*color_ingame_scale_a, - gui_color.g*color_ingame_scale_a, - gui_color.b*color_ingame_scale_a, - gui_color.a); + ms_ColorTabbarActiveIngame = vec4( + ms_GuiColor.r*ColorIngameAcaleA, + ms_GuiColor.g*ColorIngameAcaleA, + ms_GuiColor.b*ColorIngameAcaleA, + ms_GuiColor.a); // update the ui - CUIRect *screen = UI()->Screen(); - float mx = (mouse_pos.x/(float)Graphics()->ScreenWidth())*screen->w; - float my = (mouse_pos.y/(float)Graphics()->ScreenHeight())*screen->h; + CUIRect *pScreen = UI()->Screen(); + float mx = (m_MousePos.x/(float)Graphics()->ScreenWidth())*pScreen->w; + float my = (m_MousePos.y/(float)Graphics()->ScreenHeight())*pScreen->h; - int buttons = 0; - if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; - if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; - if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; + int Buttons = 0; + if(m_UseMouseButtons) + { + if(Input()->KeyPressed(KEY_MOUSE_1)) Buttons |= 1; + if(Input()->KeyPressed(KEY_MOUSE_2)) Buttons |= 2; + if(Input()->KeyPressed(KEY_MOUSE_3)) Buttons |= 4; + } - UI()->Update(mx,my,mx*3.0f,my*3.0f,buttons); + UI()->Update(mx,my,mx*3.0f,my*3.0f,Buttons); // render - if(client_state() != CLIENTSTATE_DEMOPLAYBACK) - render(); + if(Client()->State() != IClient::STATE_DEMOPLAYBACK) + Render(); // render cursor - Graphics()->TextureSet(data->images[IMAGE_CURSOR].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CURSOR].m_Id); Graphics()->QuadsBegin(); Graphics()->SetColor(1,1,1,1); - Graphics()->QuadsDrawTL(mx,my,24,24); + IGraphics::CQuadItem QuadItem(mx, my, 24, 24); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); // render debug information - if(config.debug) + if(g_Config.m_Debug) { - CUIRect screen = *UI()->Screen(); - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h); - - char buf[512]; - str_format(buf, sizeof(buf), "%p %p %p", UI()->HotItem(), UI()->ActiveItem(), UI()->LastActiveItem()); - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, 10, 10, 10, TEXTFLAG_RENDER); - gfx_text_ex(&cursor, buf, -1); + CUIRect Screen = *UI()->Screen(); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); + + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "%p %p %p", UI()->HotItem(), UI()->ActiveItem(), UI()->LastActiveItem()); + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, 10, 10, 10, TEXTFLAG_RENDER); + TextRender()->TextEx(&Cursor, aBuf, -1); } - escape_pressed = false; - enter_pressed = false; - num_inputevents = 0; + m_EscapePressed = false; + m_EnterPressed = false; + m_NumInputEvents = 0; } -static int texture_blob = -1; +static int gs_TextureBlob = -1; -void MENUS::render_background() +void CMenus::RenderBackground() { //Graphics()->Clear(1,1,1); //render_sunrays(0,0); - if(texture_blob == -1) - texture_blob = Graphics()->LoadTexture("blob.png", IMG_AUTO, 0); + if(gs_TextureBlob == -1) + gs_TextureBlob = Graphics()->LoadTexture("blob.png", CImageInfo::FORMAT_AUTO, 0); float sw = 300*Graphics()->ScreenAspect(); @@ -1150,36 +1169,41 @@ void MENUS::render_background() Graphics()->QuadsBegin(); //vec4 bottom(gui_color.r*0.3f, gui_color.g*0.3f, gui_color.b*0.3f, 1.0f); //vec4 bottom(0, 0, 0, 1.0f); - vec4 bottom(gui_color.r, gui_color.g, gui_color.b, 1.0f); - vec4 top(gui_color.r, gui_color.g, gui_color.b, 1.0f); - Graphics()->SetColorVertex(0, top.r, top.g, top.b, top.a); - Graphics()->SetColorVertex(1, top.r, top.g, top.b, top.a); - Graphics()->SetColorVertex(2, bottom.r, bottom.g, bottom.b, bottom.a); - Graphics()->SetColorVertex(3, bottom.r, bottom.g, bottom.b, bottom.a); - Graphics()->QuadsDrawTL(0, 0, sw, sh); + vec4 Bottom(ms_GuiColor.r, ms_GuiColor.g, ms_GuiColor.b, 1.0f); + vec4 Top(ms_GuiColor.r, ms_GuiColor.g, ms_GuiColor.b, 1.0f); + IGraphics::CColorVertex Array[4] = { + IGraphics::CColorVertex(0, Top.r, Top.g, Top.b, Top.a), + IGraphics::CColorVertex(1, Top.r, Top.g, Top.b, Top.a), + IGraphics::CColorVertex(2, Bottom.r, Bottom.g, Bottom.b, Bottom.a), + IGraphics::CColorVertex(3, Bottom.r, Bottom.g, Bottom.b, Bottom.a)}; + Graphics()->SetColorVertex(Array, 4); + IGraphics::CQuadItem QuadItem(0, 0, sw, sh); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); // render the tiles Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); - float size = 15.0f; - float offset_time = fmod(client_localtime()*0.15f, 2.0f); - for(int y = -2; y < (int)(sw/size); y++) - for(int x = -2; x < (int)(sh/size); x++) + float Size = 15.0f; + float OffsetTime = fmod(Client()->LocalTime()*0.15f, 2.0f); + for(int y = -2; y < (int)(sw/Size); y++) + for(int x = -2; x < (int)(sh/Size); x++) { Graphics()->SetColor(0,0,0,0.045f); - Graphics()->QuadsDrawTL((x-offset_time)*size*2+(y&1)*size, (y+offset_time)*size, size, size); + IGraphics::CQuadItem QuadItem((x-OffsetTime)*Size*2+(y&1)*Size, (y+OffsetTime)*Size, Size, Size); + Graphics()->QuadsDrawTL(&QuadItem, 1); } Graphics()->QuadsEnd(); // render border fade - Graphics()->TextureSet(texture_blob); + Graphics()->TextureSet(gs_TextureBlob); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); - Graphics()->QuadsDrawTL(-100, -100, sw+200, sh+200); + QuadItem = IGraphics::CQuadItem(-100, -100, sw+200, sh+200); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); // restore screen - {CUIRect screen = *UI()->Screen(); - Graphics()->MapScreen(screen.x, screen.y, screen.w, screen.h);} + {CUIRect Screen = *UI()->Screen(); + Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h);} } diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h new file mode 100644 index 00000000..3055e661 --- /dev/null +++ b/src/game/client/components/menus.h @@ -0,0 +1,223 @@ +#ifndef GAME_CLIENT_COMPONENTS_MENUS_H +#define GAME_CLIENT_COMPONENTS_MENUS_H + +#include <base/vmath.h> +#include <base/tl/sorted_array.h> + +#include <game/client/component.h> +#include <game/client/ui.h> + + +// compnent to fetch keypresses, override all other input +class CMenusKeyBinder : public CComponent +{ +public: + bool m_TakeKey; + bool m_GotKey; + IInput::CEvent m_Key; + CMenusKeyBinder(); + virtual bool OnInput(IInput::CEvent Event); +}; + +class CMenus : public CComponent +{ + static vec4 ms_GuiColor; + static vec4 ms_ColorTabbarInactiveOutgame; + static vec4 ms_ColorTabbarActiveOutgame; + static vec4 ms_ColorTabbarInactiveIngame; + static vec4 ms_ColorTabbarActiveIngame; + static vec4 ms_ColorTabbarInactive; + static vec4 ms_ColorTabbarActive; + + vec4 ButtonColorMul(const void *pID); + + + int DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners); + int DoButton_SettingsTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + + int DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect); + int DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + + /*static void ui_draw_menu_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_keyselect_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_menu_tab_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_settings_tab_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + */ + + int DoButton_BrowseIcon(int Checked, const CUIRect *pRect); + int DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoButton_ListRow(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + + //static void ui_draw_browse_icon(int what, const CUIRect *r); + //static void ui_draw_grid_header(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + + /*static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const CUIRect *r, const void *extra); + static void ui_draw_checkbox(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); + */ + int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false, int Corners=CUI::CORNER_ALL); + //static int ui_do_edit_box(void *id, const CUIRect *rect, char *str, unsigned str_size, float font_size, bool hidden=false); + + float DoScrollbarV(const void *pID, const CUIRect *pRect, float Current); + float DoScrollbarH(const void *pID, const CUIRect *pRect, float Current); + void DoButton_KeySelect(const void *pID, const char *pText, int Checked, const CUIRect *pRect); + int DoKeyReader(void *pID, const CUIRect *pRect, int Key); + + //static int ui_do_key_reader(void *id, const CUIRect *rect, int key); + void UiDoGetButtons(int Start, int Stop, CUIRect View); + + struct CListboxItem + { + int m_Visible; + int m_Selected; + CUIRect m_Rect; + CUIRect m_HitRect; + }; + + void UiDoListboxStart(void *pId, const CUIRect *pRect, float RowHeight, const char *pTitle, const char *pBottomText, int NumItems, + int ItemsPerRow, int SelectedIndex, float ScrollValue); + CListboxItem UiDoListboxNextItem(void *pID, bool Selected = false); + static CListboxItem UiDoListboxNextRow(); + int UiDoListboxEnd(float *pScrollValue, bool *pItemActivated); + + //static void demolist_listdir_callback(const char *name, int is_dir, void *user); + //static void demolist_list_callback(const CUIRect *rect, int index, void *user); + + enum + { + POPUP_NONE=0, + POPUP_FIRST_LAUNCH, + POPUP_CONNECTING, + POPUP_MESSAGE, + POPUP_DISCONNECTED, + POPUP_PURE, + POPUP_PASSWORD, + POPUP_QUIT, + }; + + enum + { + PAGE_NEWS=1, + PAGE_GAME, + PAGE_SERVER_INFO, + PAGE_CALLVOTE, + PAGE_INTERNET, + PAGE_LAN, + PAGE_FAVORITES, + PAGE_DEMOS, + PAGE_SETTINGS, + PAGE_SYSTEM, + }; + + int m_GamePage; + int m_Popup; + int m_ActivePage; + bool m_MenuActive; + bool m_UseMouseButtons; + vec2 m_MousePos; + + int64 m_LastInput; + + // + char m_aMessageTopic[512]; + char m_aMessageBody[512]; + char m_aMessageButton[512]; + + void PopupMessage(const char *pTopic, const char *pBody, const char *pButton); + + // TODO: this is a bit ugly but.. well.. yeah + enum { MAX_INPUTEVENTS = 32 }; + static IInput::CEvent m_aInputEvents[MAX_INPUTEVENTS]; + static int m_NumInputEvents; + + // some settings + static float ms_ButtonHeight; + static float ms_ListheaderHeight; + static float ms_FontmodHeight; + + // for graphic settings + bool m_NeedRestart; + bool m_NeedSendinfo; + + // + bool m_EscapePressed; + bool m_EnterPressed; + + // for call vote + int m_CallvoteSelectedOption; + int m_CallvoteSelectedPlayer; + + // demo + struct CDemoItem + { + char m_aFilename[512]; + char m_aName[256]; + + bool operator<(const CDemoItem &Other) { return str_comp(m_aName, Other.m_aName) < 0; } + }; + + sorted_array<CDemoItem> m_lDemos; + + void DemolistPopulate(); + static void DemolistCountCallback(const char *pName, int IsDir, void *pUser); + static void DemolistFetchCallback(const char *pName, int IsDir, void *pUser); + + // found in menus.cpp + int Render(); + //void render_background(); + //void render_loading(float percent); + int RenderMenubar(CUIRect r); + void RenderNews(CUIRect MainView); + + // found in menus_demo.cpp + void RenderDemoPlayer(CUIRect MainView); + void RenderDemoList(CUIRect MainView); + + // found in menus_ingame.cpp + void RenderGame(CUIRect MainView); + void RenderServerInfo(CUIRect MainView); + void RenderServerControl(CUIRect MainView); + void RenderServerControlKick(CUIRect MainView); + void RenderServerControlServer(CUIRect MainView); + + // found in menus_browser.cpp + int m_SelectedIndex; + void RenderServerbrowserServerList(CUIRect View); + void RenderServerbrowserServerDetail(CUIRect View); + void RenderServerbrowserFilters(CUIRect View); + void RenderServerbrowser(CUIRect MainView); + + // found in menus_settings.cpp + void RenderSettingsGeneral(CUIRect MainView); + void RenderSettingsPlayer(CUIRect MainView); + void RenderSettingsControls(CUIRect MainView); + void RenderSettingsGraphics(CUIRect MainView); + void RenderSettingsSound(CUIRect MainView); + void RenderSettings(CUIRect MainView); + + void SetActive(bool Active); +public: + void RenderBackground(); + + void UseMouseButtons(bool Use) { m_UseMouseButtons = Use; } + + static CMenusKeyBinder m_Binder; + + CMenus(); + + void RenderLoading(float Percent); + + bool IsActive() const { return m_MenuActive; } + + virtual void OnInit(); + + virtual void OnStateChange(int NewState, int OldState); + virtual void OnReset(); + virtual void OnRender(); + virtual bool OnInput(IInput::CEvent Event); + virtual bool OnMouseMove(float x, float y); +}; +#endif diff --git a/src/game/client/components/menus.hpp b/src/game/client/components/menus.hpp deleted file mode 100644 index 02759403..00000000 --- a/src/game/client/components/menus.hpp +++ /dev/null @@ -1,216 +0,0 @@ -#include <base/vmath.hpp> -#include <base/tl/sorted_array.hpp> - -#include <game/client/component.hpp> -#include <game/client/ui.hpp> - - -// compnent to fetch keypresses, override all other input -class MENUS_KEYBINDER : public COMPONENT -{ -public: - bool take_key; - bool got_key; - INPUT_EVENT key; - MENUS_KEYBINDER(); - virtual bool on_input(INPUT_EVENT e); -}; - -class MENUS : public COMPONENT -{ - static vec4 gui_color; - static vec4 color_tabbar_inactive_outgame; - static vec4 color_tabbar_active_outgame; - static vec4 color_tabbar_inactive_ingame; - static vec4 color_tabbar_active_ingame; - static vec4 color_tabbar_inactive; - static vec4 color_tabbar_active; - - vec4 button_color_mul(const void *pID); - - - int DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - int DoButton_MenuTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Corners); - int DoButton_SettingsTab(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - - int DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect); - int DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - int DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - - /*static void ui_draw_menu_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - static void ui_draw_keyselect_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - static void ui_draw_menu_tab_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - static void ui_draw_settings_tab_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - */ - - int DoButton_BrowseIcon(int Checked, const CUIRect *pRect); - int DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - int DoButton_ListRow(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - - //static void ui_draw_browse_icon(int what, const CUIRect *r); - //static void ui_draw_grid_header(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - - /*static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const CUIRect *r, const void *extra); - static void ui_draw_checkbox(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - */ - int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false); - //static int ui_do_edit_box(void *id, const CUIRect *rect, char *str, unsigned str_size, float font_size, bool hidden=false); - - float DoScrollbarV(const void *pID, const CUIRect *pRect, float Current); - float DoScrollbarH(const void *pID, const CUIRect *pRect, float Current); - int DoButton_KeySelect(const void *pID, const char *pText, int Checked, const CUIRect *pRect); - int DoKeyReader(void *pID, const CUIRect *pRect, int Key); - - //static int ui_do_key_reader(void *id, const CUIRect *rect, int key); - void ui_do_getbuttons(int start, int stop, CUIRect view); - - struct LISTBOXITEM - { - int visible; - int selected; - CUIRect rect; - CUIRect hitrect; - }; - - void ui_do_listbox_start(void *id, const CUIRect *rect, float row_height, const char *title, int num_items, int selected_index); - LISTBOXITEM ui_do_listbox_nextitem(void *id); - static LISTBOXITEM ui_do_listbox_nextrow(); - int ui_do_listbox_end(); - - //static void demolist_listdir_callback(const char *name, int is_dir, void *user); - //static void demolist_list_callback(const RECT *rect, int index, void *user); - - enum - { - POPUP_NONE=0, - POPUP_FIRST_LAUNCH, - POPUP_CONNECTING, - POPUP_MESSAGE, - POPUP_DISCONNECTED, - POPUP_PURE, - POPUP_PASSWORD, - POPUP_QUIT, - }; - - enum - { - PAGE_NEWS=1, - PAGE_GAME, - PAGE_SERVER_INFO, - PAGE_CALLVOTE, - PAGE_INTERNET, - PAGE_LAN, - PAGE_FAVORITES, - PAGE_DEMOS, - PAGE_SETTINGS, - PAGE_SYSTEM, - }; - - int game_page; - int popup; - int active_page; - bool menu_active; - vec2 mouse_pos; - - int64 last_input; - - // - char message_topic[512]; - char message_body[512]; - char message_button[512]; - - void popup_message(const char *topic, const char *body, const char *button); - - // TODO: this is a bit ugly but.. well.. yeah - enum { MAX_INPUTEVENTS = 32 }; - static INPUT_EVENT inputevents[MAX_INPUTEVENTS]; - static int num_inputevents; - - // some settings - static float button_height; - static float listheader_height; - static float fontmod_height; - - // for graphic settings - bool need_restart; - bool need_sendinfo; - - // - bool escape_pressed; - bool enter_pressed; - - // for call vote - int callvote_selectedoption; - int callvote_selectedplayer; - - // demo - struct DEMOITEM - { - char filename[512]; - char name[256]; - - bool operator<(const DEMOITEM &other) { return str_comp(name, other.name) < 0; } - }; - - sorted_array<DEMOITEM> demos; - - void demolist_populate(); - static void demolist_count_callback(const char *name, int is_dir, void *user); - static void demolist_fetch_callback(const char *name, int is_dir, void *user); - - // found in menus.cpp - int render(); - //void render_background(); - //void render_loading(float percent); - int render_menubar(CUIRect r); - void render_news(CUIRect main_view); - - // found in menus_demo.cpp - void render_demoplayer(CUIRect main_view); - void render_demolist(CUIRect main_view); - - // found in menus_ingame.cpp - void render_game(CUIRect main_view); - void render_serverinfo(CUIRect main_view); - void render_servercontrol(CUIRect main_view); - void render_servercontrol_kick(CUIRect main_view); - void render_servercontrol_server(CUIRect main_view); - - // found in menus_browser.cpp - int selected_index; - void render_serverbrowser_serverlist(CUIRect view); - void render_serverbrowser_serverdetail(CUIRect view); - void render_serverbrowser_filters(CUIRect view); - void render_serverbrowser(CUIRect main_view); - - // found in menus_settings.cpp - void render_settings_general(CUIRect main_view); - void render_settings_player(CUIRect main_view); - void render_settings_controls(CUIRect main_view); - void render_settings_graphics(CUIRect main_view); - void render_settings_sound(CUIRect main_view); - void render_settings(CUIRect main_view); - - void set_active(bool active); -public: - void render_background(); - - - static MENUS_KEYBINDER binder; - - MENUS(); - - void render_loading(float percent); - - bool is_active() const { return menu_active; } - - virtual void on_init(); - - virtual void on_statechange(int new_state, int old_state); - virtual void on_reset(); - virtual void on_render(); - virtual bool on_input(INPUT_EVENT e); - virtual bool on_mousemove(float x, float y); -}; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index dcf68f8d..0a737052 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1,46 +1,45 @@ +#include <engine/serverbrowser.h> +#include <engine/textrender.h> +#include <engine/keys.h> +#include <engine/shared/config.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> + +#include <game/client/ui.h> +#include <game/client/render.h> +#include "menus.h" +#include <game/localization.h> +#include <game/version.h> + +void CMenus::RenderServerbrowserServerList(CUIRect View) +{ + CUIRect Headers; + CUIRect Status; -#include <string.h> // strcmp, strlen, strncpy -#include <stdlib.h> // atoi - -#include <engine/e_client_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> - -#include <game/client/ui.hpp> -#include <game/client/render.hpp> -#include "menus.hpp" -#include <game/localization.hpp> -#include <game/version.hpp> + View.HSplitTop(ms_ListheaderHeight, &Headers, &View); + View.HSplitBottom(28.0f, &View, &Status); -void MENUS::render_serverbrowser_serverlist(CUIRect view) -{ - CUIRect headers; - CUIRect status; - - view.HSplitTop(listheader_height, &headers, &view); - view.HSplitBottom(28.0f, &view, &status); - // split of the scrollbar - RenderTools()->DrawUIRect(&headers, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); - headers.VSplitRight(20.0f, &headers, 0); - - struct column + RenderTools()->DrawUIRect(&Headers, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + Headers.VSplitRight(20.0f, &Headers, 0); + + struct CColumn { - int id; - int sort; - LOC_CONSTSTRING caption; - int direction; - float width; - int flags; - CUIRect rect; - CUIRect spacer; + int m_Id; + int m_Sort; + CLocConstString m_Caption; + int m_Direction; + float m_Width; + int m_Flags; + CUIRect m_Rect; + CUIRect m_Spacer; }; - + enum { FIXED=1, SPACER=2, - + COL_FLAG_LOCK=0, COL_FLAG_PURE, COL_FLAG_FAV, @@ -51,494 +50,542 @@ void MENUS::render_serverbrowser_serverlist(CUIRect view) COL_PING, COL_VERSION, }; - - static column cols[] = { + + static CColumn s_aCols[] = { {-1, -1, " ", -1, 2.0f, 0, {0}, {0}}, {COL_FLAG_LOCK, -1, " ", -1, 14.0f, 0, {0}, {0}}, {COL_FLAG_PURE, -1, " ", -1, 14.0f, 0, {0}, {0}}, {COL_FLAG_FAV, -1, " ", -1, 14.0f, 0, {0}, {0}}, - {COL_NAME, BROWSESORT_NAME, localize("Name"), 0, 300.0f, 0, {0}, {0}}, - {COL_GAMETYPE, BROWSESORT_GAMETYPE, localize("Type"), 1, 50.0f, 0, {0}, {0}}, - {COL_MAP, BROWSESORT_MAP, localize("Map"), 1, 100.0f, 0, {0}, {0}}, - {COL_PLAYERS, BROWSESORT_NUMPLAYERS, localize("Players"), 1, 60.0f, 0, {0}, {0}}, + {COL_NAME, IServerBrowser::SORT_NAME, Localize("Name"), 0, 300.0f, 0, {0}, {0}}, + {COL_GAMETYPE, IServerBrowser::SORT_GAMETYPE, Localize("Type"), 1, 50.0f, 0, {0}, {0}}, + {COL_MAP, IServerBrowser::SORT_MAP, Localize("Map"), 1, 100.0f, 0, {0}, {0}}, + {COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, Localize("Players"), 1, 60.0f, 0, {0}, {0}}, {-1, -1, " ", 1, 10.0f, 0, {0}, {0}}, - {COL_PING, BROWSESORT_PING, localize("Ping"), 1, 40.0f, FIXED, {0}, {0}}, + {COL_PING, IServerBrowser::SORT_PING, Localize("Ping"), 1, 40.0f, FIXED, {0}, {0}}, }; - - int num_cols = sizeof(cols)/sizeof(column); - + + int NumCols = sizeof(s_aCols)/sizeof(CColumn); + // do layout - for(int i = 0; i < num_cols; i++) + for(int i = 0; i < NumCols; i++) { - if(cols[i].direction == -1) + if(s_aCols[i].m_Direction == -1) { - headers.VSplitLeft(cols[i].width, &cols[i].rect, &headers); - - if(i+1 < num_cols) + Headers.VSplitLeft(s_aCols[i].m_Width, &s_aCols[i].m_Rect, &Headers); + + if(i+1 < NumCols) { - //cols[i].flags |= SPACER; - headers.VSplitLeft(2, &cols[i].spacer, &headers); + //Cols[i].flags |= SPACER; + Headers.VSplitLeft(2, &s_aCols[i].m_Spacer, &Headers); } } } - - for(int i = num_cols-1; i >= 0; i--) + + for(int i = NumCols-1; i >= 0; i--) { - if(cols[i].direction == 1) + if(s_aCols[i].m_Direction == 1) { - headers.VSplitRight(cols[i].width, &headers, &cols[i].rect); - headers.VSplitRight(2, &headers, &cols[i].spacer); + Headers.VSplitRight(s_aCols[i].m_Width, &Headers, &s_aCols[i].m_Rect); + Headers.VSplitRight(2, &Headers, &s_aCols[i].m_Spacer); } } - - for(int i = 0; i < num_cols; i++) + + for(int i = 0; i < NumCols; i++) { - if(cols[i].direction == 0) - cols[i].rect = headers; + if(s_aCols[i].m_Direction == 0) + s_aCols[i].m_Rect = Headers; } - + // do headers - for(int i = 0; i < num_cols; i++) + for(int i = 0; i < NumCols; i++) { - if(DoButton_GridHeader(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect)) + if(DoButton_GridHeader(s_aCols[i].m_Caption, s_aCols[i].m_Caption, g_Config.m_BrSort == s_aCols[i].m_Sort, &s_aCols[i].m_Rect)) { - if(cols[i].sort != -1) + if(s_aCols[i].m_Sort != -1) { - if(config.b_sort == cols[i].sort) - config.b_sort_order ^= 1; + if(g_Config.m_BrSort == s_aCols[i].m_Sort) + g_Config.m_BrSortOrder ^= 1; else - config.b_sort_order = 0; - config.b_sort = cols[i].sort; + g_Config.m_BrSortOrder = 0; + g_Config.m_BrSort = s_aCols[i].m_Sort; } } } - - RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.15f), 0, 0); - - CUIRect scroll; - view.VSplitRight(15, &view, &scroll); - - int num_servers = client_serverbrowse_sorted_num(); - + + RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.15f), 0, 0); + + CUIRect Scroll; + View.VSplitRight(15, &View, &Scroll); + + int NumServers = ServerBrowser()->NumSortedServers(); + // display important messages in the middle of the screen so no // users misses it { - CUIRect msgbox = view; - msgbox.y += view.h/3; - - if(active_page == PAGE_INTERNET && client_serverbrowse_refreshingmasters()) - UI()->DoLabel(&msgbox, localize("Refreshing master servers"), 16.0f, 0); - else if(!client_serverbrowse_num()) - UI()->DoLabel(&msgbox, localize("No servers found"), 16.0f, 0); - else if(client_serverbrowse_num() && !num_servers) - UI()->DoLabel(&msgbox, localize("No servers match your filter criteria"), 16.0f, 0); + CUIRect MsgBox = View; + MsgBox.y += View.h/3; + + if(m_ActivePage == PAGE_INTERNET && ServerBrowser()->IsRefreshingMasters()) + UI()->DoLabel(&MsgBox, Localize("Refreshing master servers"), 16.0f, 0); + else if(!ServerBrowser()->NumServers()) + UI()->DoLabel(&MsgBox, Localize("No servers found"), 16.0f, 0); + else if(ServerBrowser()->NumServers() && !NumServers) + UI()->DoLabel(&MsgBox, Localize("No servers match your filter criteria"), 16.0f, 0); } - int num = (int)(view.h/cols[0].rect.h); - static int scrollbar = 0; - static float scrollvalue = 0; - //static int selected_index = -1; - scroll.HMargin(5.0f, &scroll); - scrollvalue = DoScrollbarV(&scrollbar, &scroll, scrollvalue); - - int scrollnum = num_servers-num+10; - if(scrollnum > 0) + int Num = (int)(View.h/s_aCols[0].m_Rect.h); + static int s_ScrollBar = 0; + static float s_ScrollValue = 0; + + Scroll.HMargin(5.0f, &Scroll); + s_ScrollValue = DoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue); + + int ScrollNum = NumServers-Num+10; + if(ScrollNum > 0) { - if(inp_key_presses(KEY_MOUSE_WHEEL_UP)) - scrollvalue -= 1.0f/scrollnum; - if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN)) - scrollvalue += 1.0f/scrollnum; - - if(scrollvalue < 0) scrollvalue = 0; - if(scrollvalue > 1) scrollvalue = 1; + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue -= 1.0f/ScrollNum; + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + s_ScrollValue += 1.0f/ScrollNum; } else - scrollnum = 0; + ScrollNum = 0; + + if(m_SelectedIndex > -1) + { + for(int i = 0; i < m_NumInputEvents; i++) + { + int NewIndex = -1; + if(m_aInputEvents[i].m_Flags&IInput::FLAG_PRESS) + { + if(m_aInputEvents[i].m_Key == KEY_DOWN) NewIndex = m_SelectedIndex + 1; + if(m_aInputEvents[i].m_Key == KEY_UP) NewIndex = m_SelectedIndex - 1; + } + if(NewIndex > -1 && NewIndex < NumServers) + { + //scroll + if(ScrollNum) + { + if(NewIndex - m_SelectedIndex > 0) + s_ScrollValue += 1.0f/ScrollNum; + else + s_ScrollValue -= 1.0f/ScrollNum; + } + + m_SelectedIndex = NewIndex; + + const CServerInfo *pItem = ServerBrowser()->SortedGet(m_SelectedIndex); + str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress)); + } + } + } + + if(s_ScrollValue < 0) s_ScrollValue = 0; + if(s_ScrollValue > 1) s_ScrollValue = 1; // set clipping - UI()->ClipEnable(&view); - - int start = (int)(scrollnum*scrollvalue); - if(start < 0) - start = 0; - - CUIRect original_view = view; - view.y -= scrollvalue*scrollnum*cols[0].rect.h; - - int new_selected = -1; - int num_players = 0; + UI()->ClipEnable(&View); + + CUIRect OriginalView = View; + View.y -= s_ScrollValue*ScrollNum*s_aCols[0].m_Rect.h; - selected_index = -1; + int NewSelected = -1; + int NumPlayers = 0; - for (int i = 0; i < num_servers; i++) + m_SelectedIndex = -1; + + for (int i = 0; i < NumServers; i++) { - SERVER_INFO *item = client_serverbrowse_sorted_get(i); - num_players += item->num_players; + const CServerInfo *pItem = ServerBrowser()->SortedGet(i); + NumPlayers += pItem->m_NumPlayers; } - - for (int i = 0; i < num_servers; i++) + + for (int i = 0; i < NumServers; i++) { - int item_index = i; - SERVER_INFO *item = client_serverbrowse_sorted_get(item_index); - CUIRect row; - CUIRect select_hit_box; - - int selected = strcmp(item->address, config.ui_server_address) == 0; //selected_index==item_index; - - view.HSplitTop(17.0f, &row, &view); - select_hit_box = row; - - if(selected) - { - selected_index = i; - CUIRect r = row; - r.Margin(1.5f, &r); - RenderTools()->DrawUIRect(&r, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f); - } + int ItemIndex = i; + const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); + CUIRect Row; + CUIRect SelectHitBox; + + int Selected = str_comp(pItem->m_aAddress, g_Config.m_UiServerAddress) == 0; //selected_index==ItemIndex; + View.HSplitTop(17.0f, &Row, &View); + SelectHitBox = Row; + + if(Selected) + m_SelectedIndex = i; // make sure that only those in view can be selected - if(row.y+row.h > original_view.y) + if(Row.y+Row.h > OriginalView.y && Row.y < OriginalView.y+OriginalView.h) { - if(select_hit_box.y < original_view.y) // clip the selection + if(Selected) + { + CUIRect r = Row; + r.Margin(1.5f, &r); + RenderTools()->DrawUIRect(&r, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f); + } + + // clip the selection + if(SelectHitBox.y < OriginalView.y) // top { - select_hit_box.h -= original_view.y-select_hit_box.y; - select_hit_box.y = original_view.y; + SelectHitBox.h -= OriginalView.y-SelectHitBox.y; + SelectHitBox.y = OriginalView.y; } - - if(UI()->DoButtonLogic(item, "", selected, &select_hit_box)) + else if(SelectHitBox.y+SelectHitBox.h > OriginalView.y+OriginalView.h) // bottom + SelectHitBox.h = OriginalView.y+OriginalView.h-SelectHitBox.y; + + if(UI()->DoButtonLogic(pItem, "", Selected, &SelectHitBox)) { - new_selected = item_index; + NewSelected = ItemIndex; } } - - // check if we need to do more - if(row.y > original_view.y+original_view.h) - break; + else + { + // reset active item, if not visible + if(UI()->ActiveItem() == pItem) + UI()->SetActiveItem(0); - for(int c = 0; c < num_cols; c++) + // don't render invisible items + continue; + } + + for(int c = 0; c < NumCols; c++) { - CUIRect button; - char temp[64]; - button.x = cols[c].rect.x; - button.y = row.y; - button.h = row.h; - button.w = cols[c].rect.w; - - //int s = 0; - int id = cols[c].id; - - //s = UI()->DoButton(item, "L", l, &button, ui_draw_browse_icon, 0); - - if(id == COL_FLAG_LOCK) + CUIRect Button; + char aTemp[64]; + Button.x = s_aCols[c].m_Rect.x; + Button.y = Row.y; + Button.h = Row.h; + Button.w = s_aCols[c].m_Rect.w; + + int Id = s_aCols[c].m_Id; + + if(Id == COL_FLAG_LOCK) { - if(item->flags & SRVFLAG_PASSWORD) - DoButton_BrowseIcon(SPRITE_BROWSE_LOCK, &button); + if(pItem->m_Flags & SERVER_FLAG_PASSWORD) + DoButton_BrowseIcon(SPRITE_BROWSE_LOCK, &Button); } - else if(id == COL_FLAG_PURE) + else if(Id == COL_FLAG_PURE) { - if(strcmp(item->gametype, "DM") == 0 || strcmp(item->gametype, "TDM") == 0 || strcmp(item->gametype, "CTF") == 0) + if( str_comp(pItem->m_aGameType, "DM") == 0 || + str_comp(pItem->m_aGameType, "TDM") == 0 || + str_comp(pItem->m_aGameType, "CTF") == 0) { // pure server } else { // unpure - DoButton_BrowseIcon(SPRITE_BROWSE_UNPURE, &button); + DoButton_BrowseIcon(SPRITE_BROWSE_UNPURE, &Button); } } - else if(id == COL_FLAG_FAV) + else if(Id == COL_FLAG_FAV) { - if(item->favorite) - DoButton_BrowseIcon(SPRITE_BROWSE_HEART, &button); + if(pItem->m_Favorite) + DoButton_BrowseIcon(SPRITE_BROWSE_HEART, &Button); } - else if(id == COL_NAME) + else if(Id == COL_NAME) { - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, button.x, button.y, 12.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); - cursor.line_width = button.w; - - if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_SERVERNAME)) + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, Button.x, Button.y, 12.0f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = Button.w; + + if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit&IServerBrowser::QUICK_SERVERNAME)) { // highlight the parts that matches - const char *s = str_find_nocase(item->name, config.b_filter_string); + const char *s = str_find_nocase(pItem->m_aName, g_Config.m_BrFilterString); if(s) { - gfx_text_ex(&cursor, item->name, (int)(s-item->name)); - gfx_text_color(0.4f,0.4f,1.0f,1); - gfx_text_ex(&cursor, s, strlen(config.b_filter_string)); - gfx_text_color(1,1,1,1); - gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1); + TextRender()->TextEx(&Cursor, pItem->m_aName, (int)(s-pItem->m_aName)); + TextRender()->TextColor(0.4f,0.4f,1.0f,1); + TextRender()->TextEx(&Cursor, s, str_length(g_Config.m_BrFilterString)); + TextRender()->TextColor(1,1,1,1); + TextRender()->TextEx(&Cursor, s+str_length(g_Config.m_BrFilterString), -1); } else - gfx_text_ex(&cursor, item->name, -1); + TextRender()->TextEx(&Cursor, pItem->m_aName, -1); } else - gfx_text_ex(&cursor, item->name, -1); + TextRender()->TextEx(&Cursor, pItem->m_aName, -1); } - else if(id == COL_MAP) - UI()->DoLabel(&button, item->map, 12.0f, -1); - else if(id == COL_PLAYERS) + else if(Id == COL_MAP) + UI()->DoLabel(&Button, pItem->m_aMap, 12.0f, -1); + else if(Id == COL_PLAYERS) { - str_format(temp, sizeof(temp), "%i/%i", item->num_players, item->max_players); - if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_PLAYERNAME)) - gfx_text_color(0.4f,0.4f,1.0f,1); - UI()->DoLabel(&button, temp, 12.0f, 1); - gfx_text_color(1,1,1,1); + str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumPlayers, pItem->m_MaxPlayers); + if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit&IServerBrowser::QUICK_PLAYERNAME)) + TextRender()->TextColor(0.4f,0.4f,1.0f,1); + UI()->DoLabel(&Button, aTemp, 12.0f, 1); + TextRender()->TextColor(1,1,1,1); } - else if(id == COL_PING) + else if(Id == COL_PING) { - str_format(temp, sizeof(temp), "%i", item->latency); - UI()->DoLabel(&button, temp, 12.0f, 1); + str_format(aTemp, sizeof(aTemp), "%i", pItem->m_Latency); + UI()->DoLabel(&Button, aTemp, 12.0f, 1); } - else if(id == COL_VERSION) + else if(Id == COL_VERSION) { - const char *version = item->version; - if(strcmp(version, "0.3 e2d7973c6647a13c") == 0) // TODO: remove me later on - version = "0.3.0"; - UI()->DoLabel(&button, version, 12.0f, 1); - } - else if(id == COL_GAMETYPE) + const char *pVersion = pItem->m_aVersion; + if(str_comp(pVersion, "0.3 e2d7973c6647a13c") == 0) // TODO: remove me later on + pVersion = "0.3.0"; + UI()->DoLabel(&Button, pVersion, 12.0f, 1); + } + else if(Id == COL_GAMETYPE) { - UI()->DoLabel(&button, item->gametype, 12.0f, 0); + UI()->DoLabel(&Button, pItem->m_aGameType, 12.0f, 0); } } } UI()->ClipDisable(); - - if(new_selected != -1) + + if(NewSelected != -1) { // select the new server - SERVER_INFO *item = client_serverbrowse_sorted_get(new_selected); - strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address)); - if(inp_mouse_doubleclick()) - client_connect(config.ui_server_address); + const CServerInfo *pItem = ServerBrowser()->SortedGet(NewSelected); + str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress)); + if(Input()->MouseDoubleClick()) + Client()->Connect(g_Config.m_UiServerAddress); } - RenderTools()->DrawUIRect(&status, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); - status.Margin(5.0f, &status); - + RenderTools()->DrawUIRect(&Status, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + Status.Margin(5.0f, &Status); + // render quick search - CUIRect quicksearch; - status.VSplitLeft(250.0f, &quicksearch, &status); - const char *label = localize("Quick search"); - UI()->DoLabel(&quicksearch, label, 14.0f, -1); - quicksearch.VSplitLeft(gfx_text_width(0, 14.0f, label, -1), 0, &quicksearch); - quicksearch.VSplitLeft(5, 0, &quicksearch); - DoEditBox(&config.b_filter_string, &quicksearch, config.b_filter_string, sizeof(config.b_filter_string), 14.0f); - + CUIRect QuickSearch, Button; + Status.VSplitLeft(260.0f, &QuickSearch, &Status); + const char *pLabel = Localize("Quick search:"); + UI()->DoLabel(&QuickSearch, pLabel, 12.0f, -1); + QuickSearch.VSplitLeft(TextRender()->TextWidth(0, 12.0f, pLabel, -1), 0, &QuickSearch); + QuickSearch.VSplitLeft(5.0f, 0, &QuickSearch); + QuickSearch.VSplitLeft(155.0f, &QuickSearch, &Button); + DoEditBox(&g_Config.m_BrFilterString, &QuickSearch, g_Config.m_BrFilterString, sizeof(g_Config.m_BrFilterString), 12.0f, false, CUI::CORNER_L); + // clear button + { + static int s_ClearButton = 0; + RenderTools()->DrawUIRect(&Button, vec4(1,1,1,0.33f)*ButtonColorMul(&s_ClearButton), CUI::CORNER_R, 3.0f); + UI()->DoLabel(&Button, "x", Button.h*ms_FontmodHeight, 0); + if(UI()->DoButtonLogic(&s_ClearButton, "x", 0, &Button)) + { + g_Config.m_BrFilterString[0] = 0; + UI()->SetActiveItem(&g_Config.m_BrFilterString); + } + } + // render status - char buf[128]; - str_format(buf, sizeof(buf), localize("%d of %d servers, %d players"), client_serverbrowse_sorted_num(), client_serverbrowse_num(), num_players); - status.VSplitRight(gfx_text_width(0, 14.0f, buf, -1), 0, &status); - UI()->DoLabel(&status, buf, 14.0f, -1); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers, %d players"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers(), NumPlayers); + Status.VSplitRight(TextRender()->TextWidth(0, 14.0f, aBuf, -1), 0, &Status); + UI()->DoLabel(&Status, aBuf, 14.0f, -1); } -void MENUS::render_serverbrowser_filters(CUIRect view) +void CMenus::RenderServerbrowserFilters(CUIRect View) { // filters - CUIRect button; + CUIRect Button; - view.HSplitTop(5.0f, 0, &view); - view.VSplitLeft(5.0f, 0, &view); - view.VSplitRight(5.0f, &view, 0); - view.HSplitBottom(5.0f, &view, 0); + View.HSplitTop(5.0f, 0, &View); + View.VSplitLeft(5.0f, 0, &View); + View.VSplitRight(5.0f, &View, 0); + View.HSplitBottom(5.0f, &View, 0); // render filters - view.HSplitTop(20.0f, &button, &view); - if (DoButton_CheckBox(&config.b_filter_empty, localize("Has people playing"), config.b_filter_empty, &button)) - config.b_filter_empty ^= 1; + View.HSplitTop(20.0f, &Button, &View); + if (DoButton_CheckBox(&g_Config.m_BrFilterEmpty, Localize("Has people playing"), g_Config.m_BrFilterEmpty, &Button)) + g_Config.m_BrFilterEmpty ^= 1; - view.HSplitTop(20.0f, &button, &view); - if (DoButton_CheckBox(&config.b_filter_full, localize("Server not full"), config.b_filter_full, &button)) - config.b_filter_full ^= 1; + View.HSplitTop(20.0f, &Button, &View); + if (DoButton_CheckBox(&g_Config.m_BrFilterFull, Localize("Server not full"), g_Config.m_BrFilterFull, &Button)) + g_Config.m_BrFilterFull ^= 1; - view.HSplitTop(20.0f, &button, &view); - if (DoButton_CheckBox(&config.b_filter_pw, localize("No password"), config.b_filter_pw, &button)) - config.b_filter_pw ^= 1; + View.HSplitTop(20.0f, &Button, &View); + if (DoButton_CheckBox(&g_Config.m_BrFilterPw, Localize("No password"), g_Config.m_BrFilterPw, &Button)) + g_Config.m_BrFilterPw ^= 1; - view.HSplitTop(20.0f, &button, &view); - if (DoButton_CheckBox((char *)&config.b_filter_compatversion, localize("Compatible version"), config.b_filter_compatversion, &button)) - config.b_filter_compatversion ^= 1; + View.HSplitTop(20.0f, &Button, &View); + if (DoButton_CheckBox((char *)&g_Config.m_BrFilterCompatversion, Localize("Compatible version"), g_Config.m_BrFilterCompatversion, &Button)) + g_Config.m_BrFilterCompatversion ^= 1; + + View.HSplitTop(20.0f, &Button, &View); + if (DoButton_CheckBox((char *)&g_Config.m_BrFilterPure, Localize("Standard gametype"), g_Config.m_BrFilterPure, &Button)) + g_Config.m_BrFilterPure ^= 1; + + View.HSplitTop(20.0f, &Button, &View); + //button.VSplitLeft(20.0f, 0, &button); + if (DoButton_CheckBox((char *)&g_Config.m_BrFilterPureMap, Localize("Standard map"), g_Config.m_BrFilterPureMap, &Button)) + g_Config.m_BrFilterPureMap ^= 1; - view.HSplitTop(20.0f, &button, &view); - if (DoButton_CheckBox((char *)&config.b_filter_pure, localize("Standard gametype"), config.b_filter_pure, &button)) - config.b_filter_pure ^= 1; - - view.HSplitTop(20.0f, &button, &view); - /*button.VSplitLeft(20.0f, 0, &button);*/ - if (DoButton_CheckBox((char *)&config.b_filter_pure_map, localize("Standard map"), config.b_filter_pure_map, &button)) - config.b_filter_pure_map ^= 1; - - view.HSplitTop(20.0f, &button, &view); - UI()->DoLabel(&button, localize("Game types"), 14.0f, -1); - button.VSplitLeft(95.0f, 0, &button); - button.Margin(1.0f, &button); - DoEditBox(&config.b_filter_gametype, &button, config.b_filter_gametype, sizeof(config.b_filter_gametype), 14.0f); + View.HSplitTop(5.0f, 0, &View); + + View.HSplitTop(19.0f, &Button, &View); + UI()->DoLabel(&Button, Localize("Game types:"), 12.0f, -1); + Button.VSplitLeft(95.0f, 0, &Button); + View.HSplitTop(3.0f, 0, &View); + DoEditBox(&g_Config.m_BrFilterGametype, &Button, g_Config.m_BrFilterGametype, sizeof(g_Config.m_BrFilterGametype), 12.0f); { - view.HSplitTop(20.0f, &button, &view); - CUIRect editbox; - button.VSplitLeft(40.0f, &editbox, &button); - button.VSplitLeft(5.0f, &button, &button); + View.HSplitTop(19.0f, &Button, &View); + CUIRect EditBox; + Button.VSplitRight(50.0f, &Button, &EditBox); + EditBox.VSplitRight(5.0f, &EditBox, 0); - char buf[8]; - str_format(buf, sizeof(buf), "%d", config.b_filter_ping); - DoEditBox(&config.b_filter_ping, &editbox, buf, sizeof(buf), 14.0f); - config.b_filter_ping = atoi(buf); + UI()->DoLabel(&Button, Localize("Maximum ping:"), 12.0f, -1); - UI()->DoLabel(&button, localize("Maximum ping"), 14.0f, -1); + char aBuf[5]; + str_format(aBuf, sizeof(aBuf), "%d", g_Config.m_BrFilterPing); + DoEditBox(&g_Config.m_BrFilterPing, &EditBox, aBuf, sizeof(aBuf), 12.0f); + g_Config.m_BrFilterPing = str_toint(aBuf); } - - view.HSplitBottom(button_height, &view, &button); - static int clear_button = 0; - if(DoButton_Menu(&clear_button, localize("Reset filter"), 0, &button)) + + View.HSplitBottom(ms_ButtonHeight, &View, &Button); + static int s_ClearButton = 0; + if(DoButton_Menu(&s_ClearButton, Localize("Reset filter"), 0, &Button)) { - config.b_filter_full = 0; - config.b_filter_empty = 0; - config.b_filter_pw = 0; - config.b_filter_ping = 999; - config.b_filter_gametype[0] = 0; - config.b_filter_compatversion = 1; - config.b_filter_string[0] = 0; - config.b_filter_pure = 1; + g_Config.m_BrFilterFull = 0; + g_Config.m_BrFilterEmpty = 0; + g_Config.m_BrFilterPw = 0; + g_Config.m_BrFilterPing = 999; + g_Config.m_BrFilterGametype[0] = 0; + g_Config.m_BrFilterCompatversion = 1; + g_Config.m_BrFilterString[0] = 0; + g_Config.m_BrFilterPure = 1; + g_Config.m_BrFilterPureMap = 1; } } -void MENUS::render_serverbrowser_serverdetail(CUIRect view) +void CMenus::RenderServerbrowserServerDetail(CUIRect View) { - CUIRect server_details = view; - CUIRect server_scoreboard, server_header; - - SERVER_INFO *selected_server = client_serverbrowse_sorted_get(selected_index); - + CUIRect ServerDetails = View; + CUIRect ServerScoreBoard, ServerHeader; + + const CServerInfo *pSelectedServer = ServerBrowser()->SortedGet(m_SelectedIndex); + //server_details.VSplitLeft(10.0f, 0x0, &server_details); // split off a piece to use for scoreboard - server_details.HSplitTop(140.0f, &server_details, &server_scoreboard); - server_details.HSplitBottom(10.0f, &server_details, 0x0); + ServerDetails.HSplitTop(140.0f, &ServerDetails, &ServerScoreBoard); + ServerDetails.HSplitBottom(10.0f, &ServerDetails, 0x0); // server details - const float font_size = 12.0f; - server_details.HSplitTop(20.0f, &server_header, &server_details); - RenderTools()->DrawUIRect(&server_header, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); - RenderTools()->DrawUIRect(&server_details, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); - server_header.VSplitLeft(8.0f, 0x0, &server_header); - UI()->DoLabel(&server_header, localize("Server details"), font_size+2.0f, -1); + const float FontSize = 12.0f; + ServerDetails.HSplitTop(20.0f, &ServerHeader, &ServerDetails); + RenderTools()->DrawUIRect(&ServerHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); + RenderTools()->DrawUIRect(&ServerDetails, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); + ServerHeader.VSplitLeft(8.0f, 0x0, &ServerHeader); + UI()->DoLabel(&ServerHeader, Localize("Server details"), FontSize+2.0f, -1); - server_details.VSplitLeft(5.0f, 0x0, &server_details); + ServerDetails.VSplitLeft(5.0f, 0x0, &ServerDetails); - server_details.Margin(3.0f, &server_details); + ServerDetails.Margin(3.0f, &ServerDetails); - if (selected_server) + if (pSelectedServer) { - CUIRect row; - static LOC_CONSTSTRING labels[] = { - localize("Version"), - localize("Game type"), - localize("Ping")}; + CUIRect Row; + static CLocConstString s_aLabels[] = { + Localize("Version"), + Localize("Game type"), + Localize("Ping")}; - CUIRect left_column; - CUIRect right_column; + CUIRect LeftColumn; + CUIRect RightColumn; - // + // { - CUIRect button; - server_details.HSplitBottom(20.0f, &server_details, &button); - static int add_fav_button = 0; - if(DoButton_CheckBox(&add_fav_button, localize("Favorite"), selected_server->favorite, &button)) + CUIRect Button; + ServerDetails.HSplitBottom(20.0f, &ServerDetails, &Button); + static int s_AddFavButton = 0; + if(DoButton_CheckBox(&s_AddFavButton, Localize("Favorite"), pSelectedServer->m_Favorite, &Button)) { - if(selected_server->favorite) - client_serverbrowse_removefavorite(selected_server->netaddr); + if(pSelectedServer->m_Favorite) + ServerBrowser()->RemoveFavorite(pSelectedServer->m_NetAddr); else - client_serverbrowse_addfavorite(selected_server->netaddr); + ServerBrowser()->AddFavorite(pSelectedServer->m_NetAddr); } } - //UI()->DoLabel(&row, temp, font_size, -1); + //UI()->DoLabel(&row, temp, font_size, -1); - server_details.VSplitLeft(5.0f, 0x0, &server_details); - server_details.VSplitLeft(80.0f, &left_column, &right_column); + ServerDetails.VSplitLeft(5.0f, 0x0, &ServerDetails); + ServerDetails.VSplitLeft(80.0f, &LeftColumn, &RightColumn); - for (unsigned int i = 0; i < sizeof(labels) / sizeof(labels[0]); i++) + for (unsigned int i = 0; i < sizeof(s_aLabels) / sizeof(s_aLabels[0]); i++) { - left_column.HSplitTop(15.0f, &row, &left_column); - UI()->DoLabel(&row, labels[i], font_size, -1); + LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn); + UI()->DoLabel(&Row, s_aLabels[i], FontSize, -1); } - right_column.HSplitTop(15.0f, &row, &right_column); - UI()->DoLabel(&row, selected_server->version, font_size, -1); + RightColumn.HSplitTop(15.0f, &Row, &RightColumn); + UI()->DoLabel(&Row, pSelectedServer->m_aVersion, FontSize, -1); - right_column.HSplitTop(15.0f, &row, &right_column); - UI()->DoLabel(&row, selected_server->gametype, font_size, -1); + RightColumn.HSplitTop(15.0f, &Row, &RightColumn); + UI()->DoLabel(&Row, pSelectedServer->m_aGameType, FontSize, -1); - char temp[16]; - str_format(temp, sizeof(temp), "%d", selected_server->latency); - right_column.HSplitTop(15.0f, &row, &right_column); - UI()->DoLabel(&row, temp, font_size, -1); + char aTemp[16]; + str_format(aTemp, sizeof(aTemp), "%d", pSelectedServer->m_Latency); + RightColumn.HSplitTop(15.0f, &Row, &RightColumn); + UI()->DoLabel(&Row, aTemp, FontSize, -1); } - + // server scoreboard - - server_scoreboard.HSplitBottom(10.0f, &server_scoreboard, 0x0); - server_scoreboard.HSplitTop(20.0f, &server_header, &server_scoreboard); - RenderTools()->DrawUIRect(&server_header, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); - RenderTools()->DrawUIRect(&server_scoreboard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); - server_header.VSplitLeft(8.0f, 0x0, &server_header); - UI()->DoLabel(&server_header, localize("Scoreboard"), font_size+2.0f, -1); - server_scoreboard.VSplitLeft(5.0f, 0x0, &server_scoreboard); + ServerScoreBoard.HSplitBottom(10.0f, &ServerScoreBoard, 0x0); + ServerScoreBoard.HSplitTop(20.0f, &ServerHeader, &ServerScoreBoard); + RenderTools()->DrawUIRect(&ServerHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); + RenderTools()->DrawUIRect(&ServerScoreBoard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); + ServerHeader.VSplitLeft(8.0f, 0x0, &ServerHeader); + UI()->DoLabel(&ServerHeader, Localize("Scoreboard"), FontSize+2.0f, -1); - server_scoreboard.Margin(3.0f, &server_scoreboard); + ServerScoreBoard.VSplitLeft(5.0f, 0x0, &ServerScoreBoard); - if (selected_server) + ServerScoreBoard.Margin(3.0f, &ServerScoreBoard); + + if (pSelectedServer) { - for (int i = 0; i < selected_server->num_players; i++) + for (int i = 0; i < pSelectedServer->m_NumPlayers; i++) { - CUIRect row; - char temp[16]; - server_scoreboard.HSplitTop(16.0f, &row, &server_scoreboard); + CUIRect Row; + char aTemp[16]; + ServerScoreBoard.HSplitTop(16.0f, &Row, &ServerScoreBoard); - str_format(temp, sizeof(temp), "%d", selected_server->players[i].score); - UI()->DoLabel(&row, temp, font_size, -1); + str_format(aTemp, sizeof(aTemp), "%d", pSelectedServer->m_aPlayers[i].m_Score); + UI()->DoLabel(&Row, aTemp, FontSize, -1); - row.VSplitLeft(25.0f, 0x0, &row); - - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, row.x, row.y, 12.0f, TEXTFLAG_RENDER); - - const char *name = selected_server->players[i].name; - if(config.b_filter_string[0]) + Row.VSplitLeft(25.0f, 0x0, &Row); + + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, Row.x, Row.y, 12.0f, TEXTFLAG_RENDER); + + const char *pName = pSelectedServer->m_aPlayers[i].m_aName; + if(g_Config.m_BrFilterString[0]) { // highlight the parts that matches - const char *s = str_find_nocase(name, config.b_filter_string); + const char *s = str_find_nocase(pName, g_Config.m_BrFilterString); if(s) { - gfx_text_ex(&cursor, name, (int)(s-name)); - gfx_text_color(0.4f,0.4f,1,1); - gfx_text_ex(&cursor, s, strlen(config.b_filter_string)); - gfx_text_color(1,1,1,1); - gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1); + TextRender()->TextEx(&Cursor, pName, (int)(s-pName)); + TextRender()->TextColor(0.4f,0.4f,1,1); + TextRender()->TextEx(&Cursor, s, str_length(g_Config.m_BrFilterString)); + TextRender()->TextColor(1,1,1,1); + TextRender()->TextEx(&Cursor, s+str_length(g_Config.m_BrFilterString), -1); } else - gfx_text_ex(&cursor, name, -1); + TextRender()->TextEx(&Cursor, pName, -1); } else - gfx_text_ex(&cursor, name, -1); - + TextRender()->TextEx(&Cursor, pName, -1); + } } } -void MENUS::render_serverbrowser(CUIRect main_view) +void CMenus::RenderServerbrowser(CUIRect MainView) { - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); - - CUIRect view; - main_view.Margin(10.0f, &view); - + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); + + CUIRect View; + MainView.Margin(10.0f, &View); + /* +-----------------+ +------+ | | | | @@ -549,100 +596,103 @@ void MENUS::render_serverbrowser(CUIRect main_view) +-----------------+ button status toolbar box */ - - + + //CUIRect filters; - CUIRect status_toolbar; - CUIRect toolbox; - CUIRect button_box; + CUIRect StatusToolBox; + CUIRect ToolBox; + CUIRect ButtonBox; // split off a piece for filters, details and scoreboard - view.VSplitRight(200.0f, &view, &toolbox); - toolbox.HSplitBottom(80.0f, &toolbox, &button_box); - view.HSplitBottom(button_height+5.0f, &view, &status_toolbar); + View.VSplitRight(200.0f, &View, &ToolBox); + ToolBox.HSplitBottom(80.0f, &ToolBox, &ButtonBox); + View.HSplitBottom(ms_ButtonHeight+5.0f, &View, &StatusToolBox); - render_serverbrowser_serverlist(view); - - static int toolbox_page = 0; - - toolbox.VSplitLeft(5.0f, 0, &toolbox); + RenderServerbrowserServerList(View); + + int ToolboxPage = g_Config.m_UiToolboxPage; + + ToolBox.VSplitLeft(5.0f, 0, &ToolBox); // do tabbar { - CUIRect tab_bar; - CUIRect tabbutton0, tabbutton1; - toolbox.HSplitTop(22.0f, &tab_bar, &toolbox); - - tab_bar.VSplitMid(&tabbutton0, &tabbutton1); - tabbutton0.VSplitRight(5.0f, &tabbutton0, 0); - tabbutton1.VSplitLeft(5.0f, 0, &tabbutton1); - - static int filters_tab = 0; - if (DoButton_MenuTab(&filters_tab, localize("Filter"), toolbox_page==0, &tabbutton0, 0)) - toolbox_page = 0; - - static int info_tab = 0; - if (DoButton_MenuTab(&info_tab, localize("Info"), toolbox_page==1, &tabbutton1, 0)) - toolbox_page = 1; + CUIRect TabBar; + CUIRect TabButton0, TabButton1; + ToolBox.HSplitTop(22.0f, &TabBar, &ToolBox); + + TabBar.VSplitMid(&TabButton0, &TabButton1); + //TabButton0.VSplitRight(5.0f, &TabButton0, 0); + //TabButton1.VSplitLeft(5.0f, 0, &TabButton1); + + static int s_FiltersTab = 0; + if (DoButton_MenuTab(&s_FiltersTab, Localize("Filter"), ToolboxPage==0, &TabButton0, CUI::CORNER_TL)) + ToolboxPage = 0; + + static int s_InfoTab = 0; + if (DoButton_MenuTab(&s_InfoTab, Localize("Info"), ToolboxPage==1, &TabButton1, CUI::CORNER_TR)) + ToolboxPage = 1; } - RenderTools()->DrawUIRect(&toolbox, vec4(0,0,0,0.15f), 0, 0); - - toolbox.HSplitTop(5.0f, 0, &toolbox); - - if(toolbox_page == 0) - render_serverbrowser_filters(toolbox); - else if(toolbox_page == 1) - render_serverbrowser_serverdetail(toolbox); + g_Config.m_UiToolboxPage = ToolboxPage; + + RenderTools()->DrawUIRect(&ToolBox, vec4(0,0,0,0.15f), 0, 0); + + ToolBox.HSplitTop(5.0f, 0, &ToolBox); + + if(ToolboxPage == 0) + RenderServerbrowserFilters(ToolBox); + else if(ToolboxPage == 1) + RenderServerbrowserServerDetail(ToolBox); { - status_toolbar.HSplitTop(5.0f, 0, &status_toolbar); - - CUIRect button; + StatusToolBox.HSplitTop(5.0f, 0, &StatusToolBox); + + CUIRect Button; //buttons.VSplitRight(20.0f, &buttons, &button); - status_toolbar.VSplitRight(110.0f, &status_toolbar, &button); - button.VMargin(2.0f, &button); - static int refresh_button = 0; - if(DoButton_Menu(&refresh_button, localize("Refresh"), 0, &button)) + StatusToolBox.VSplitRight(110.0f, &StatusToolBox, &Button); + Button.VMargin(2.0f, &Button); + static int s_RefreshButton = 0; + if(DoButton_Menu(&s_RefreshButton, Localize("Refresh"), 0, &Button)) { - if(config.ui_page == PAGE_INTERNET) - client_serverbrowse_refresh(BROWSETYPE_INTERNET); - else if(config.ui_page == PAGE_LAN) - client_serverbrowse_refresh(BROWSETYPE_LAN); - else if(config.ui_page == PAGE_FAVORITES) - client_serverbrowse_refresh(BROWSETYPE_FAVORITES); + if(g_Config.m_UiPage == PAGE_INTERNET) + ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); + else if(g_Config.m_UiPage == PAGE_LAN) + ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); + else if(g_Config.m_UiPage == PAGE_FAVORITES) + ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); } - - char buf[512]; - if(strcmp(client_latestversion(), "0") != 0) - str_format(buf, sizeof(buf), localize("Teeworlds %s is out! Download it at www.teeworlds.com!"), client_latestversion()); + + char aBuf[512]; + if(str_comp(Client()->LatestVersion(), "0") != 0) + str_format(aBuf, sizeof(aBuf), Localize("Teeworlds %s is out! Download it at www.teeworlds.com!"), Client()->LatestVersion()); else - str_format(buf, sizeof(buf), localize("Current version: %s"), GAME_VERSION); - UI()->DoLabel(&status_toolbar, buf, 14.0f, -1); + str_format(aBuf, sizeof(aBuf), Localize("Current version: %s"), GAME_VERSION); + UI()->DoLabel(&StatusToolBox, aBuf, 14.0f, -1); } - + // do the button box { - - button_box.VSplitLeft(5.0f, 0, &button_box); - button_box.VSplitRight(5.0f, &button_box, 0); - - CUIRect button; - button_box.HSplitBottom(button_height, &button_box, &button); - button.VSplitRight(120.0f, 0, &button); - button.VMargin(2.0f, &button); + + ButtonBox.VSplitLeft(5.0f, 0, &ButtonBox); + ButtonBox.VSplitRight(5.0f, &ButtonBox, 0); + + CUIRect Button; + ButtonBox.HSplitBottom(ms_ButtonHeight, &ButtonBox, &Button); + Button.VSplitRight(120.0f, 0, &Button); + Button.VMargin(2.0f, &Button); //button.VMargin(2.0f, &button); - static int join_button = 0; - if(DoButton_Menu(&join_button, localize("Connect"), 0, &button) || enter_pressed) + static int s_JoinButton = 0; + if(DoButton_Menu(&s_JoinButton, Localize("Connect"), 0, &Button) || m_EnterPressed) { - client_connect(config.ui_server_address); - enter_pressed = false; + dbg_msg("", "%s", g_Config.m_UiServerAddress); + Client()->Connect(g_Config.m_UiServerAddress); + m_EnterPressed = false; } - - button_box.HSplitBottom(5.0f, &button_box, &button); - button_box.HSplitBottom(20.0f, &button_box, &button); - DoEditBox(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address), 14.0f); - button_box.HSplitBottom(20.0f, &button_box, &button); - UI()->DoLabel(&button, localize("Host address"), 14.0f, -1); + + ButtonBox.HSplitBottom(5.0f, &ButtonBox, &Button); + ButtonBox.HSplitBottom(20.0f, &ButtonBox, &Button); + DoEditBox(&g_Config.m_UiServerAddress, &Button, g_Config.m_UiServerAddress, sizeof(g_Config.m_UiServerAddress), 14.0f); + ButtonBox.HSplitBottom(20.0f, &ButtonBox, &Button); + UI()->DoLabel(&Button, Localize("Host address"), 14.0f, -1); } } diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index 07019d46..62c03a92 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -1,86 +1,82 @@ -#include <base/math.hpp> +#include <base/math.h> -//#include <string.h> // strcmp, strlen, strncpy -//#include <stdlib.h> // atoi -#include <engine/e_client_interface.h> -#include <game/client/render.hpp> -#include <game/client/gameclient.hpp> +#include <engine/demo.h> +#include <engine/keys.h> -//#include <game/generated/g_protocol.hpp> -//#include <game/generated/gc_data.hpp> +#include <game/client/render.h> +#include <game/client/gameclient.h> +#include <game/localization.h> -#include <game/client/ui.hpp> -//#include <game/client/gameclient.hpp> -//#include <game/client/animstate.hpp> +#include <game/client/ui.h> -#include "menus.hpp" +#include "menus.h" -int MENUS::DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +int CMenus::DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - RenderTools()->DrawUIRect(pRect, vec4(1,1,1, Checked ? 0.10f : 0.5f)*button_color_mul(pID), CUI::CORNER_ALL, 5.0f); + RenderTools()->DrawUIRect(pRect, vec4(1,1,1, Checked ? 0.10f : 0.5f)*ButtonColorMul(pID), CUI::CORNER_ALL, 5.0f); UI()->DoLabel(pRect, pText, 14.0f, 0); return UI()->DoButtonLogic(pID, pText, Checked, pRect); } -void MENUS::render_demoplayer(CUIRect main_view) +void CMenus::RenderDemoPlayer(CUIRect MainView) { - const DEMOPLAYBACK_INFO *info = client_demoplayer_getinfo(); + const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo(); - const float seekbar_height = 15.0f; - const float buttonbar_height = 20.0f; - const float margins = 5.0f; - float total_height; + const float SeekBarHeight = 15.0f; + const float ButtonbarHeight = 20.0f; + const float Margins = 5.0f; + float TotalHeight; - if(menu_active) - total_height = seekbar_height+buttonbar_height+margins*3; + if(m_MenuActive) + TotalHeight = SeekBarHeight+ButtonbarHeight+Margins*3; else - total_height = seekbar_height+margins*2; + TotalHeight = SeekBarHeight+Margins*2; - main_view.HSplitBottom(total_height, 0, &main_view); - main_view.VSplitLeft(250.0f, 0, &main_view); - main_view.VSplitRight(250.0f, &main_view, 0); + MainView.HSplitBottom(TotalHeight, 0, &MainView); + MainView.VSplitLeft(250.0f, 0, &MainView); + MainView.VSplitRight(250.0f, &MainView, 0); - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_T, 10.0f); + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_T, 10.0f); - main_view.Margin(5.0f, &main_view); + MainView.Margin(5.0f, &MainView); - CUIRect seekbar, buttonbar; + CUIRect SeekBar, ButtonBar; - if(menu_active) + if(m_MenuActive) { - main_view.HSplitTop(seekbar_height, &seekbar, &buttonbar); - buttonbar.HSplitTop(margins, 0, &buttonbar); + MainView.HSplitTop(SeekBarHeight, &SeekBar, &ButtonBar); + ButtonBar.HSplitTop(Margins, 0, &ButtonBar); } else - seekbar = main_view; + SeekBar = MainView; // do seekbar { - static int seekbar_id = 0; - void *id = &seekbar_id; - char buffer[128]; + static int s_SeekBarId = 0; + void *id = &s_SeekBarId; + char aBuffer[128]; - RenderTools()->DrawUIRect(&seekbar, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 5.0f); + RenderTools()->DrawUIRect(&SeekBar, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 5.0f); - int current_tick = info->current_tick - info->first_tick; - int total_ticks = info->last_tick - info->first_tick; + int CurrentTick = pInfo->m_CurrentTick - pInfo->m_FirstTick; + int TotalTicks = pInfo->m_LastTick - pInfo->m_FirstTick; - float amount = current_tick/(float)total_ticks; + float Amount = CurrentTick/(float)TotalTicks; - CUIRect filledbar = seekbar; - filledbar.w = 10.0f + (filledbar.w-10.0f)*amount; + CUIRect FilledBar = SeekBar; + FilledBar.w = 10.0f + (FilledBar.w-10.0f)*Amount; - RenderTools()->DrawUIRect(&filledbar, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); + RenderTools()->DrawUIRect(&FilledBar, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); - str_format(buffer, sizeof(buffer), "%d:%02d / %d:%02d", - current_tick/SERVER_TICK_SPEED/60, (current_tick/SERVER_TICK_SPEED)%60, - total_ticks/SERVER_TICK_SPEED/60, (total_ticks/SERVER_TICK_SPEED)%60); - UI()->DoLabel(&seekbar, buffer, seekbar.h*0.70f, 0); + str_format(aBuffer, sizeof(aBuffer), "%d:%02d / %d:%02d", + CurrentTick/SERVER_TICK_SPEED/60, (CurrentTick/SERVER_TICK_SPEED)%60, + TotalTicks/SERVER_TICK_SPEED/60, (TotalTicks/SERVER_TICK_SPEED)%60); + UI()->DoLabel(&SeekBar, aBuffer, SeekBar.h*0.70f, 0); // do the logic - int inside = UI()->MouseInside(&seekbar); + int Inside = UI()->MouseInside(&SeekBar); if(UI()->ActiveItem() == id) { @@ -88,13 +84,15 @@ void MENUS::render_demoplayer(CUIRect main_view) UI()->SetActiveItem(0); else { - float amount = (UI()->MouseX()-seekbar.x)/(float)seekbar.w; - if(amount > 0 && amount < 1.0f) + static float PrevAmount = 0.0f; + float Amount = (UI()->MouseX()-SeekBar.x)/(float)SeekBar.w; + if(Amount > 0 && Amount < 1.0f && PrevAmount != Amount) { - gameclient.on_reset(); - gameclient.suppress_events = true; - client_demoplayer_setpos(amount); - gameclient.suppress_events = false; + PrevAmount = Amount; + m_pClient->OnReset(); + m_pClient->m_SuppressEvents = true; + DemoPlayer()->SetPos(Amount); + m_pClient->m_SuppressEvents = false; } } } @@ -104,334 +102,357 @@ void MENUS::render_demoplayer(CUIRect main_view) UI()->SetActiveItem(id); } - if(inside) + if(Inside) UI()->SetHotItem(id); } - if(menu_active) + if(m_MenuActive) { // do buttons - CUIRect button; + CUIRect Button; // pause button - buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); - static int pause_button = 0; - if(DoButton_DemoPlayer(&pause_button, "| |", info->paused, &button)) - client_demoplayer_setpause(!info->paused); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_PauseButton = 0; + if(DoButton_DemoPlayer(&s_PauseButton, "| |", pInfo->m_Paused, &Button)) + { + if(pInfo->m_Paused) + DemoPlayer()->Unpause(); + else + DemoPlayer()->Pause(); + } // play button - buttonbar.VSplitLeft(margins, 0, &buttonbar); - buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); - static int play_button = 0; - if(DoButton_DemoPlayer(&play_button, ">", !info->paused, &button)) + ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_PlayButton = 0; + if(DoButton_DemoPlayer(&s_PlayButton, ">", !pInfo->m_Paused, &Button)) { - client_demoplayer_setpause(0); - client_demoplayer_setspeed(1.0f); + DemoPlayer()->Unpause(); + DemoPlayer()->SetSpeed(1.0f); } // slowdown - buttonbar.VSplitLeft(margins, 0, &buttonbar); - buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); - static int slowdown_button = 0; - if(DoButton_DemoPlayer(&slowdown_button, "<<", 0, &button)) + ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_SlowDownButton = 0; + if(DoButton_DemoPlayer(&s_SlowDownButton, "<<", 0, &Button)) { - if(info->speed > 4.0f) client_demoplayer_setspeed(4.0f); - else if(info->speed > 2.0f) client_demoplayer_setspeed(2.0f); - else if(info->speed > 1.0f) client_demoplayer_setspeed(1.0f); - else if(info->speed > 0.5f) client_demoplayer_setspeed(0.5f); - else client_demoplayer_setspeed(0.05f); + if(pInfo->m_Speed > 4.0f) DemoPlayer()->SetSpeed(4.0f); + else if(pInfo->m_Speed > 2.0f) DemoPlayer()->SetSpeed(2.0f); + else if(pInfo->m_Speed > 1.0f) DemoPlayer()->SetSpeed(1.0f); + else if(pInfo->m_Speed > 0.5f) DemoPlayer()->SetSpeed(0.5f); + else DemoPlayer()->SetSpeed(0.05f); } // fastforward - buttonbar.VSplitLeft(margins, 0, &buttonbar); - buttonbar.VSplitLeft(buttonbar_height, &button, &buttonbar); - static int fastforward_button = 0; - if(DoButton_DemoPlayer(&fastforward_button, ">>", 0, &button)) + ButtonBar.VSplitLeft(Margins, 0, &ButtonBar); + ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar); + static int s_FastForwardButton = 0; + if(DoButton_DemoPlayer(&s_FastForwardButton, ">>", 0, &Button)) { - if(info->speed < 0.5f) client_demoplayer_setspeed(0.5f); - else if(info->speed < 1.0f) client_demoplayer_setspeed(1.0f); - else if(info->speed < 2.0f) client_demoplayer_setspeed(2.0f); - else if(info->speed < 4.0f) client_demoplayer_setspeed(4.0f); - else client_demoplayer_setspeed(8.0f); + if(pInfo->m_Speed < 0.5f) DemoPlayer()->SetSpeed(0.5f); + else if(pInfo->m_Speed < 1.0f) DemoPlayer()->SetSpeed(1.0f); + else if(pInfo->m_Speed < 2.0f) DemoPlayer()->SetSpeed(2.0f); + else if(pInfo->m_Speed < 4.0f) DemoPlayer()->SetSpeed(4.0f); + else DemoPlayer()->SetSpeed(8.0f); } // speed meter - buttonbar.VSplitLeft(margins*3, 0, &buttonbar); - char buffer[64]; - if(info->speed >= 1.0f) - str_format(buffer, sizeof(buffer), "x%.0f", info->speed); + ButtonBar.VSplitLeft(Margins*3, 0, &ButtonBar); + char aBuffer[64]; + if(pInfo->m_Speed >= 1.0f) + str_format(aBuffer, sizeof(aBuffer), "x%.0f", pInfo->m_Speed); else - str_format(buffer, sizeof(buffer), "x%.1f", info->speed); - UI()->DoLabel(&buttonbar, buffer, button.h*0.7f, -1); + str_format(aBuffer, sizeof(aBuffer), "x%.1f", pInfo->m_Speed); + UI()->DoLabel(&ButtonBar, aBuffer, Button.h*0.7f, -1); // close button - buttonbar.VSplitRight(buttonbar_height*3, &buttonbar, &button); - static int exit_button = 0; - if(DoButton_DemoPlayer(&exit_button, localize("Close"), 0, &button)) - client_disconnect(); + ButtonBar.VSplitRight(ButtonbarHeight*3, &ButtonBar, &Button); + static int s_ExitButton = 0; + if(DoButton_DemoPlayer(&s_ExitButton, Localize("Close"), 0, &Button)) + Client()->Disconnect(); } } -static CUIRect listbox_originalview; -static CUIRect listbox_view; -static float listbox_rowheight; -static int listbox_itemindex; -static int listbox_selected_index; -static int listbox_new_selected; -static int listbox_doneevents; -static int listbox_numitems; - -void MENUS::ui_do_listbox_start(void *id, const CUIRect *rect, float row_height, const char *title, int num_items, int selected_index) +static CUIRect gs_ListBoxOriginalView; +static CUIRect gs_ListBoxView; +static float gs_ListBoxRowHeight; +static int gs_ListBoxItemIndex; +static int gs_ListBoxSelectedIndex; +static int gs_ListBoxNewSelected; +static int gs_ListBoxDoneEvents; +static int gs_ListBoxNumItems; +static int gs_ListBoxItemsPerRow; +static float gs_ListBoxScrollValue; +static bool gs_ListBoxItemActivated; + +void CMenus::UiDoListboxStart(void *pId, const CUIRect *pRect, float RowHeight, const char *pTitle, const char *pBottomText, int NumItems, + int ItemsPerRow, int SelectedIndex, float ScrollValue) { - CUIRect scroll, row; - CUIRect view = *rect; - CUIRect header, footer; + CUIRect Scroll, Row; + CUIRect View = *pRect; + CUIRect Header, Footer; // draw header - view.HSplitTop(listheader_height, &header, &view); - RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); - UI()->DoLabel(&header, title, header.h*fontmod_height, 0); + View.HSplitTop(ms_ListheaderHeight, &Header, &View); + RenderTools()->DrawUIRect(&Header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + UI()->DoLabel(&Header, pTitle, Header.h*ms_FontmodHeight, 0); // draw footers - view.HSplitBottom(listheader_height, &view, &footer); - RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); - footer.VSplitLeft(10.0f, 0, &footer); + View.HSplitBottom(ms_ListheaderHeight, &View, &Footer); + RenderTools()->DrawUIRect(&Footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + Footer.VSplitLeft(10.0f, 0, &Footer); + UI()->DoLabel(&Footer, pBottomText, Header.h*ms_FontmodHeight, 0); // background - RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.15f), 0, 0); + RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.15f), 0, 0); // prepare the scroll - view.VSplitRight(15, &view, &scroll); + View.VSplitRight(15, &View, &Scroll); // setup the variables - listbox_originalview = view; - listbox_selected_index = selected_index; - listbox_new_selected = selected_index; - listbox_itemindex = 0; - listbox_rowheight = row_height; - listbox_numitems = num_items; - listbox_doneevents = 0; - //int num_servers = client_serverbrowse_sorted_num(); - + gs_ListBoxOriginalView = View; + gs_ListBoxSelectedIndex = SelectedIndex; + gs_ListBoxNewSelected = SelectedIndex; + gs_ListBoxItemIndex = 0; + gs_ListBoxRowHeight = RowHeight; + gs_ListBoxNumItems = NumItems; + gs_ListBoxItemsPerRow = ItemsPerRow; + gs_ListBoxDoneEvents = 0; + gs_ListBoxScrollValue = ScrollValue; + gs_ListBoxItemActivated = false; // do the scrollbar - view.HSplitTop(listbox_rowheight, &row, 0); + View.HSplitTop(gs_ListBoxRowHeight, &Row, 0); - int num_viewable = (int)(listbox_originalview.h/row.h) + 1; - int num = num_items-num_viewable+1; - if(num < 0) - num = 0; + int NumViewable = (int)(gs_ListBoxOriginalView.h/Row.h) + 1; + int Num = (NumItems+gs_ListBoxItemsPerRow-1)/gs_ListBoxItemsPerRow-NumViewable+1; + if(Num < 0) + Num = 0; + if(Num > 0) + { + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + gs_ListBoxScrollValue -= 1.0f/Num; + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + gs_ListBoxScrollValue += 1.0f/Num; - static float scrollvalue = 0; - scroll.HMargin(5.0f, &scroll); - scrollvalue = DoScrollbarV(id, &scroll, scrollvalue); - - int start = (int)(num*scrollvalue); - if(start < 0) - start = 0; + if(gs_ListBoxScrollValue < 0.0f) gs_ListBoxScrollValue = 0.0f; + if(gs_ListBoxScrollValue > 1.0f) gs_ListBoxScrollValue = 1.0f; + } + + Scroll.HMargin(5.0f, &Scroll); + gs_ListBoxScrollValue = DoScrollbarV(pId, &Scroll, gs_ListBoxScrollValue); // the list - listbox_view = listbox_originalview; - listbox_view.VMargin(5.0f, &listbox_view); - UI()->ClipEnable(&listbox_view); - listbox_view.y -= scrollvalue*num*row.h; + gs_ListBoxView = gs_ListBoxOriginalView; + gs_ListBoxView.VMargin(5.0f, &gs_ListBoxView); + UI()->ClipEnable(&gs_ListBoxView); + gs_ListBoxView.y -= gs_ListBoxScrollValue*Num*Row.h; } -MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextrow() +CMenus::CListboxItem CMenus::UiDoListboxNextRow() { - LISTBOXITEM item = {0}; - listbox_view.HSplitTop(listbox_rowheight /*-2.0f*/, &item.rect, &listbox_view); - item.visible = 1; + static CUIRect s_RowView; + CListboxItem Item = {0}; + if(gs_ListBoxItemIndex%gs_ListBoxItemsPerRow == 0) + gs_ListBoxView.HSplitTop(gs_ListBoxRowHeight /*-2.0f*/, &s_RowView, &gs_ListBoxView); + + s_RowView.VSplitLeft(s_RowView.w/(gs_ListBoxItemsPerRow-gs_ListBoxItemIndex%gs_ListBoxItemsPerRow), &Item.m_Rect, &s_RowView); + + Item.m_Visible = 1; //item.rect = row; - item.hitrect = item.rect; + Item.m_HitRect = Item.m_Rect; //CUIRect select_hit_box = item.rect; - if(listbox_selected_index == listbox_itemindex) - item.selected = 1; + if(gs_ListBoxSelectedIndex == gs_ListBoxItemIndex) + Item.m_Selected = 1; // make sure that only those in view can be selected - if(item.rect.y+item.rect.h > listbox_originalview.y) + if(Item.m_Rect.y+Item.m_Rect.h > gs_ListBoxOriginalView.y) { - if(item.hitrect.y < item.hitrect.y) // clip the selection + if(Item.m_HitRect.y < Item.m_HitRect.y) // clip the selection { - item.hitrect.h -= listbox_originalview.y-item.hitrect.y; - item.hitrect.y = listbox_originalview.y; + Item.m_HitRect.h -= gs_ListBoxOriginalView.y-Item.m_HitRect.y; + Item.m_HitRect.y = gs_ListBoxOriginalView.y; } } else - item.visible = 0; + Item.m_Visible = 0; // check if we need to do more - if(item.rect.y > listbox_originalview.y+listbox_originalview.h) - item.visible = 0; + if(Item.m_Rect.y > gs_ListBoxOriginalView.y+gs_ListBoxOriginalView.h) + Item.m_Visible = 0; - listbox_itemindex++; - return item; + gs_ListBoxItemIndex++; + return Item; } -MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextitem(void *id) +CMenus::CListboxItem CMenus::UiDoListboxNextItem(void *pId, bool Selected) { - int this_itemindex = listbox_itemindex; - - LISTBOXITEM item = ui_do_listbox_nextrow(); - - if(UI()->DoButtonLogic(id, "", listbox_selected_index == listbox_itemindex, &item.hitrect)) - listbox_new_selected = listbox_itemindex; - - //CUIRect row; - //LISTBOXITEM item = {0}; - //listbox_view.HSplitTop(listbox_rowheight /*-2.0f*/, &row, &listbox_view); - //listbox_view.HSplitTop(2.0f, 0, &listbox_view); - /* - CUIRect select_hit_box = row; - - item.visible = 1; - if(listbox_selected_index == listbox_itemindex) - item.selected = 1; - - // make sure that only those in view can be selected - if(row.y+row.h > listbox_originalview.y) + int ThisItemIndex = gs_ListBoxItemIndex; + if(Selected) { - - if(select_hit_box.y < listbox_originalview.y) // clip the selection - { - select_hit_box.h -= listbox_originalview.y-select_hit_box.y; - select_hit_box.y = listbox_originalview.y; - } - - if(UI()->DoButton(id, "", listbox_selected_index==listbox_itemindex, &select_hit_box, 0, 0)) - listbox_new_selected = listbox_itemindex; + if(gs_ListBoxSelectedIndex == gs_ListBoxNewSelected) + gs_ListBoxNewSelected = ThisItemIndex; + gs_ListBoxSelectedIndex = ThisItemIndex; } - else - item.visible = 0; - - item.rect = row; - */ + CListboxItem Item = UiDoListboxNextRow(); - if(listbox_selected_index == this_itemindex) + if(Item.m_Visible && UI()->DoButtonLogic(pId, "", gs_ListBoxSelectedIndex == gs_ListBoxItemIndex, &Item.m_HitRect)) + gs_ListBoxNewSelected = ThisItemIndex; + + // process input, regard selected index + if(gs_ListBoxSelectedIndex == ThisItemIndex) { - if(!listbox_doneevents) + if(!gs_ListBoxDoneEvents) { - listbox_doneevents = 1; - - for(int i = 0; i < num_inputevents; i++) + gs_ListBoxDoneEvents = 1; + + if(m_EnterPressed || (Input()->MouseDoubleClick() && UI()->ActiveItem() == pId)) { - if(inputevents[i].flags&INPFLAG_PRESS) + gs_ListBoxItemActivated = true; + } + else + { + for(int i = 0; i < m_NumInputEvents; i++) { - if(inputevents[i].key == KEY_DOWN) listbox_new_selected++; - if(inputevents[i].key == KEY_UP) listbox_new_selected--; + int NewIndex = -1; + if(m_aInputEvents[i].m_Flags&IInput::FLAG_PRESS) + { + if(m_aInputEvents[i].m_Key == KEY_DOWN) NewIndex = gs_ListBoxNewSelected + 1; + if(m_aInputEvents[i].m_Key == KEY_UP) NewIndex = gs_ListBoxNewSelected - 1; + } + if(NewIndex > -1 && NewIndex < gs_ListBoxNumItems) + { + // scroll + int NumViewable = (int)(gs_ListBoxOriginalView.h/gs_ListBoxRowHeight) + 1; + int ScrollNum = (gs_ListBoxNumItems+gs_ListBoxItemsPerRow-1)/gs_ListBoxItemsPerRow-NumViewable+1; + if(ScrollNum > 0 && NewIndex/gs_ListBoxItemsPerRow-gs_ListBoxSelectedIndex/gs_ListBoxItemsPerRow) + { + // TODO: make the scrolling better + if(NewIndex - gs_ListBoxSelectedIndex > 0) + gs_ListBoxScrollValue += 1.0f/ScrollNum; + else + gs_ListBoxScrollValue -= 1.0f/ScrollNum; + if(gs_ListBoxScrollValue < 0.0f) gs_ListBoxScrollValue = 0.0f; + if(gs_ListBoxScrollValue > 1.0f) gs_ListBoxScrollValue = 1.0f; + } + + gs_ListBoxNewSelected = NewIndex; + } } } - - if(listbox_new_selected >= listbox_numitems) - listbox_new_selected = listbox_numitems-1; - if(listbox_new_selected < 0) - listbox_new_selected = 0; } //selected_index = i; - CUIRect r = item.rect; + CUIRect r = Item.m_Rect; r.Margin(1.5f, &r); RenderTools()->DrawUIRect(&r, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f); } - //listbox_itemindex++; - return item; + return Item; } -int MENUS::ui_do_listbox_end() +int CMenus::UiDoListboxEnd(float *pScrollValue, bool *pItemActivated) { UI()->ClipDisable(); - return listbox_new_selected; + if(pScrollValue) + *pScrollValue = gs_ListBoxScrollValue; + if(pItemActivated) + *pItemActivated = gs_ListBoxItemActivated; + return gs_ListBoxNewSelected; } struct FETCH_CALLBACKINFO { - MENUS *self; - const char *prefix; - int count; + CMenus *m_pSelf; + const char *m_pPrefix; + int m_Count; }; -void MENUS::demolist_fetch_callback(const char *name, int is_dir, void *user) +void CMenus::DemolistFetchCallback(const char *pName, int IsDir, void *pUser) { - if(is_dir || name[0] == '.') + if(IsDir || pName[0] == '.') return; - FETCH_CALLBACKINFO *info = (FETCH_CALLBACKINFO *)user; + FETCH_CALLBACKINFO *pInfo = (FETCH_CALLBACKINFO *)pUser; - DEMOITEM item; - str_format(item.filename, sizeof(item.filename), "%s/%s", info->prefix, name); - str_copy(item.name, name, sizeof(item.name)); - info->self->demos.add(item); + CDemoItem Item; + str_format(Item.m_aFilename, sizeof(Item.m_aFilename), "%s/%s", pInfo->m_pPrefix, pName); + str_copy(Item.m_aName, pName, sizeof(Item.m_aName)); + pInfo->m_pSelf->m_lDemos.add(Item); } -void MENUS::demolist_populate() +void CMenus::DemolistPopulate() { - demos.clear(); + m_lDemos.clear(); - char buf[512]; - str_format(buf, sizeof(buf), "%s/demos", client_user_directory()); + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "%s/demos", Client()->UserDirectory()); - FETCH_CALLBACKINFO info = {this, buf, 0}; - fs_listdir(buf, demolist_fetch_callback, &info); - info.prefix = "demos"; - fs_listdir("demos", demolist_fetch_callback, &info); + FETCH_CALLBACKINFO Info = {this, aBuf, 0}; + fs_listdir(aBuf, DemolistFetchCallback, &Info); + Info.m_pPrefix = "demos"; + fs_listdir("demos", DemolistFetchCallback, &Info); } -void MENUS::render_demolist(CUIRect main_view) +void CMenus::RenderDemoList(CUIRect MainView) { - static int inited = 0; - if(!inited) - demolist_populate(); - inited = 1; + static int s_Inited = 0; + if(!s_Inited) + DemolistPopulate(); + s_Inited = 1; // render background - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); - main_view.Margin(10.0f, &main_view); + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); + MainView.Margin(10.0f, &MainView); - CUIRect buttonbar; - main_view.HSplitBottom(button_height+5.0f, &main_view, &buttonbar); - buttonbar.HSplitTop(5.0f, 0, &buttonbar); + CUIRect ButtonBar; + MainView.HSplitBottom(ms_ButtonHeight+5.0f, &MainView, &ButtonBar); + ButtonBar.HSplitTop(5.0f, 0, &ButtonBar); - static int selected_item = -1; - static int demolist_id = 0; + static int s_SelectedItem = -1; + static int s_DemoListId = 0; + static float s_ScrollValue = 0; - ui_do_listbox_start(&demolist_id, &main_view, 17.0f, localize("Demos"), demos.size(), selected_item); + UiDoListboxStart(&s_DemoListId, &MainView, 17.0f, Localize("Demos"), "", m_lDemos.size(), 1, s_SelectedItem, s_ScrollValue); //for(int i = 0; i < num_demos; i++) - for(sorted_array<DEMOITEM>::range r = demos.all(); !r.empty(); r.pop_front()) + for(sorted_array<CDemoItem>::range r = m_lDemos.all(); !r.empty(); r.pop_front()) { - LISTBOXITEM item = ui_do_listbox_nextitem((void*)(&r.front())); - if(item.visible) - UI()->DoLabel(&item.rect, r.front().name, item.rect.h*fontmod_height, -1); + CListboxItem Item = UiDoListboxNextItem((void*)(&r.front())); + if(Item.m_Visible) + UI()->DoLabel(&Item.m_Rect, r.front().m_aName, Item.m_Rect.h*ms_FontmodHeight, -1); } - selected_item = ui_do_listbox_end(); - - CUIRect refresh_rect, play_rect; - buttonbar.VSplitRight(250.0f, &buttonbar, &refresh_rect); - refresh_rect.VSplitRight(130.0f, &refresh_rect, &play_rect); - play_rect.VSplitRight(120.0f, 0x0, &play_rect); + bool Activated = false; + s_SelectedItem = UiDoListboxEnd(&s_ScrollValue, &Activated); - static int refresh_button = 0; - if(DoButton_Menu(&refresh_button, localize("Refresh"), 0, &refresh_rect)) - { - demolist_populate(); - } + CUIRect RefreshRect, PlayRect; + ButtonBar.VSplitRight(250.0f, &ButtonBar, &RefreshRect); + RefreshRect.VSplitRight(130.0f, &RefreshRect, &PlayRect); + PlayRect.VSplitRight(120.0f, 0x0, &PlayRect); - static int play_button = 0; - if(DoButton_Menu(&play_button, localize("Play"), 0, &play_rect)) + static int s_RefreshButton = 0; + if(DoButton_Menu(&s_RefreshButton, Localize("Refresh"), 0, &RefreshRect)) { - if(selected_item >= 0 && selected_item < demos.size()) + DemolistPopulate(); + } + + static int s_PlayButton = 0; + if(DoButton_Menu(&s_PlayButton, Localize("Play"), 0, &PlayRect) || Activated) + { + if(s_SelectedItem >= 0 && s_SelectedItem < m_lDemos.size()) { - const char *error = client_demoplayer_play(demos[selected_item].filename); - if(error) - popup_message(localize("Error"), error, localize("Ok")); + const char *pError = Client()->DemoPlayer_Play(m_lDemos[s_SelectedItem].m_aFilename); + if(pError) + PopupMessage(Localize("Error"), pError, Localize("Ok")); } } diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index d31a15bd..7d1f2513 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -1,89 +1,90 @@ -#include <base/math.hpp> +#include <base/math.h> -#include <string.h> // strcmp, strlen, strncpy -#include <stdlib.h> // atoi -#include <engine/e_client_interface.h> +#include <engine/serverbrowser.h> +#include <engine/textrender.h> +#include <engine/shared/config.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/client/ui.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/animstate.hpp> +#include <game/client/ui.h> +#include <game/client/gameclient.h> +#include <game/client/animstate.h> +#include <game/localization.h> -#include "menus.hpp" -#include "motd.hpp" -#include "voting.hpp" +#include "menus.h" +#include "motd.h" +#include "voting.h" -void MENUS::render_game(CUIRect main_view) +void CMenus::RenderGame(CUIRect MainView) { - CUIRect button; + CUIRect Button; //CUIRect votearea; - main_view.HSplitTop(45.0f, &main_view, 0); - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); + MainView.HSplitTop(45.0f, &MainView, 0); + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); - main_view.HSplitTop(10.0f, 0, &main_view); - main_view.HSplitTop(25.0f, &main_view, 0); - main_view.VMargin(10.0f, &main_view); + MainView.HSplitTop(10.0f, 0, &MainView); + MainView.HSplitTop(25.0f, &MainView, 0); + MainView.VMargin(10.0f, &MainView); - main_view.VSplitRight(120.0f, &main_view, &button); - static int disconnect_button = 0; - if(DoButton_Menu(&disconnect_button, localize("Disconnect"), 0, &button)) - client_disconnect(); + MainView.VSplitRight(120.0f, &MainView, &Button); + static int s_DisconnectButton = 0; + if(DoButton_Menu(&s_DisconnectButton, Localize("Disconnect"), 0, &Button)) + Client()->Disconnect(); - if(gameclient.snap.local_info && gameclient.snap.gameobj) + if(m_pClient->m_Snap.m_pLocalInfo && m_pClient->m_Snap.m_pGameobj) { - if(gameclient.snap.local_info->team != -1) + if(m_pClient->m_Snap.m_pLocalInfo->m_Team != -1) { - main_view.VSplitLeft(10.0f, &button, &main_view); - main_view.VSplitLeft(120.0f, &button, &main_view); - static int spectate_button = 0; - if(DoButton_Menu(&spectate_button, localize("Spectate"), 0, &button)) + MainView.VSplitLeft(10.0f, &Button, &MainView); + MainView.VSplitLeft(120.0f, &Button, &MainView); + static int s_SpectateButton = 0; + if(DoButton_Menu(&s_SpectateButton, Localize("Spectate"), 0, &Button)) { - gameclient.send_switch_team(-1); - set_active(false); + m_pClient->SendSwitchTeam(-1); + SetActive(false); } } - if(gameclient.snap.gameobj->flags & GAMEFLAG_TEAMS) + if(m_pClient->m_Snap.m_pGameobj->m_Flags & GAMEFLAG_TEAMS) { - if(gameclient.snap.local_info->team != 0) + if(m_pClient->m_Snap.m_pLocalInfo->m_Team != 0) { - main_view.VSplitLeft(10.0f, &button, &main_view); - main_view.VSplitLeft(120.0f, &button, &main_view); - static int spectate_button = 0; - if(DoButton_Menu(&spectate_button, localize("Join red"), 0, &button)) + MainView.VSplitLeft(10.0f, &Button, &MainView); + MainView.VSplitLeft(120.0f, &Button, &MainView); + static int s_SpectateButton = 0; + if(DoButton_Menu(&s_SpectateButton, Localize("Join red"), 0, &Button)) { - gameclient.send_switch_team(0); - set_active(false); + m_pClient->SendSwitchTeam(0); + SetActive(false); } } - if(gameclient.snap.local_info->team != 1) + if(m_pClient->m_Snap.m_pLocalInfo->m_Team != 1) { - main_view.VSplitLeft(10.0f, &button, &main_view); - main_view.VSplitLeft(120.0f, &button, &main_view); - static int spectate_button = 0; - if(DoButton_Menu(&spectate_button, localize("Join blue"), 0, &button)) + MainView.VSplitLeft(10.0f, &Button, &MainView); + MainView.VSplitLeft(120.0f, &Button, &MainView); + static int s_SpectateButton = 0; + if(DoButton_Menu(&s_SpectateButton, Localize("Join blue"), 0, &Button)) { - gameclient.send_switch_team(1); - set_active(false); + m_pClient->SendSwitchTeam(1); + SetActive(false); } } } else { - if(gameclient.snap.local_info->team != 0) + if(m_pClient->m_Snap.m_pLocalInfo->m_Team != 0) { - main_view.VSplitLeft(10.0f, &button, &main_view); - main_view.VSplitLeft(120.0f, &button, &main_view); - static int spectate_button = 0; - if(DoButton_Menu(&spectate_button, localize("Join game"), 0, &button)) + MainView.VSplitLeft(10.0f, &Button, &MainView); + MainView.VSplitLeft(120.0f, &Button, &MainView); + static int s_SpectateButton = 0; + if(DoButton_Menu(&s_SpectateButton, Localize("Join game"), 0, &Button)) { - gameclient.send_switch_team(0); - set_active(false); + m_pClient->SendSwitchTeam(0); + SetActive(false); } } } @@ -141,102 +142,102 @@ void MENUS::render_game(CUIRect main_view) }*/ } -void MENUS::render_serverinfo(CUIRect main_view) +void CMenus::RenderServerInfo(CUIRect MainView) { // fetch server info - SERVER_INFO current_server_info; - client_serverinfo(¤t_server_info); + CServerInfo CurrentServerInfo; + Client()->GetServerInfo(&CurrentServerInfo); - if(!gameclient.snap.local_info) + if(!m_pClient->m_Snap.m_pLocalInfo) return; // count players for server info-box - int num_players = 0; - for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) + int NumPlayers = 0; + for(int i = 0; i < Client()->SnapNumItems(IClient::SNAP_CURRENT); i++) { - SNAP_ITEM item; - snap_get_item(SNAP_CURRENT, i, &item); + IClient::CSnapItem Item; + Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); - if(item.type == NETOBJTYPE_PLAYER_INFO) + if(Item.m_Type == NETOBJTYPE_PLAYERINFO) { - num_players++; + NumPlayers++; } } // render background - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_ALL, 10.0f); + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f); - CUIRect view, serverinfo, gameinfo, motd; + CUIRect View, ServerInfo, GameInfo, Motd; float x = 0.0f; float y = 0.0f; - char buf[1024]; + char aBuf[1024]; // set view to use for all sub-modules - main_view.Margin(10.0f, &view); + MainView.Margin(10.0f, &View); - /* serverinfo */ - view.HSplitTop(view.h/2-5.0f, &serverinfo, &motd); - serverinfo.VSplitLeft(view.w/2-5.0f, &serverinfo, &gameinfo); - RenderTools()->DrawUIRect(&serverinfo, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + // serverinfo + View.HSplitTop(View.h/2-5.0f, &ServerInfo, &Motd); + ServerInfo.VSplitLeft(View.w/2-5.0f, &ServerInfo, &GameInfo); + RenderTools()->DrawUIRect(&ServerInfo, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - serverinfo.Margin(5.0f, &serverinfo); + ServerInfo.Margin(5.0f, &ServerInfo); x = 5.0f; y = 0.0f; - gfx_text(0, serverinfo.x+x, serverinfo.y+y, 32, localize("Server info"), 250); + TextRender()->Text(0, ServerInfo.x+x, ServerInfo.y+y, 32, Localize("Server info"), 250); y += 32.0f+5.0f; - mem_zero(buf, sizeof(buf)); + mem_zero(aBuf, sizeof(aBuf)); str_format( - buf, - sizeof(buf), + aBuf, + sizeof(aBuf), "%s\n\n" "%s: %s\n" "%s: %d\n" "%s: %s\n" "%s: %s\n", - current_server_info.name, - localize("Address"), config.ui_server_address, - localize("Ping"), gameclient.snap.local_info->latency, - localize("Version"), current_server_info.version, - localize("Password"), current_server_info.flags&1 ? localize("Yes") : localize("No") + CurrentServerInfo.m_aName, + Localize("Address"), g_Config.m_UiServerAddress, + Localize("Ping"), m_pClient->m_Snap.m_pLocalInfo->m_Latency, + Localize("Version"), CurrentServerInfo.m_aVersion, + Localize("Password"), CurrentServerInfo.m_Flags &1 ? Localize("Yes") : Localize("No") ); - gfx_text(0, serverinfo.x+x, serverinfo.y+y, 20, buf, 250); + TextRender()->Text(0, ServerInfo.x+x, ServerInfo.y+y, 20, aBuf, 250); { - CUIRect button; - int is_favorite = client_serverbrowse_isfavorite(current_server_info.netaddr); - serverinfo.HSplitBottom(20.0f, &serverinfo, &button); - static int add_fav_button = 0; - if(DoButton_CheckBox(&add_fav_button, localize("Favorite"), is_favorite, &button)) + CUIRect Button; + int IsFavorite = ServerBrowser()->IsFavorite(CurrentServerInfo.m_NetAddr); + ServerInfo.HSplitBottom(20.0f, &ServerInfo, &Button); + static int s_AddFavButton = 0; + if(DoButton_CheckBox(&s_AddFavButton, Localize("Favorite"), IsFavorite, &Button)) { - if(is_favorite) - client_serverbrowse_removefavorite(current_server_info.netaddr); + if(IsFavorite) + ServerBrowser()->RemoveFavorite(CurrentServerInfo.m_NetAddr); else - client_serverbrowse_addfavorite(current_server_info.netaddr); + ServerBrowser()->AddFavorite(CurrentServerInfo.m_NetAddr); } } - /* gameinfo */ - gameinfo.VSplitLeft(10.0f, 0x0, &gameinfo); - RenderTools()->DrawUIRect(&gameinfo, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + // gameinfo + GameInfo.VSplitLeft(10.0f, 0x0, &GameInfo); + RenderTools()->DrawUIRect(&GameInfo, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - gameinfo.Margin(5.0f, &gameinfo); + GameInfo.Margin(5.0f, &GameInfo); x = 5.0f; y = 0.0f; - gfx_text(0, gameinfo.x+x, gameinfo.y+y, 32, localize("Game info"), 250); + TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 32, Localize("Game info"), 250); y += 32.0f+5.0f; - mem_zero(buf, sizeof(buf)); + mem_zero(aBuf, sizeof(aBuf)); str_format( - buf, - sizeof(buf), + aBuf, + sizeof(aBuf), "\n\n" "%s: %s\n" "%s: %s\n" @@ -244,153 +245,178 @@ void MENUS::render_serverinfo(CUIRect main_view) "%s: %d\n" "\n" "%s: %d/%d\n", - localize("Game type"), current_server_info.gametype, - localize("Map"), current_server_info.map, - localize("Score limit"), gameclient.snap.gameobj->score_limit, - localize("Time limit"), gameclient.snap.gameobj->time_limit, - localize("Players"), gameclient.snap.num_players, current_server_info.max_players + Localize("Game type"), CurrentServerInfo.m_aGameType, + Localize("Map"), CurrentServerInfo.m_aMap, + Localize("Score limit"), m_pClient->m_Snap.m_pGameobj->m_ScoreLimit, + Localize("Time limit"), m_pClient->m_Snap.m_pGameobj->m_TimeLimit, + Localize("Players"), m_pClient->m_Snap.m_NumPlayers, CurrentServerInfo.m_MaxPlayers ); - gfx_text(0, gameinfo.x+x, gameinfo.y+y, 20, buf, 250); + TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 20, aBuf, 250); - /* motd */ - motd.HSplitTop(10.0f, 0, &motd); - RenderTools()->DrawUIRect(&motd, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - motd.Margin(5.0f, &motd); + // motd + Motd.HSplitTop(10.0f, 0, &Motd); + RenderTools()->DrawUIRect(&Motd, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + Motd.Margin(5.0f, &Motd); y = 0.0f; x = 5.0f; - gfx_text(0, motd.x+x, motd.y+y, 32, localize("MOTD"), -1); + TextRender()->Text(0, Motd.x+x, Motd.y+y, 32, Localize("MOTD"), -1); y += 32.0f+5.0f; - gfx_text(0, motd.x+x, motd.y+y, 16, gameclient.motd->server_motd, (int)motd.w); + TextRender()->Text(0, Motd.x+x, Motd.y+y, 16, m_pClient->m_pMotd->m_aServerMotd, (int)Motd.w); } -static const char *format_command(const char *cmd) +static const char *FormatCommand(const char *pCmd) { - return cmd; + return pCmd; } -void MENUS::render_servercontrol_server(CUIRect main_view) +void CMenus::RenderServerControlServer(CUIRect MainView) { - int num_options = 0; - for(VOTING::VOTEOPTION *option = gameclient.voting->first; option; option = option->next) - num_options++; - - static int votelist = 0; - CUIRect list = main_view; - ui_do_listbox_start(&votelist, &list, 24.0f, localize("Settings"), num_options, callvote_selectedoption); + int NumOptions = 0; + for(CVoting::CVoteOption *pOption = m_pClient->m_pVoting->m_pFirst; pOption; pOption = pOption->m_pNext) + NumOptions++; + + static int s_VoteList = 0; + static float s_ScrollValue = 0; + CUIRect List = MainView; + UiDoListboxStart(&s_VoteList, &List, 24.0f, Localize("Settings"), "", NumOptions, 1, m_CallvoteSelectedOption, s_ScrollValue); - for(VOTING::VOTEOPTION *option = gameclient.voting->first; option; option = option->next) + for(CVoting::CVoteOption *pOption = m_pClient->m_pVoting->m_pFirst; pOption; pOption = pOption->m_pNext) { - LISTBOXITEM item = ui_do_listbox_nextitem(option); + CListboxItem Item = UiDoListboxNextItem(pOption); - if(item.visible) - UI()->DoLabel(&item.rect, format_command(option->command), 16.0f, -1); + if(Item.m_Visible) + UI()->DoLabel(&Item.m_Rect, FormatCommand(pOption->m_aCommand), 16.0f, -1); } - callvote_selectedoption = ui_do_listbox_end(); + m_CallvoteSelectedOption = UiDoListboxEnd(&s_ScrollValue, 0); } -void MENUS::render_servercontrol_kick(CUIRect main_view) +void CMenus::RenderServerControlKick(CUIRect MainView) { // draw header - CUIRect header, footer; - main_view.HSplitTop(20, &header, &main_view); - RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); - UI()->DoLabel(&header, localize("Players"), 18.0f, 0); + CUIRect Header, Footer; + MainView.HSplitTop(20, &Header, &MainView); + RenderTools()->DrawUIRect(&Header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); + UI()->DoLabel(&Header, Localize("Players"), 18.0f, 0); // draw footers - main_view.HSplitBottom(20, &main_view, &footer); - RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); - footer.VSplitLeft(10.0f, 0, &footer); + MainView.HSplitBottom(20, &MainView, &Footer); + RenderTools()->DrawUIRect(&Footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); + Footer.VSplitLeft(10.0f, 0, &Footer); // players - RenderTools()->DrawUIRect(&main_view, vec4(0,0,0,0.15f), 0, 0); - CUIRect list = main_view; + RenderTools()->DrawUIRect(&MainView, vec4(0,0,0,0.15f), 0, 0); + CUIRect List = MainView; for(int i = 0; i < MAX_CLIENTS; i++) { - if(!gameclient.snap.player_infos[i]) + if(!m_pClient->m_Snap.m_paPlayerInfos[i]) continue; - CUIRect button; - list.HSplitTop(button_height, &button, &list); + CUIRect Button; + List.HSplitTop(ms_ButtonHeight, &Button, &List); - if(DoButton_ListRow((char *)&gameclient.snap+i, "", callvote_selectedplayer == i, &button)) - callvote_selectedplayer = i; + if(DoButton_ListRow((char *)&m_pClient->m_Snap+i, "", m_CallvoteSelectedPlayer == i, &Button)) + m_CallvoteSelectedPlayer = i; - TEE_RENDER_INFO info = gameclient.clients[i].render_info; - info.size = button.h; - RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), vec2(button.x+button.h/2, button.y+button.h/2)); + CTeeRenderInfo Info = m_pClient->m_aClients[i].m_RenderInfo; + Info.m_Size = Button.h; + RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, EMOTE_NORMAL, vec2(1,0), vec2(Button.x+Button.h/2, Button.y+Button.h/2)); - button.x += button.h; - UI()->DoLabel(&button, gameclient.clients[i].name, 18.0f, -1); + Button.x += Button.h; + UI()->DoLabel(&Button, m_pClient->m_aClients[i].m_aName, 18.0f, -1); } } -void MENUS::render_servercontrol(CUIRect main_view) +void CMenus::RenderServerControl(CUIRect MainView) { - static int control_page = 0; + static int s_ControlPage = 0; // render background - CUIRect temp, tabbar; - main_view.VSplitRight(120.0f, &main_view, &tabbar); - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_B|CUI::CORNER_TL, 10.0f); - tabbar.HSplitTop(50.0f, &temp, &tabbar); - RenderTools()->DrawUIRect(&temp, color_tabbar_active, CUI::CORNER_R, 10.0f); + CUIRect Temp, TabBar; + MainView.VSplitRight(120.0f, &MainView, &TabBar); + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_B|CUI::CORNER_TL, 10.0f); + TabBar.HSplitTop(50.0f, &Temp, &TabBar); + RenderTools()->DrawUIRect(&Temp, ms_ColorTabbarActive, CUI::CORNER_R, 10.0f); - main_view.HSplitTop(10.0f, 0, &main_view); + MainView.HSplitTop(10.0f, 0, &MainView); - CUIRect button; + CUIRect Button; - const char *tabs[] = { - localize("Settings"), - localize("Kick")}; - int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs)); + const char *paTabs[] = { + Localize("Settings"), + Localize("Kick")}; + int aNumTabs = (int)(sizeof(paTabs)/sizeof(*paTabs)); - for(int i = 0; i < num_tabs; i++) + for(int i = 0; i < aNumTabs; i++) { - tabbar.HSplitTop(10, &button, &tabbar); - tabbar.HSplitTop(26, &button, &tabbar); - if(DoButton_SettingsTab(tabs[i], tabs[i], control_page == i, &button)) + TabBar.HSplitTop(10, &Button, &TabBar); + TabBar.HSplitTop(26, &Button, &TabBar); + if(DoButton_SettingsTab(paTabs[i], paTabs[i], s_ControlPage == i, &Button)) { - control_page = i; - callvote_selectedplayer = -1; - callvote_selectedoption = -1; + s_ControlPage = i; + m_CallvoteSelectedPlayer = -1; + m_CallvoteSelectedOption = -1; } } - main_view.Margin(10.0f, &main_view); - CUIRect bottom; - main_view.HSplitBottom(button_height + 5*2, &main_view, &bottom); - bottom.HMargin(5.0f, &bottom); + MainView.Margin(10.0f, &MainView); + CUIRect Bottom; + MainView.HSplitBottom(ms_ButtonHeight + 5*2, &MainView, &Bottom); + Bottom.HMargin(5.0f, &Bottom); // render page - if(control_page == 0) - render_servercontrol_server(main_view); - else if(control_page == 1) - render_servercontrol_kick(main_view); + if(s_ControlPage == 0) + RenderServerControlServer(MainView); + else if(s_ControlPage == 1) + RenderServerControlKick(MainView); { - CUIRect button; - bottom.VSplitRight(120.0f, &bottom, &button); + CUIRect Button; + Bottom.VSplitRight(120.0f, &Bottom, &Button); - static int callvote_button = 0; - if(DoButton_Menu(&callvote_button, localize("Call vote"), 0, &button)) + static int s_CallVoteButton = 0; + if(DoButton_Menu(&s_CallVoteButton, Localize("Call vote"), 0, &Button)) { - if(control_page == 0) + if(s_ControlPage == 0) { // - gameclient.voting->callvote_option(callvote_selectedoption); + m_pClient->m_pVoting->CallvoteOption(m_CallvoteSelectedOption); /* if(callvote_selectedmap >= 0 && callvote_selectedmap < gameclient.maplist->num()) gameclient.voting->callvote_map(gameclient.maplist->name(callvote_selectedmap));*/ } - else if(control_page == 1) + else if(s_ControlPage == 1) + { + if(m_CallvoteSelectedPlayer >= 0 && m_CallvoteSelectedPlayer < MAX_CLIENTS && + m_pClient->m_Snap.m_paPlayerInfos[m_CallvoteSelectedPlayer]) + { + m_pClient->m_pVoting->CallvoteKick(m_CallvoteSelectedPlayer); + SetActive(false); + } + } + } + + // force vote button (only available when authed in rcon) + if(Client()->RconAuthed()) + { + Bottom.VSplitLeft(120.0f, &Button, &Bottom); + + static int s_ForceVoteButton = 0; + if(DoButton_Menu(&s_ForceVoteButton, Localize("Force vote"), 0, &Button)) { - if(callvote_selectedplayer >= 0 && callvote_selectedplayer < MAX_CLIENTS && - gameclient.snap.player_infos[callvote_selectedplayer]) + if(s_ControlPage == 0) + { + m_pClient->m_pVoting->ForcevoteOption(m_CallvoteSelectedOption); + } + else if(s_ControlPage == 1) { - gameclient.voting->callvote_kick(callvote_selectedplayer); - set_active(false); + if(m_CallvoteSelectedPlayer >= 0 && m_CallvoteSelectedPlayer < MAX_CLIENTS && + m_pClient->m_Snap.m_paPlayerInfos[m_CallvoteSelectedPlayer]) + { + m_pClient->m_pVoting->ForcevoteKick(m_CallvoteSelectedPlayer); + SetActive(false); + } } } } diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 05b4d047..a612ed77 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -1,821 +1,752 @@ -#include <base/math.hpp> +#include <base/math.h> -#include <string.h> // strcmp, strlen, strncpy -#include <stdlib.h> // atoi -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/shared/config.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/client/ui.hpp> -#include <game/client/render.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/animstate.hpp> -#include <game/localization.hpp> +#include <game/client/ui.h> +#include <game/client/render.h> +#include <game/client/gameclient.h> +#include <game/client/animstate.h> +#include <game/localization.h> -#include "binds.hpp" -#include "menus.hpp" -#include "skins.hpp" +#include "binds.h" +#include "menus.h" +#include "skins.h" -MENUS_KEYBINDER MENUS::binder; +CMenusKeyBinder CMenus::m_Binder; -MENUS_KEYBINDER::MENUS_KEYBINDER() +CMenusKeyBinder::CMenusKeyBinder() { - take_key = false; - got_key = false; + m_TakeKey = false; + m_GotKey = false; } -bool MENUS_KEYBINDER::on_input(INPUT_EVENT e) +bool CMenusKeyBinder::OnInput(IInput::CEvent Event) { - if(take_key) + if(m_TakeKey) { - if(e.flags&INPFLAG_PRESS && e.key != KEY_ESCAPE) + if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key != KEY_ESCAPE) { - key = e; - got_key = true; - take_key = false; + m_Key = Event; + m_GotKey = true; + m_TakeKey = false; } return true; } - + return false; } -void MENUS::render_settings_player(CUIRect main_view) +void CMenus::RenderSettingsPlayer(CUIRect MainView) { - CUIRect button; - CUIRect othersection; - main_view.VSplitLeft(300.0f, &main_view, &othersection); - main_view.HSplitTop(20.0f, &button, &main_view); + CUIRect Button; + CUIRect LeftView, RightView; + + MainView.VSplitLeft(MainView.w/2, &LeftView, &RightView); + LeftView.HSplitTop(20.0f, &Button, &LeftView); // render settings - { - char buf[128]; - - main_view.HSplitTop(20.0f, &button, &main_view); - str_format(buf, sizeof(buf), "%s:", localize("Name")); - UI()->DoLabel(&button, buf, 14.0, -1); - button.VSplitLeft(80.0f, 0, &button); - button.VSplitLeft(180.0f, &button, 0); - if(DoEditBox(config.player_name, &button, config.player_name, sizeof(config.player_name), 14.0f)) - need_sendinfo = true; + { + char aBuf[128]; + + LeftView.HSplitTop(20.0f, &Button, &LeftView); + str_format(aBuf, sizeof(aBuf), "%s:", Localize("Name")); + UI()->DoLabel(&Button, aBuf, 14.0, -1); + Button.VSplitLeft(80.0f, 0, &Button); + Button.VSplitLeft(180.0f, &Button, 0); + if(DoEditBox(g_Config.m_PlayerName, &Button, g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName), 14.0f)) + m_NeedSendinfo = true; // extra spacing - main_view.HSplitTop(10.0f, 0, &main_view); + LeftView.HSplitTop(10.0f, 0, &LeftView); - static int dynamic_camera_button = 0; - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&dynamic_camera_button, localize("Dynamic Camera"), config.cl_mouse_deadzone != 0, &button)) + static int s_DynamicCameraButton = 0; + LeftView.HSplitTop(20.0f, &Button, &LeftView); + if(DoButton_CheckBox(&s_DynamicCameraButton, Localize("Dynamic Camera"), g_Config.m_ClMouseDeadzone != 0, &Button)) { - - if(config.cl_mouse_deadzone) + + if(g_Config.m_ClMouseDeadzone) { - config.cl_mouse_followfactor = 0; - config.cl_mouse_max_distance = 400; - config.cl_mouse_deadzone = 0; + g_Config.m_ClMouseFollowfactor = 0; + g_Config.m_ClMouseMaxDistance = 400; + g_Config.m_ClMouseDeadzone = 0; } else { - config.cl_mouse_followfactor = 60; - config.cl_mouse_max_distance = 1000; - config.cl_mouse_deadzone = 300; + g_Config.m_ClMouseFollowfactor = 60; + g_Config.m_ClMouseMaxDistance = 1000; + g_Config.m_ClMouseDeadzone = 300; } } - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.cl_autoswitch_weapons, localize("Switch weapon on pickup"), config.cl_autoswitch_weapons, &button)) - config.cl_autoswitch_weapons ^= 1; - - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.cl_nameplates, localize("Show name plates"), config.cl_nameplates, &button)) - config.cl_nameplates ^= 1; + LeftView.HSplitTop(20.0f, &Button, &LeftView); + if(DoButton_CheckBox(&g_Config.m_ClAutoswitchWeapons, Localize("Switch weapon on pickup"), g_Config.m_ClAutoswitchWeapons, &Button)) + g_Config.m_ClAutoswitchWeapons ^= 1; + + LeftView.HSplitTop(20.0f, &Button, &LeftView); + if(DoButton_CheckBox(&g_Config.m_ClNameplates, Localize("Show name plates"), g_Config.m_ClNameplates, &Button)) + g_Config.m_ClNameplates ^= 1; //if(config.cl_nameplates) { - main_view.HSplitTop(20.0f, &button, &main_view); - button.VSplitLeft(15.0f, 0, &button); - if(DoButton_CheckBox(&config.cl_nameplates_always, localize("Always show name plates"), config.cl_nameplates_always, &button)) - config.cl_nameplates_always ^= 1; + LeftView.HSplitTop(20.0f, &Button, &LeftView); + Button.VSplitLeft(15.0f, 0, &Button); + if(DoButton_CheckBox(&g_Config.m_ClNameplatesAlways, Localize("Always show name plates"), g_Config.m_ClNameplatesAlways, &Button)) + g_Config.m_ClNameplatesAlways ^= 1; } - - main_view.HSplitTop(20.0f, &button, &main_view); - - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.player_color_body, localize("Custom colors"), config.player_use_custom_color, &button)) + + { + const CSkins::CSkin *pOwnSkin = m_pClient->m_pSkins->Get(max(0, m_pClient->m_pSkins->Find(g_Config.m_PlayerSkin))); + + CTeeRenderInfo OwnSkinInfo; + OwnSkinInfo.m_Texture = pOwnSkin->m_OrgTexture; + OwnSkinInfo.m_ColorBody = vec4(1, 1, 1, 1); + OwnSkinInfo.m_ColorFeet = vec4(1, 1, 1, 1); + + if(g_Config.m_PlayerUseCustomColor) + { + OwnSkinInfo.m_ColorBody = m_pClient->m_pSkins->GetColor(g_Config.m_PlayerColorBody); + OwnSkinInfo.m_ColorFeet = m_pClient->m_pSkins->GetColor(g_Config.m_PlayerColorFeet); + OwnSkinInfo.m_Texture = pOwnSkin->m_ColorTexture; + } + + OwnSkinInfo.m_Size = UI()->Scale()*50.0f; + + LeftView.HSplitTop(20.0f, &Button, &LeftView); + LeftView.HSplitTop(20.0f, &Button, &LeftView); + + str_format(aBuf, sizeof(aBuf), "%s:", Localize("Your skin")); + UI()->DoLabel(&Button, aBuf, 14.0, -1); + + CUIRect SkinRect; + LeftView.VSplitLeft(LeftView.w/1.2f, &SkinRect, 0); + SkinRect.HSplitTop(50.0f, &SkinRect, 0); + RenderTools()->DrawUIRect(&SkinRect, vec4(1, 1, 1, 0.25f), CUI::CORNER_ALL, 10.0f); + + Button.VSplitLeft(30.0f, 0, &Button); + Button.HSplitTop(50.0f, 0, &Button); + RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1, 0), vec2(Button.x, Button.y)); + + LeftView.HSplitTop(20.0f, &Button, &LeftView); + Button.HSplitTop(15.0f, 0, &Button); + Button.VSplitLeft(100.0f, 0, &Button); + + str_format(aBuf, sizeof(aBuf), "%s", g_Config.m_PlayerSkin); + UI()->DoLabel(&Button, aBuf, 14.0, -1); + } + + RightView.HSplitTop(20.0f, &Button, &RightView); + RightView.HSplitTop(20.0f, &Button, &RightView); + + if(DoButton_CheckBox(&g_Config.m_PlayerColorBody, Localize("Custom colors"), g_Config.m_PlayerUseCustomColor, &Button)) { - config.player_use_custom_color = config.player_use_custom_color?0:1; - need_sendinfo = true; + g_Config.m_PlayerUseCustomColor = g_Config.m_PlayerUseCustomColor?0:1; + m_NeedSendinfo = true; } - - if(config.player_use_custom_color) + + if(g_Config.m_PlayerUseCustomColor) { - int *colors[2]; - colors[0] = &config.player_color_body; - colors[1] = &config.player_color_feet; - - const char *parts[] = { - localize("Body"), - localize("Feet")}; - const char *labels[] = { - localize("Hue"), - localize("Sat."), - localize("Lht.")}; - static int color_slider[2][3] = {{0}}; + int *paColors[2]; + paColors[0] = &g_Config.m_PlayerColorBody; + paColors[1] = &g_Config.m_PlayerColorFeet; + + const char *paParts[] = { + Localize("Body"), + Localize("Feet")}; + const char *paLabels[] = { + Localize("Hue"), + Localize("Sat."), + Localize("Lht.")}; + static int s_aColorSlider[2][3] = {{0}}; //static float v[2][3] = {{0, 0.5f, 0.25f}, {0, 0.5f, 0.25f}}; - + for(int i = 0; i < 2; i++) { - CUIRect text; - main_view.HSplitTop(20.0f, &text, &main_view); - text.VSplitLeft(15.0f, 0, &text); - UI()->DoLabel(&text, parts[i], 14.0f, -1); - - int prevcolor = *colors[i]; - int color = 0; + CUIRect Text; + RightView.HSplitTop(20.0f, &Text, &RightView); + Text.VSplitLeft(15.0f, 0, &Text); + UI()->DoLabel(&Text, paParts[i], 14.0f, -1); + + int PrevColor = *paColors[i]; + int Color = 0; for(int s = 0; s < 3; s++) { - CUIRect text; - main_view.HSplitTop(19.0f, &button, &main_view); - button.VSplitLeft(30.0f, 0, &button); - button.VSplitLeft(70.0f, &text, &button); - button.VSplitRight(5.0f, &button, 0); - button.HSplitTop(4.0f, 0, &button); - - float k = ((prevcolor>>((2-s)*8))&0xff) / 255.0f; - k = DoScrollbarH(&color_slider[i][s], &button, k); - color <<= 8; - color += clamp((int)(k*255), 0, 255); - UI()->DoLabel(&text, labels[s], 15.0f, -1); - + CUIRect Text; + RightView.HSplitTop(19.0f, &Button, &RightView); + Button.VSplitLeft(30.0f, 0, &Button); + Button.VSplitLeft(70.0f, &Text, &Button); + Button.VSplitRight(5.0f, &Button, 0); + Button.HSplitTop(4.0f, 0, &Button); + + float k = ((PrevColor>>((2-s)*8))&0xff) / 255.0f; + k = DoScrollbarH(&s_aColorSlider[i][s], &Button, k); + Color <<= 8; + Color += clamp((int)(k*255), 0, 255); + UI()->DoLabel(&Text, paLabels[s], 15.0f, -1); + } - - if(*colors[i] != color) - need_sendinfo = true; - - *colors[i] = color; - main_view.HSplitTop(5.0f, 0, &main_view); + + if(*paColors[i] != Color) + m_NeedSendinfo = true; + + *paColors[i] = Color; + RightView.HSplitTop(5.0f, 0, &RightView); } } - + + MainView.HSplitTop(MainView.h/2, 0, &MainView); + // render skinselector - static int skinselector_id = 0; - ui_do_listbox_start(&skinselector_id, &main_view, 50, localize("Skins"), (gameclient.skins->num()+3)/4, 0); + static const int s_MaxSkins = 256; + static const CSkins::CSkin *s_paSkinList[s_MaxSkins]; + static int s_NumSkins = -1; + static float s_ScrollValue = 0; + if(s_NumSkins == -1) + { + mem_zero(s_paSkinList, sizeof(s_paSkinList)); + s_NumSkins = 0; + for(int i = 0; i < m_pClient->m_pSkins->Num() && i < s_MaxSkins; ++i) + { + const CSkins::CSkin *s = m_pClient->m_pSkins->Get(i); + // no special skins + if(s->m_aName[0] == 'x' && s->m_aName[1] == '_') + continue; + s_paSkinList[s_NumSkins++] = s; + } + } - for(int skin_id = 0; skin_id < gameclient.skins->num(); ) + int OldSelected = -1; + UiDoListboxStart(&s_NumSkins , &MainView, 50.0f, Localize("Skins"), "", s_NumSkins, 4, OldSelected, s_ScrollValue); + + for(int i = 0; i < s_NumSkins; ++i) { - LISTBOXITEM item = ui_do_listbox_nextrow(); - CUIRect boxes[4]; - CUIRect first_half, second_half; - item.rect.VSplitMid(&first_half, &second_half); - first_half.VSplitMid(&boxes[0], &boxes[1]); - second_half.VSplitMid(&boxes[2], &boxes[3]); - - for(int i = 0; i < 4 && skin_id < gameclient.skins->num(); i++, skin_id++) + const CSkins::CSkin *s = s_paSkinList[i]; + if(s == 0) + continue; + + if(str_comp(s->m_aName, g_Config.m_PlayerSkin) == 0) + OldSelected = i; + + CListboxItem Item = UiDoListboxNextItem(&s_paSkinList[i], OldSelected == i); + if(Item.m_Visible) { - //CUIRect r = item. - const SKINS::SKIN *s = gameclient.skins->get(skin_id); - - TEE_RENDER_INFO info; - info.texture = s->org_texture; - info.color_body = vec4(1,1,1,1); - info.color_feet = vec4(1,1,1,1); - if(config.player_use_custom_color) + CTeeRenderInfo Info; + Info.m_Texture = s->m_OrgTexture; + Info.m_ColorBody = vec4(1, 1, 1, 1); + Info.m_ColorFeet = vec4(1, 1, 1, 1); + + if(g_Config.m_PlayerUseCustomColor) { - info.color_body = gameclient.skins->get_color(config.player_color_body); - info.color_feet = gameclient.skins->get_color(config.player_color_feet); - info.texture = s->color_texture; + Info.m_ColorBody = m_pClient->m_pSkins->GetColor(g_Config.m_PlayerColorBody); + Info.m_ColorFeet = m_pClient->m_pSkins->GetColor(g_Config.m_PlayerColorFeet); + Info.m_Texture = s->m_ColorTexture; } - - info.size = UI()->Scale()*50.0f; - - CUIRect icon = boxes[i]; //item.rect; - //button.VSplitLeft(50.0f, &icon, &text); - - /*if(UI()->DoButton(s, "", selected, &button, ui_draw_list_row, 0)) - { - config_set_player_skin(&config, s->name); - need_sendinfo = true; - }*/ - - //text.HSplitTop(12.0f, 0, &text); // some margin from the top - //UI()->DoLabel(&text, buf, 18.0f, 0); - - icon.HSplitTop(5.0f, 0, &icon); // some margin from the top - RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); - - if(config.debug) + + Info.m_Size = UI()->Scale()*50.0f; + Item.m_Rect.HSplitTop(5.0f, 0, &Item.m_Rect); // some margin from the top + RenderTools()->RenderTee(CAnimState::GetIdle(), &Info, 0, vec2(1, 0), vec2(Item.m_Rect.x+Item.m_Rect.w/2, Item.m_Rect.y+Item.m_Rect.h/2)); + + if(g_Config.m_Debug) { Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); - Graphics()->SetColor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f); - Graphics()->QuadsDrawTL(icon.x, icon.y, 12, 12); + Graphics()->SetColor(s->m_BloodColor.r, s->m_BloodColor.g, s->m_BloodColor.b, 1.0f); + IGraphics::CQuadItem QuadItem(Item.m_Rect.x, Item.m_Rect.y, 12, 12); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } } } - - int new_selection = ui_do_listbox_end(); - (void)new_selection; - //main_view - } - - // render skinselector - /* - { - - //othersection - } - - // draw header - CUIRect header, footer; - skinselection.HSplitTop(20, &header, &skinselection); - RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); - UI()->DoLabel(&header, localize("Skins"), 18.0f, 0); - - // draw footers - skinselection.HSplitBottom(20, &skinselection, &footer); - RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); - footer.VSplitLeft(10.0f, 0, &footer); - - // modes - RenderTools()->DrawUIRect(&skinselection, vec4(0,0,0,0.15f), 0, 0); - - CUIRect scroll; - skinselection.VSplitRight(15, &skinselection, &scroll); - - CUIRect list = skinselection; - list.HSplitTop(50, &button, &list); - - int num = (int)(skinselection.h/button.h); - static float scrollvalue = 0; - static int scrollbar = 0; - scroll.HMargin(5.0f, &scroll); - scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); - - int start = (int)((gameclient.skins->num()-num)*scrollvalue); - if(start < 0) - start = 0; - - for(int i = start; i < start+num && i < gameclient.skins->num(); i++) - { - const SKINS::SKIN *s = gameclient.skins->get(i); - - // no special skins - if(s->name[0] == 'x' && s->name[1] == '_') - { - num++; - continue; - } - - char buf[128]; - str_format(buf, sizeof(buf), "%s", s->name); - int selected = 0; - if(strcmp(s->name, config.player_skin) == 0) - selected = 1; - - TEE_RENDER_INFO info; - info.texture = s->org_texture; - info.color_body = vec4(1,1,1,1); - info.color_feet = vec4(1,1,1,1); - if(config.player_use_custom_color) - { - info.color_body = gameclient.skins->get_color(config.player_color_body); - info.color_feet = gameclient.skins->get_color(config.player_color_feet); - info.texture = s->color_texture; - } - - info.size = UI()->Scale()*50.0f; - - CUIRect icon; - CUIRect text; - button.VSplitLeft(50.0f, &icon, &text); - - if(UI()->DoButton(s, "", selected, &button, ui_draw_list_row, 0)) - { - config_set_player_skin(&config, s->name); - need_sendinfo = true; - } - text.HSplitTop(12.0f, 0, &text); // some margin from the top - UI()->DoLabel(&text, buf, 18.0f, 0); - - icon.HSplitTop(5.0f, 0, &icon); // some margin from the top - RenderTools()->RenderTee(ANIMSTATE::get_idle(), &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2)); - - if(config.debug) + const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); + if(OldSelected != NewSelected) { - Graphics()->TextureSet(-1); - Graphics()->QuadsBegin(); - Graphics()->SetColor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f); - Graphics()->QuadsDrawTL(icon.x, icon.y, 12, 12); - Graphics()->QuadsEnd(); + mem_copy(g_Config.m_PlayerSkin, s_paSkinList[NewSelected]->m_aName, sizeof(g_Config.m_PlayerSkin)); + m_NeedSendinfo = true; } - - list.HSplitTop(50, &button, &list); - }*/ + } } -typedef void (*assign_func_callback)(CONFIGURATION *config, int value); +typedef void (*pfnAssignFuncCallback)(CConfiguration *pConfig, int Value); -typedef struct +typedef struct { - LOC_CONSTSTRING name; - const char *command; - int keyid; -} KEYINFO; + CLocConstString m_Name; + const char *m_pCommand; + int m_KeyId; +} CKeyInfo; -static KEYINFO keys[] = +static CKeyInfo gs_aKeys[] = { // we need to do localize so the scripts can pickup the string - { localize("Move left"), "+left", 0}, - { localize("Move right"), "+right", 0 }, - { localize("Jump"), "+jump", 0 }, - { localize("Fire"), "+fire", 0 }, - { localize("Hook"), "+hook", 0 }, - { localize("Hammer"), "+weapon1", 0 }, - { localize("Pistol"), "+weapon2", 0 }, - { localize("Shotgun"), "+weapon3", 0 }, - { localize("Grenade"), "+weapon4", 0 }, - { localize("Rifle"), "+weapon5", 0 }, - { localize("Next weapon"), "+nextweapon", 0 }, - { localize("Prev. weapon"), "+prevweapon", 0 }, - { localize("Vote yes"), "vote yes", 0 }, - { localize("Vote no"), "vote no", 0 }, - { localize("Chat"), "chat all", 0 }, - { localize("Team chat"), "chat team", 0 }, - { localize("Emoticon"), "+emote", 0 }, - { localize("Console"), "toggle_local_console", 0 }, - { localize("Remote console"), "toggle_remote_console", 0 }, - { localize("Screenshot"), "screenshot", 0 }, - { localize("Scoreboard"), "+scoreboard", 0 }, + { Localize("Move left"), "+left", 0}, + { Localize("Move right"), "+right", 0 }, + { Localize("Jump"), "+jump", 0 }, + { Localize("Fire"), "+fire", 0 }, + { Localize("Hook"), "+hook", 0 }, + { Localize("Hammer"), "+weapon1", 0 }, + { Localize("Pistol"), "+weapon2", 0 }, + { Localize("Shotgun"), "+weapon3", 0 }, + { Localize("Grenade"), "+weapon4", 0 }, + { Localize("Rifle"), "+weapon5", 0 }, + { Localize("Next weapon"), "+nextweapon", 0 }, + { Localize("Prev. weapon"), "+prevweapon", 0 }, + { Localize("Vote yes"), "vote yes", 0 }, + { Localize("Vote no"), "vote no", 0 }, + { Localize("Chat"), "chat all", 0 }, + { Localize("Team chat"), "chat team", 0 }, + { Localize("Emoticon"), "+emote", 0 }, + { Localize("Console"), "toggle_local_console", 0 }, + { Localize("Remote console"), "toggle_remote_console", 0 }, + { Localize("Screenshot"), "screenshot", 0 }, + { Localize("Scoreboard"), "+scoreboard", 0 }, }; -const int key_count = sizeof(keys) / sizeof(KEYINFO); +const int g_KeyCount = sizeof(gs_aKeys) / sizeof(CKeyInfo); -void MENUS::ui_do_getbuttons(int start, int stop, CUIRect view) +void CMenus::UiDoGetButtons(int Start, int Stop, CUIRect View) { - for (int i = start; i < stop; i++) + for (int i = Start; i < Stop; i++) { - KEYINFO &key = keys[i]; - CUIRect button, label; - view.HSplitTop(20.0f, &button, &view); - button.VSplitLeft(130.0f, &label, &button); - - char buf[64]; - str_format(buf, sizeof(buf), "%s:", (const char *)key.name); - - UI()->DoLabel(&label, key.name, 14.0f, -1); - int oldid = key.keyid; - int newid = DoKeyReader((void *)&keys[i].name, &button, oldid); - if(newid != oldid) + CKeyInfo &Key = gs_aKeys[i]; + CUIRect Button, Label; + View.HSplitTop(20.0f, &Button, &View); + Button.VSplitLeft(130.0f, &Label, &Button); + + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%s:", (const char *)Key.m_Name); + + UI()->DoLabel(&Label, aBuf, 14.0f, -1); + int OldId = Key.m_KeyId; + int NewId = DoKeyReader((void *)&gs_aKeys[i].m_Name, &Button, OldId); + if(NewId != OldId) { - gameclient.binds->bind(oldid, ""); - gameclient.binds->bind(newid, keys[i].command); + m_pClient->m_pBinds->Bind(OldId, ""); + m_pClient->m_pBinds->Bind(NewId, gs_aKeys[i].m_pCommand); } - view.HSplitTop(5.0f, 0, &view); + View.HSplitTop(5.0f, 0, &View); } } -void MENUS::render_settings_controls(CUIRect main_view) +void CMenus::RenderSettingsControls(CUIRect MainView) { // this is kinda slow, but whatever - for(int i = 0; i < key_count; i++) - keys[i].keyid = 0; - - for(int keyid = 0; keyid < KEY_LAST; keyid++) + for(int i = 0; i < g_KeyCount; i++) + gs_aKeys[i].m_KeyId = 0; + + for(int KeyId = 0; KeyId < KEY_LAST; KeyId++) { - const char *bind = gameclient.binds->get(keyid); - if(!bind[0]) + const char *pBind = m_pClient->m_pBinds->Get(KeyId); + if(!pBind[0]) continue; - - for(int i = 0; i < key_count; i++) - if(strcmp(bind, keys[i].command) == 0) + + for(int i = 0; i < g_KeyCount; i++) + if(str_comp(pBind, gs_aKeys[i].m_pCommand) == 0) { - keys[i].keyid = keyid; + gs_aKeys[i].m_KeyId = KeyId; break; } } - CUIRect movement_settings, weapon_settings, voting_settings, chat_settings, misc_settings, reset_button; - main_view.VSplitLeft(main_view.w/2-5.0f, &movement_settings, &voting_settings); - - /* movement settings */ + CUIRect MovementSettings, WeaponSettings, VotingSettings, ChatSettings, MiscSettings, ResetButton; + MainView.VSplitLeft(MainView.w/2-5.0f, &MovementSettings, &VotingSettings); + + // movement settings { - movement_settings.HSplitTop(main_view.h/2-5.0f, &movement_settings, &weapon_settings); - RenderTools()->DrawUIRect(&movement_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - movement_settings.Margin(10.0f, &movement_settings); - - gfx_text(0, movement_settings.x, movement_settings.y, 14, localize("Movement"), -1); - - movement_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &movement_settings); - + MovementSettings.HSplitTop(MainView.h/2-5.0f, &MovementSettings, &WeaponSettings); + RenderTools()->DrawUIRect(&MovementSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + MovementSettings.Margin(10.0f, &MovementSettings); + + TextRender()->Text(0, MovementSettings.x, MovementSettings.y, 14, Localize("Movement"), -1); + + MovementSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &MovementSettings); + { - CUIRect button, label; - movement_settings.HSplitTop(20.0f, &button, &movement_settings); - button.VSplitLeft(130.0f, &label, &button); - UI()->DoLabel(&label, localize("Mouse sens."), 14.0f, -1); - button.HMargin(2.0f, &button); - config.inp_mousesens = (int)(DoScrollbarV(&config.inp_mousesens, &button, (config.inp_mousesens-5)/500.0f)*500.0f)+5; - //*key.key = ui_do_key_reader(key.key, &button, *key.key); - movement_settings.HSplitTop(20.0f, 0, &movement_settings); + CUIRect Button, Label; + MovementSettings.HSplitTop(20.0f, &Button, &MovementSettings); + Button.VSplitLeft(130.0f, &Label, &Button); + UI()->DoLabel(&Label, Localize("Mouse sens."), 14.0f, -1); + Button.HMargin(2.0f, &Button); + g_Config.m_InpMousesens = (int)(DoScrollbarH(&g_Config.m_InpMousesens, &Button, (g_Config.m_InpMousesens-5)/500.0f)*500.0f)+5; + //*key.key = ui_do_key_reader(key.key, &Button, *key.key); + MovementSettings.HSplitTop(20.0f, 0, &MovementSettings); } - - ui_do_getbuttons(0, 5, movement_settings); + + UiDoGetButtons(0, 5, MovementSettings); } - - /* weapon settings */ + + // weapon settings { - weapon_settings.HSplitTop(10.0f, 0, &weapon_settings); - RenderTools()->DrawUIRect(&weapon_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - weapon_settings.Margin(10.0f, &weapon_settings); - - gfx_text(0, weapon_settings.x, weapon_settings.y, 14, localize("Weapon"), -1); - - weapon_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &weapon_settings); - ui_do_getbuttons(5, 12, weapon_settings); + WeaponSettings.HSplitTop(10.0f, 0, &WeaponSettings); + RenderTools()->DrawUIRect(&WeaponSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + WeaponSettings.Margin(10.0f, &WeaponSettings); + + TextRender()->Text(0, WeaponSettings.x, WeaponSettings.y, 14, Localize("Weapon"), -1); + + WeaponSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &WeaponSettings); + UiDoGetButtons(5, 12, WeaponSettings); } - - /* voting settings */ + + // voting settings { - voting_settings.VSplitLeft(10.0f, 0, &voting_settings); - voting_settings.HSplitTop(main_view.h/4-5.0f, &voting_settings, &chat_settings); - RenderTools()->DrawUIRect(&voting_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - voting_settings.Margin(10.0f, &voting_settings); - - gfx_text(0, voting_settings.x, voting_settings.y, 14, localize("Voting"), -1); - - voting_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &voting_settings); - ui_do_getbuttons(12, 14, voting_settings); + VotingSettings.VSplitLeft(10.0f, 0, &VotingSettings); + VotingSettings.HSplitTop(MainView.h/4-5.0f, &VotingSettings, &ChatSettings); + RenderTools()->DrawUIRect(&VotingSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + VotingSettings.Margin(10.0f, &VotingSettings); + + TextRender()->Text(0, VotingSettings.x, VotingSettings.y, 14, Localize("Voting"), -1); + + VotingSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &VotingSettings); + UiDoGetButtons(12, 14, VotingSettings); } - - /* chat settings */ + + // chat settings { - chat_settings.HSplitTop(10.0f, 0, &chat_settings); - chat_settings.HSplitTop(main_view.h/4-10.0f, &chat_settings, &misc_settings); - RenderTools()->DrawUIRect(&chat_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - chat_settings.Margin(10.0f, &chat_settings); - - gfx_text(0, chat_settings.x, chat_settings.y, 14, localize("Chat"), -1); - - chat_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &chat_settings); - ui_do_getbuttons(14, 16, chat_settings); + ChatSettings.HSplitTop(10.0f, 0, &ChatSettings); + ChatSettings.HSplitTop(MainView.h/4-10.0f, &ChatSettings, &MiscSettings); + RenderTools()->DrawUIRect(&ChatSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + ChatSettings.Margin(10.0f, &ChatSettings); + + TextRender()->Text(0, ChatSettings.x, ChatSettings.y, 14, Localize("Chat"), -1); + + ChatSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &ChatSettings); + UiDoGetButtons(14, 16, ChatSettings); } - - /* misc settings */ + + // misc settings { - misc_settings.HSplitTop(10.0f, 0, &misc_settings); - misc_settings.HSplitTop(main_view.h/2-5.0f-45.0f, &misc_settings, &reset_button); - RenderTools()->DrawUIRect(&misc_settings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); - misc_settings.Margin(10.0f, &misc_settings); - - gfx_text(0, misc_settings.x, misc_settings.y, 14, localize("Miscellaneous"), -1); - - misc_settings.HSplitTop(14.0f+5.0f+10.0f, 0, &misc_settings); - ui_do_getbuttons(16, 21, misc_settings); + MiscSettings.HSplitTop(10.0f, 0, &MiscSettings); + MiscSettings.HSplitTop(MainView.h/2-5.0f-45.0f, &MiscSettings, &ResetButton); + RenderTools()->DrawUIRect(&MiscSettings, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f); + MiscSettings.Margin(10.0f, &MiscSettings); + + TextRender()->Text(0, MiscSettings.x, MiscSettings.y, 14, Localize("Miscellaneous"), -1); + + MiscSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &MiscSettings); + UiDoGetButtons(16, 21, MiscSettings); } - + // defaults - reset_button.HSplitTop(10.0f, 0, &reset_button); - static int default_button = 0; - if(DoButton_Menu((void*)&default_button, localize("Reset to defaults"), 0, &reset_button)) - gameclient.binds->set_defaults(); + ResetButton.HSplitTop(10.0f, 0, &ResetButton); + static int s_DefaultButton = 0; + if(DoButton_Menu((void*)&s_DefaultButton, Localize("Reset to defaults"), 0, &ResetButton)) + m_pClient->m_pBinds->SetDefaults(); } -void MENUS::render_settings_graphics(CUIRect main_view) +void CMenus::RenderSettingsGraphics(CUIRect MainView) { - CUIRect button; - char buf[128]; - + CUIRect Button; + char aBuf[128]; + static const int MAX_RESOLUTIONS = 256; - static VIDEO_MODE modes[MAX_RESOLUTIONS]; - static int num_modes = -1; - - if(num_modes == -1) - num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); - - CUIRect modelist; - main_view.VSplitLeft(300.0f, &main_view, &modelist); - + static CVideoMode s_aModes[MAX_RESOLUTIONS]; + static int s_NumNodes = -1; + const static int s_GfxScreenWidth = g_Config.m_GfxScreenWidth; + const static int s_GfxScreenHeight = g_Config.m_GfxScreenHeight; + const static int s_GfxColorDepth = g_Config.m_GfxColorDepth; + + if(s_NumNodes == -1) + s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS); + + CUIRect ModeList; + MainView.VSplitLeft(300.0f, &MainView, &ModeList); + // draw allmodes switch - CUIRect header, footer; - modelist.HSplitTop(20, &button, &modelist); - if(DoButton_CheckBox(&config.gfx_display_all_modes, localize("Show only supported"), config.gfx_display_all_modes^1, &button)) + ModeList.HSplitTop(20, &Button, &ModeList); + if(DoButton_CheckBox(&g_Config.m_GfxDisplayAllModes, Localize("Show only supported"), g_Config.m_GfxDisplayAllModes^1, &Button)) { - config.gfx_display_all_modes ^= 1; - num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS); + g_Config.m_GfxDisplayAllModes ^= 1; + s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS); } - - // draw header - modelist.HSplitTop(20, &header, &modelist); - RenderTools()->DrawUIRect(&header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f); - UI()->DoLabel(&header, localize("Display Modes"), 14.0f, 0); - - // draw footers - modelist.HSplitBottom(20, &modelist, &footer); - str_format(buf, sizeof(buf), "%s: %dx%d %d bit", localize("Current"), config.gfx_screen_width, config.gfx_screen_height, config.gfx_color_depth); - RenderTools()->DrawUIRect(&footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f); - footer.VSplitLeft(10.0f, 0, &footer); - UI()->DoLabel(&footer, buf, 14.0f, -1); - - // modes - RenderTools()->DrawUIRect(&modelist, vec4(0,0,0,0.15f), 0, 0); - - CUIRect scroll; - modelist.VSplitRight(15, &modelist, &scroll); - - CUIRect list = modelist; - list.HSplitTop(20, &button, &list); - - int num = (int)(modelist.h/button.h); - static float scrollvalue = 0; - static int scrollbar = 0; - scroll.HMargin(5.0f, &scroll); - scrollvalue = DoScrollbarV(&scrollbar, &scroll, scrollvalue); - - int start = (int)((num_modes-num)*scrollvalue); - if(start < 0) - start = 0; - - for(int i = start; i < start+num && i < num_modes; i++) + + // display mode list + static float s_ScrollValue = 0; + int OldSelected = -1; + str_format(aBuf, sizeof(aBuf), "%s: %dx%d %d bit", Localize("Current"), s_GfxScreenWidth, s_GfxScreenHeight, s_GfxColorDepth); + UiDoListboxStart(&s_NumNodes , &ModeList, 24.0f, Localize("Display Modes"), aBuf, s_NumNodes, 1, OldSelected, s_ScrollValue); + + for(int i = 0; i < s_NumNodes; ++i) { - int depth = modes[i].red+modes[i].green+modes[i].blue; - if(depth < 16) - depth = 16; - else if(depth > 16) - depth = 24; - - int selected = 0; - if(config.gfx_color_depth == depth && - config.gfx_screen_width == modes[i].width && - config.gfx_screen_height == modes[i].height) + const int Depth = s_aModes[i].m_Red+s_aModes[i].m_Green+s_aModes[i].m_Blue > 16 ? 24 : 16; + if(g_Config.m_GfxColorDepth == Depth && + g_Config.m_GfxScreenWidth == s_aModes[i].m_Width && + g_Config.m_GfxScreenHeight == s_aModes[i].m_Height) { - selected = 1; + OldSelected = i; } - - str_format(buf, sizeof(buf), " %dx%d %d bit", modes[i].width, modes[i].height, depth); - if(DoButton_ListRow(&modes[i], buf, selected, &button)) + + CListboxItem Item = UiDoListboxNextItem(&s_aModes[i], OldSelected == i); + if(Item.m_Visible) { - config.gfx_color_depth = depth; - config.gfx_screen_width = modes[i].width; - config.gfx_screen_height = modes[i].height; - if(!selected) - need_restart = true; + str_format(aBuf, sizeof(aBuf), " %dx%d %d bit", s_aModes[i].m_Width, s_aModes[i].m_Height, Depth); + UI()->DoLabel(&Item.m_Rect, aBuf, 16.0f, -1); } - - list.HSplitTop(20, &button, &list); } - - + + const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); + if(OldSelected != NewSelected) + { + const int Depth = s_aModes[NewSelected].m_Red+s_aModes[NewSelected].m_Green+s_aModes[NewSelected].m_Blue > 16 ? 24 : 16; + g_Config.m_GfxColorDepth = Depth; + g_Config.m_GfxScreenWidth = s_aModes[NewSelected].m_Width; + g_Config.m_GfxScreenHeight = s_aModes[NewSelected].m_Height; + m_NeedRestart = true; + } + // switches - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.gfx_fullscreen, localize("Fullscreen"), config.gfx_fullscreen, &button)) + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_GfxFullscreen, Localize("Fullscreen"), g_Config.m_GfxFullscreen, &Button)) { - config.gfx_fullscreen ^= 1; - need_restart = true; + g_Config.m_GfxFullscreen ^= 1; + m_NeedRestart = true; } - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.gfx_vsync, localize("V-Sync"), config.gfx_vsync, &button)) + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_GfxVsync, Localize("V-Sync"), g_Config.m_GfxVsync, &Button)) { - config.gfx_vsync ^= 1; - need_restart = true; + g_Config.m_GfxVsync ^= 1; + m_NeedRestart = true; } - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox_Number(&config.gfx_fsaa_samples, localize("FSAA samples"), config.gfx_fsaa_samples, &button)) + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox_Number(&g_Config.m_GfxFsaaSamples, Localize("FSAA samples"), g_Config.m_GfxFsaaSamples, &Button)) { - config.gfx_fsaa_samples = (config.gfx_fsaa_samples+1)%17; - need_restart = true; + g_Config.m_GfxFsaaSamples = (g_Config.m_GfxFsaaSamples+1)%17; + m_NeedRestart = true; } - - main_view.HSplitTop(40.0f, &button, &main_view); - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.gfx_texture_quality, localize("Quality Textures"), config.gfx_texture_quality, &button)) + + MainView.HSplitTop(40.0f, &Button, &MainView); + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_GfxTextureQuality, Localize("Quality Textures"), g_Config.m_GfxTextureQuality, &Button)) { - config.gfx_texture_quality ^= 1; - need_restart = true; + g_Config.m_GfxTextureQuality ^= 1; + m_NeedRestart = true; } - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.gfx_texture_compression, localize("Texture Compression"), config.gfx_texture_compression, &button)) + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_GfxTextureCompression, Localize("Texture Compression"), g_Config.m_GfxTextureCompression, &Button)) { - config.gfx_texture_compression ^= 1; - need_restart = true; + g_Config.m_GfxTextureCompression ^= 1; + m_NeedRestart = true; } - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.gfx_high_detail, localize("High Detail"), config.gfx_high_detail, &button)) - config.gfx_high_detail ^= 1; + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_GfxHighDetail, Localize("High Detail"), g_Config.m_GfxHighDetail, &Button)) + g_Config.m_GfxHighDetail ^= 1; // - - CUIRect text; - main_view.HSplitTop(20.0f, 0, &main_view); - main_view.HSplitTop(20.0f, &text, &main_view); + + CUIRect Text; + MainView.HSplitTop(20.0f, 0, &MainView); + MainView.HSplitTop(20.0f, &Text, &MainView); //text.VSplitLeft(15.0f, 0, &text); - UI()->DoLabel(&text, localize("UI Color"), 14.0f, -1); - - const char *labels[] = { - localize("Hue"), - localize("Sat."), - localize("Lht."), - localize("Alpha")}; - int *color_slider[4] = {&config.ui_color_hue, &config.ui_color_sat, &config.ui_color_lht, &config.ui_color_alpha}; + UI()->DoLabel(&Text, Localize("UI Color"), 14.0f, -1); + + const char *paLabels[] = { + Localize("Hue"), + Localize("Sat."), + Localize("Lht."), + Localize("Alpha")}; + int *pColorSlider[4] = {&g_Config.m_UiColorHue, &g_Config.m_UiColorSat, &g_Config.m_UiColorLht, &g_Config.m_UiColorAlpha}; for(int s = 0; s < 4; s++) { - CUIRect text; - main_view.HSplitTop(19.0f, &button, &main_view); - button.VMargin(15.0f, &button); - button.VSplitLeft(50.0f, &text, &button); - button.VSplitRight(5.0f, &button, 0); - button.HSplitTop(4.0f, 0, &button); - - float k = (*color_slider[s]) / 255.0f; - k = DoScrollbarH(color_slider[s], &button, k); - *color_slider[s] = (int)(k*255.0f); - UI()->DoLabel(&text, labels[s], 15.0f, -1); - } + CUIRect Text; + MainView.HSplitTop(19.0f, &Button, &MainView); + Button.VMargin(15.0f, &Button); + Button.VSplitLeft(50.0f, &Text, &Button); + Button.VSplitRight(5.0f, &Button, 0); + Button.HSplitTop(4.0f, 0, &Button); + + float k = (*pColorSlider[s]) / 255.0f; + k = DoScrollbarH(pColorSlider[s], &Button, k); + *pColorSlider[s] = (int)(k*255.0f); + UI()->DoLabel(&Text, paLabels[s], 15.0f, -1); + } } -void MENUS::render_settings_sound(CUIRect main_view) +void CMenus::RenderSettingsSound(CUIRect MainView) { - CUIRect button; - main_view.VSplitLeft(300.0f, &main_view, 0); - - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.snd_enable, localize("Use sounds"), config.snd_enable, &button)) + CUIRect Button; + MainView.VSplitLeft(300.0f, &MainView, 0); + + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_SndEnable, Localize("Use sounds"), g_Config.m_SndEnable, &Button)) { - config.snd_enable ^= 1; - need_restart = true; + g_Config.m_SndEnable ^= 1; + m_NeedRestart = true; } - - if(!config.snd_enable) + + if(!g_Config.m_SndEnable) return; - - main_view.HSplitTop(20.0f, &button, &main_view); - if(DoButton_CheckBox(&config.snd_nonactive_mute, localize("Mute when not active"), config.snd_nonactive_mute, &button)) - config.snd_nonactive_mute ^= 1; - + + MainView.HSplitTop(20.0f, &Button, &MainView); + if(DoButton_CheckBox(&g_Config.m_SndNonactiveMute, Localize("Mute when not active"), g_Config.m_SndNonactiveMute, &Button)) + g_Config.m_SndNonactiveMute ^= 1; + // sample rate box { - char buf[64]; - str_format(buf, sizeof(buf), "%d", config.snd_rate); - main_view.HSplitTop(20.0f, &button, &main_view); - UI()->DoLabel(&button, localize("Sample rate"), 14.0f, -1); - button.VSplitLeft(110.0f, 0, &button); - button.VSplitLeft(180.0f, &button, 0); - DoEditBox(&config.snd_rate, &button, buf, sizeof(buf), 14.0f); - int before = config.snd_rate; - config.snd_rate = atoi(buf); - - if(config.snd_rate != before) - need_restart = true; - - if(config.snd_rate < 1) - config.snd_rate = 1; + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%d", g_Config.m_SndRate); + MainView.HSplitTop(20.0f, &Button, &MainView); + UI()->DoLabel(&Button, Localize("Sample rate"), 14.0f, -1); + Button.VSplitLeft(110.0f, 0, &Button); + Button.VSplitLeft(180.0f, &Button, 0); + DoEditBox(&g_Config.m_SndRate, &Button, aBuf, sizeof(aBuf), 14.0f); + int Before = g_Config.m_SndRate; + g_Config.m_SndRate = str_toint(aBuf); + + if(g_Config.m_SndRate != Before) + m_NeedRestart = true; + + if(g_Config.m_SndRate < 1) + g_Config.m_SndRate = 1; } - + // volume slider { - CUIRect button, label; - main_view.HSplitTop(5.0f, &button, &main_view); - main_view.HSplitTop(20.0f, &button, &main_view); - button.VSplitLeft(110.0f, &label, &button); - button.HMargin(2.0f, &button); - UI()->DoLabel(&label, localize("Sound volume"), 14.0f, -1); - config.snd_volume = (int)(DoScrollbarH(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f); - main_view.HSplitTop(20.0f, 0, &main_view); + CUIRect Button, Label; + MainView.HSplitTop(5.0f, &Button, &MainView); + MainView.HSplitTop(20.0f, &Button, &MainView); + Button.VSplitLeft(110.0f, &Label, &Button); + Button.HMargin(2.0f, &Button); + UI()->DoLabel(&Label, Localize("Sound volume"), 14.0f, -1); + g_Config.m_SndVolume = (int)(DoScrollbarH(&g_Config.m_SndVolume, &Button, g_Config.m_SndVolume/100.0f)*100.0f); + MainView.HSplitTop(20.0f, 0, &MainView); } } struct LANGUAGE { LANGUAGE() {} - LANGUAGE(const char *n, const char *f) : name(n), filename(f) {} - - string name; - string filename; - - bool operator<(const LANGUAGE &other) { return name < other.name; } + LANGUAGE(const char *n, const char *f) : m_Name(n), m_FileName(f) {} + + string m_Name; + string m_FileName; + + bool operator<(const LANGUAGE &Other) { return m_Name < Other.m_Name; } }; -int fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, void *user); +int fs_listdir(const char *pDir, FS_LISTDIR_CALLBACK cb, void *pUser); -void gather_languages(const char *name, int is_dir, void *user) +void GatherLanguages(const char *pName, int IsDir, void *pUser) { - if(is_dir || name[0] == '.') + if(IsDir || pName[0] == '.') return; - - sorted_array<LANGUAGE> &languages = *((sorted_array<LANGUAGE> *)user); - char filename[128]; - str_format(filename, sizeof(filename), "data/languages/%s", name); - - char nicename[128]; - str_format(nicename, sizeof(nicename), "%s", name); - nicename[0] = str_uppercase(nicename[0]); - - - for(char *p = nicename; *p; p++) + + sorted_array<LANGUAGE> &Languages = *((sorted_array<LANGUAGE> *)pUser); + char aFileName[128]; + str_format(aFileName, sizeof(aFileName), "data/languages/%s", pName); + + char NiceName[128]; + str_format(NiceName, sizeof(NiceName), "%s", pName); + NiceName[0] = str_uppercase(NiceName[0]); + + + for(char *p = NiceName; *p; p++) if(*p == '.') *p = 0; - - languages.add(LANGUAGE(nicename, filename)); + + Languages.add(LANGUAGE(NiceName, aFileName)); } -void MENUS::render_settings_general(CUIRect main_view) +void CMenus::RenderSettingsGeneral(CUIRect MainView) { - static int lanuagelist = 0; - static int selected_language = 0; - static sorted_array<LANGUAGE> languages; - - if(languages.size() == 0) + static int s_LanguageList = 0; + static int s_SelectedLanguage = 0; + static sorted_array<LANGUAGE> s_Languages; + static float s_ScrollValue = 0; + + if(s_Languages.size() == 0) { - languages.add(LANGUAGE("English", "")); - fs_listdir("data/languages", gather_languages, &languages); - for(int i = 0; i < languages.size(); i++) - if(str_comp(languages[i].filename, config.cl_languagefile) == 0) + s_Languages.add(LANGUAGE("English", "")); + fs_listdir("data/languages", GatherLanguages, &s_Languages); + for(int i = 0; i < s_Languages.size(); i++) + if(str_comp(s_Languages[i].m_FileName, g_Config.m_ClLanguagefile) == 0) { - selected_language = i; + s_SelectedLanguage = i; break; } } - - int old_selected = selected_language; - - CUIRect list = main_view; - ui_do_listbox_start(&lanuagelist, &list, 24.0f, localize("Language"), languages.size(), selected_language); - - for(sorted_array<LANGUAGE>::range r = languages.all(); !r.empty(); r.pop_front()) + + int OldSelected = s_SelectedLanguage; + + CUIRect List = MainView; + UiDoListboxStart(&s_LanguageList , &List, 24.0f, Localize("Language"), "", s_Languages.size(), 1, s_SelectedLanguage, s_ScrollValue); + + for(sorted_array<LANGUAGE>::range r = s_Languages.all(); !r.empty(); r.pop_front()) { - LISTBOXITEM item = ui_do_listbox_nextitem(&r.front()); - - if(item.visible) - UI()->DoLabel(&item.rect, r.front().name, 16.0f, -1); + CListboxItem Item = UiDoListboxNextItem(&r.front()); + + if(Item.m_Visible) + UI()->DoLabel(&Item.m_Rect, r.front().m_Name, 16.0f, -1); } - - selected_language = ui_do_listbox_end(); - - if(old_selected != selected_language) + + s_SelectedLanguage = UiDoListboxEnd(&s_ScrollValue, 0); + + if(OldSelected != s_SelectedLanguage) { - str_copy(config.cl_languagefile, languages[selected_language].filename, sizeof(config.cl_languagefile)); - localization.load(languages[selected_language].filename); + str_copy(g_Config.m_ClLanguagefile, s_Languages[s_SelectedLanguage].m_FileName, sizeof(g_Config.m_ClLanguagefile)); + g_Localization.Load(s_Languages[s_SelectedLanguage].m_FileName); } } -void MENUS::render_settings(CUIRect main_view) +void CMenus::RenderSettings(CUIRect MainView) { - static int settings_page = 0; - + static int s_SettingsPage = 0; + // render background - CUIRect temp, tabbar; - main_view.VSplitRight(120.0f, &main_view, &tabbar); - RenderTools()->DrawUIRect(&main_view, color_tabbar_active, CUI::CORNER_B|CUI::CORNER_TL, 10.0f); - tabbar.HSplitTop(50.0f, &temp, &tabbar); - RenderTools()->DrawUIRect(&temp, color_tabbar_active, CUI::CORNER_R, 10.0f); - - main_view.HSplitTop(10.0f, 0, &main_view); - - CUIRect button; - - const char *tabs[] = { - localize("General"), - localize("Player"), - localize("Controls"), - localize("Graphics"), - localize("Sound")}; - - int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs)); - - for(int i = 0; i < num_tabs; i++) + CUIRect Temp, TabBar; + MainView.VSplitRight(120.0f, &MainView, &TabBar); + RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_B|CUI::CORNER_TL, 10.0f); + TabBar.HSplitTop(50.0f, &Temp, &TabBar); + RenderTools()->DrawUIRect(&Temp, ms_ColorTabbarActive, CUI::CORNER_R, 10.0f); + + MainView.HSplitTop(10.0f, 0, &MainView); + + CUIRect Button; + + const char *aTabs[] = { + Localize("General"), + Localize("Player"), + Localize("Controls"), + Localize("Graphics"), + Localize("Sound")}; + + int NumTabs = (int)(sizeof(aTabs)/sizeof(*aTabs)); + + for(int i = 0; i < NumTabs; i++) { - tabbar.HSplitTop(10, &button, &tabbar); - tabbar.HSplitTop(26, &button, &tabbar); - if(DoButton_SettingsTab(tabs[i], tabs[i], settings_page == i, &button)) - settings_page = i; + TabBar.HSplitTop(10, &Button, &TabBar); + TabBar.HSplitTop(26, &Button, &TabBar); + if(DoButton_SettingsTab(aTabs[i], aTabs[i], s_SettingsPage == i, &Button)) + s_SettingsPage = i; } - - main_view.Margin(10.0f, &main_view); - - if(settings_page == 0) - render_settings_general(main_view); - else if(settings_page == 1) - render_settings_player(main_view); - else if(settings_page == 2) - render_settings_controls(main_view); - else if(settings_page == 3) - render_settings_graphics(main_view); - else if(settings_page == 4) - render_settings_sound(main_view); - - if(need_restart) + + MainView.Margin(10.0f, &MainView); + + if(s_SettingsPage == 0) + RenderSettingsGeneral(MainView); + else if(s_SettingsPage == 1) + RenderSettingsPlayer(MainView); + else if(s_SettingsPage == 2) + RenderSettingsControls(MainView); + else if(s_SettingsPage == 3) + RenderSettingsGraphics(MainView); + else if(s_SettingsPage == 4) + RenderSettingsSound(MainView); + + if(m_NeedRestart) { - CUIRect restart_warning; - main_view.HSplitBottom(40, &main_view, &restart_warning); - UI()->DoLabel(&restart_warning, localize("You must restart the game for all settings to take effect."), 15.0f, -1, 220); + CUIRect RestartWarning; + MainView.HSplitBottom(40, &MainView, &RestartWarning); + UI()->DoLabel(&RestartWarning, Localize("You must restart the game for all settings to take effect."), 15.0f, -1, 220); } } diff --git a/src/game/client/components/motd.cpp b/src/game/client/components/motd.cpp index ba85f7f8..5905d52e 100644 --- a/src/game/client/components/motd.cpp +++ b/src/game/client/components/motd.cpp @@ -1,87 +1,91 @@ -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <engine/e_config.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/keys.h> -#include <game/client/gameclient.hpp> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> +#include <game/client/gameclient.h> -#include "motd.hpp" +#include "motd.h" -void MOTD::clear() +void CMotd::Clear() { - server_motd_time = 0; + m_ServerMotdTime = 0; } -bool MOTD::is_active() +bool CMotd::IsActive() { - return time_get() < server_motd_time; + return time_get() < m_ServerMotdTime; } -void MOTD::on_statechange(int new_state, int old_state) +void CMotd::OnStateChange(int NewState, int OldState) { - if(old_state == CLIENTSTATE_ONLINE || old_state == CLIENTSTATE_OFFLINE) - clear(); + if(OldState == IClient::STATE_ONLINE || OldState == IClient::STATE_OFFLINE) + Clear(); } -void MOTD::on_render() +void CMotd::OnRender() { - if(!is_active()) + if(!IsActive()) return; - float width = 400*3.0f*Graphics()->ScreenAspect(); - float height = 400*3.0f; + float Width = 400*3.0f*Graphics()->ScreenAspect(); + float Height = 400*3.0f; - Graphics()->MapScreen(0, 0, width, height); + Graphics()->MapScreen(0, 0, Width, Height); float h = 800.0f; float w = 650.0f; - float x = width/2 - w/2; + float x = Width/2 - w/2; float y = 150.0f; Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); - RenderTools()->draw_round_rect(x, y, w, h, 40.0f); + RenderTools()->DrawRoundRect(x, y, w, h, 40.0f); Graphics()->QuadsEnd(); - gfx_text(0, x+40.0f, y+40.0f, 32.0f, server_motd, (int)(w-80.0f)); + TextRender()->Text(0, x+40.0f, y+40.0f, 32.0f, m_aServerMotd, (int)(w-80.0f)); } -void MOTD::on_message(int msgtype, void *rawmsg) +void CMotd::OnMessage(int MsgType, void *pRawMsg) { - if(msgtype == NETMSGTYPE_SV_MOTD) + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) + return; + + if(MsgType == NETMSGTYPE_SV_MOTD) { - NETMSG_SV_MOTD *msg = (NETMSG_SV_MOTD *)rawmsg; + CNetMsg_Sv_Motd *pMsg = (CNetMsg_Sv_Motd *)pRawMsg; // process escaping - str_copy(server_motd, msg->message, sizeof(server_motd)); - for(int i = 0; server_motd[i]; i++) + str_copy(m_aServerMotd, pMsg->m_pMessage, sizeof(m_aServerMotd)); + for(int i = 0; m_aServerMotd[i]; i++) { - if(server_motd[i] == '\\') + if(m_aServerMotd[i] == '\\') { - if(server_motd[i+1] == 'n') + if(m_aServerMotd[i+1] == 'n') { - server_motd[i] = ' '; - server_motd[i+1] = '\n'; + m_aServerMotd[i] = ' '; + m_aServerMotd[i+1] = '\n'; i++; } } } - if(server_motd[0] && config.cl_motd_time) - server_motd_time = time_get()+time_freq()*config.cl_motd_time; + if(m_aServerMotd[0] && g_Config.m_ClMotdTime) + m_ServerMotdTime = time_get()+time_freq()*g_Config.m_ClMotdTime; else - server_motd_time = 0; + m_ServerMotdTime = 0; } } -bool MOTD::on_input(INPUT_EVENT e) +bool CMotd::OnInput(IInput::CEvent Event) { - if(is_active() && e.flags&INPFLAG_PRESS && e.key == KEY_ESCAPE) + if(IsActive() && Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE) { - clear(); + Clear(); return true; } return false; diff --git a/src/game/client/components/motd.h b/src/game/client/components/motd.h new file mode 100644 index 00000000..41e15b8c --- /dev/null +++ b/src/game/client/components/motd.h @@ -0,0 +1,21 @@ +#ifndef GAME_CLIENT_COMPONENTS_MOTD_H +#define GAME_CLIENT_COMPONENTS_MOTD_H +#include <game/client/component.h> + +class CMotd : public CComponent +{ + // motd + int64 m_ServerMotdTime; +public: + char m_aServerMotd[900]; + + void Clear(); + bool IsActive(); + + virtual void OnRender(); + virtual void OnStateChange(int NewState, int OldState); + virtual void OnMessage(int MsgType, void *pRawMsg); + virtual bool OnInput(IInput::CEvent Event); +}; + +#endif diff --git a/src/game/client/components/motd.hpp b/src/game/client/components/motd.hpp deleted file mode 100644 index 3b175e0a..00000000 --- a/src/game/client/components/motd.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#include <game/client/component.hpp> - -class MOTD : public COMPONENT -{ - // motd - int64 server_motd_time; -public: - char server_motd[900]; - - void clear(); - bool is_active(); - - virtual void on_render(); - virtual void on_statechange(int new_state, int old_state); - virtual void on_message(int msgtype, void *rawmsg); - virtual bool on_input(INPUT_EVENT e); -}; - diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index 4a47ea37..da114bbb 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -1,65 +1,66 @@ -#include <engine/e_client_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> +#include <engine/textrender.h> +#include <engine/shared/config.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> -#include <game/client/gameclient.hpp> -#include <game/client/animstate.hpp> -#include "nameplates.hpp" -#include "controls.hpp" +#include <game/client/gameclient.h> +#include <game/client/animstate.h> +#include "nameplates.h" +#include "controls.h" -void NAMEPLATES::render_nameplate( - const NETOBJ_CHARACTER *prev_char, - const NETOBJ_CHARACTER *player_char, - const NETOBJ_PLAYER_INFO *player_info +void CNamePlates::RenderNameplate( + const CNetObj_Character *pPrevChar, + const CNetObj_Character *pPlayerChar, + const CNetObj_PlayerInfo *pPlayerInfo ) { - float intratick = client_intratick(); + float IntraTick = Client()->IntraGameTick(); - vec2 position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), intratick); + vec2 Position = mix(vec2(pPrevChar->m_X, pPrevChar->m_Y), vec2(pPlayerChar->m_X, pPlayerChar->m_Y), IntraTick); // render name plate - if(!player_info->local) + if(!pPlayerInfo->m_Local) { - //gfx_text_color + //TextRender()->TextColor float a = 1; - if(config.cl_nameplates_always == 0) - a = clamp(1-powf(distance(gameclient.controls->target_pos, position)/200.0f,16.0f), 0.0f, 1.0f); + if(g_Config.m_ClNameplatesAlways == 0) + a = clamp(1-powf(distance(m_pClient->m_pControls->m_TargetPos, Position)/200.0f,16.0f), 0.0f, 1.0f); - const char *name = gameclient.clients[player_info->cid].name; - float tw = gfx_text_width(0, 28.0f, name, -1); - gfx_text_color(1,1,1,a); - gfx_text(0, position.x-tw/2.0f, position.y-60, 28.0f, name, -1); + const char *pName = m_pClient->m_aClients[pPlayerInfo->m_ClientId].m_aName; + float tw = TextRender()->TextWidth(0, 28.0f, pName, -1); + TextRender()->TextColor(1,1,1,a); + TextRender()->Text(0, Position.x-tw/2.0f, Position.y-60, 28.0f, pName, -1); - if(config.debug) // render client id when in debug aswell + if(g_Config.m_Debug) // render client id when in debug aswell { - char buf[128]; - str_format(buf, sizeof(buf),"%d", player_info->cid); - gfx_text(0, position.x, position.y-90, 28.0f, buf, -1); + char aBuf[128]; + str_format(aBuf, sizeof(aBuf),"%d", pPlayerInfo->m_ClientId); + TextRender()->Text(0, Position.x, Position.y-90, 28.0f, aBuf, -1); } - gfx_text_color(1,1,1,1); + TextRender()->TextColor(1,1,1,1); } } -void NAMEPLATES::on_render() +void CNamePlates::OnRender() { - if (!config.cl_nameplates) + if (!g_Config.m_ClNameplates) return; for(int i = 0; i < MAX_CLIENTS; i++) { // only render active characters - if(!gameclient.snap.characters[i].active) + if(!m_pClient->m_Snap.m_aCharacters[i].m_Active) continue; - const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, i); + const void *pInfo = Client()->SnapFindItem(IClient::SNAP_CURRENT, NETOBJTYPE_PLAYERINFO, i); - if(info) + if(pInfo) { - render_nameplate( - &gameclient.snap.characters[i].prev, - &gameclient.snap.characters[i].cur, - (const NETOBJ_PLAYER_INFO *)info); + RenderNameplate( + &m_pClient->m_Snap.m_aCharacters[i].m_Prev, + &m_pClient->m_Snap.m_aCharacters[i].m_Cur, + (const CNetObj_PlayerInfo *)pInfo); } } } diff --git a/src/game/client/components/nameplates.h b/src/game/client/components/nameplates.h new file mode 100644 index 00000000..279b6582 --- /dev/null +++ b/src/game/client/components/nameplates.h @@ -0,0 +1,17 @@ +#ifndef GAME_CLIENT_COMPONENTS_NAMEPLATES_H +#define GAME_CLIENT_COMPONENTS_NAMEPLATES_H +#include <game/client/component.h> + +class CNamePlates : public CComponent +{ + void RenderNameplate( + const class CNetObj_Character *pPrevChar, + const class CNetObj_Character *pPlayerChar, + const class CNetObj_PlayerInfo *pPlayerInfo + ); + +public: + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/nameplates.hpp b/src/game/client/components/nameplates.hpp deleted file mode 100644 index 2695f5ac..00000000 --- a/src/game/client/components/nameplates.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#include <game/client/component.hpp> - -class NAMEPLATES : public COMPONENT -{ - void render_nameplate( - const class NETOBJ_CHARACTER *prev_char, - const class NETOBJ_CHARACTER *player_char, - const class NETOBJ_PLAYER_INFO *player_info - ); - -public: - virtual void on_render(); -}; - diff --git a/src/game/client/components/particles.cpp b/src/game/client/components/particles.cpp index 61fcf738..99c2c721 100644 --- a/src/game/client/components/particles.cpp +++ b/src/game/client/components/particles.cpp @@ -1,155 +1,156 @@ -#include <base/math.hpp> -#include <engine/client/graphics.h> +#include <base/math.h> +#include <engine/graphics.h> -#include <game/generated/gc_data.hpp> -#include <game/client/render.hpp> -#include <game/gamecore.hpp> -#include "particles.hpp" +#include <game/generated/client_data.h> +#include <game/client/render.h> +#include <game/gamecore.h> +#include "particles.h" -PARTICLES::PARTICLES() +CParticles::CParticles() { - on_reset(); - render_trail.parts = this; - render_explosions.parts = this; - render_general.parts = this; + OnReset(); + m_RenderTrail.m_pParts = this; + m_RenderExplosions.m_pParts = this; + m_RenderGeneral.m_pParts = this; } -void PARTICLES::on_reset() +void CParticles::OnReset() { // reset particles for(int i = 0; i < MAX_PARTICLES; i++) { - particles[i].prev_part = i-1; - particles[i].next_part = i+1; + m_aParticles[i].m_PrevPart = i-1; + m_aParticles[i].m_NextPart = i+1; } - particles[0].prev_part = 0; - particles[MAX_PARTICLES-1].next_part = -1; - first_free = 0; + m_aParticles[0].m_PrevPart = 0; + m_aParticles[MAX_PARTICLES-1].m_NextPart = -1; + m_FirstFree = 0; for(int i = 0; i < NUM_GROUPS; i++) - first_part[i] = -1; + m_aFirstPart[i] = -1; } -void PARTICLES::add(int group, PARTICLE *part) +void CParticles::Add(int Group, CParticle *pPart) { - if (first_free == -1) + if (m_FirstFree == -1) return; // remove from the free list - int id = first_free; - first_free = particles[id].next_part; - particles[first_free].prev_part = -1; + int Id = m_FirstFree; + m_FirstFree = m_aParticles[Id].m_NextPart; + m_aParticles[m_FirstFree].m_PrevPart = -1; // copy data - particles[id] = *part; + m_aParticles[Id] = *pPart; // insert to the group list - particles[id].prev_part = -1; - particles[id].next_part = first_part[group]; - if(first_part[group] != -1) - particles[first_part[group]].prev_part = id; - first_part[group] = id; + m_aParticles[Id].m_PrevPart = -1; + m_aParticles[Id].m_NextPart = m_aFirstPart[Group]; + if(m_aFirstPart[Group] != -1) + m_aParticles[m_aFirstPart[Group]].m_PrevPart = Id; + m_aFirstPart[Group] = Id; // set some parameters - particles[id].life = 0; + m_aParticles[Id].m_Life = 0; } -void PARTICLES::update(float time_passed) +void CParticles::Update(float TimePassed) { - static float friction_fraction = 0; - friction_fraction += time_passed; + static float FrictionFraction = 0; + FrictionFraction += TimePassed; - if(friction_fraction > 2.0f) // safty messure - friction_fraction = 0; + if(FrictionFraction > 2.0f) // safty messure + FrictionFraction = 0; - int friction_count = 0; - while(friction_fraction > 0.05f) + int FrictionCount = 0; + while(FrictionFraction > 0.05f) { - friction_count++; - friction_fraction -= 0.05f; + FrictionCount++; + FrictionFraction -= 0.05f; } for(int g = 0; g < NUM_GROUPS; g++) { - int i = first_part[g]; + int i = m_aFirstPart[g]; while(i != -1) { - int next = particles[i].next_part; - //particles[i].vel += flow_get(particles[i].pos)*time_passed * particles[i].flow_affected; - particles[i].vel.y += particles[i].gravity*time_passed; + int Next = m_aParticles[i].m_NextPart; + //m_aParticles[i].vel += flow_get(m_aParticles[i].pos)*time_passed * m_aParticles[i].flow_affected; + m_aParticles[i].m_Vel.y += m_aParticles[i].m_Gravity*TimePassed; - for(int f = 0; f < friction_count; f++) // apply friction - particles[i].vel *= particles[i].friction; + for(int f = 0; f < FrictionCount; f++) // apply friction + m_aParticles[i].m_Vel *= m_aParticles[i].m_Friction; // move the point - vec2 vel = particles[i].vel*time_passed; - move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL); - particles[i].vel = vel* (1.0f/time_passed); + vec2 Vel = m_aParticles[i].m_Vel*TimePassed; + Collision()->MovePoint(&m_aParticles[i].m_Pos, &Vel, 0.1f+0.9f*frandom(), NULL); + m_aParticles[i].m_Vel = Vel* (1.0f/TimePassed); - particles[i].life += time_passed; - particles[i].rot += time_passed * particles[i].rotspeed; + m_aParticles[i].m_Life += TimePassed; + m_aParticles[i].m_Rot += TimePassed * m_aParticles[i].m_Rotspeed; // check particle death - if(particles[i].life > particles[i].life_span) + if(m_aParticles[i].m_Life > m_aParticles[i].m_LifeSpan) { // remove it from the group list - if(particles[i].prev_part != -1) - particles[particles[i].prev_part].next_part = particles[i].next_part; + if(m_aParticles[i].m_PrevPart != -1) + m_aParticles[m_aParticles[i].m_PrevPart].m_NextPart = m_aParticles[i].m_NextPart; else - first_part[g] = particles[i].next_part; + m_aFirstPart[g] = m_aParticles[i].m_NextPart; - if(particles[i].next_part != -1) - particles[particles[i].next_part].prev_part = particles[i].prev_part; + if(m_aParticles[i].m_NextPart != -1) + m_aParticles[m_aParticles[i].m_NextPart].m_PrevPart = m_aParticles[i].m_PrevPart; // insert to the free list - if(first_free != -1) - particles[first_free].prev_part = i; - particles[i].prev_part = -1; - particles[i].next_part = first_free; - first_free = i; + if(m_FirstFree != -1) + m_aParticles[m_FirstFree].m_PrevPart = i; + m_aParticles[i].m_PrevPart = -1; + m_aParticles[i].m_NextPart = m_FirstFree; + m_FirstFree = i; } - i = next; + i = Next; } } } -void PARTICLES::on_render() +void CParticles::OnRender() { - static int64 lasttime = 0; + static int64 LastTime = 0; int64 t = time_get(); - update((float)((t-lasttime)/(double)time_freq())); - lasttime = t; + Update((float)((t-LastTime)/(double)time_freq())); + LastTime = t; } -void PARTICLES::render_group(int group) +void CParticles::RenderGroup(int Group) { Graphics()->BlendNormal(); //gfx_blend_additive(); - Graphics()->TextureSet(data->images[IMAGE_PARTICLES].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_PARTICLES].m_Id); Graphics()->QuadsBegin(); - int i = first_part[group]; + int i = m_aFirstPart[Group]; while(i != -1) { - RenderTools()->select_sprite(particles[i].spr); - float a = particles[i].life / particles[i].life_span; - vec2 p = particles[i].pos; - float size = mix(particles[i].start_size, particles[i].end_size, a); + RenderTools()->SelectSprite(m_aParticles[i].m_Spr); + float a = m_aParticles[i].m_Life / m_aParticles[i].m_LifeSpan; + vec2 p = m_aParticles[i].m_Pos; + float Size = mix(m_aParticles[i].m_StartSize, m_aParticles[i].m_EndSize, a); - Graphics()->QuadsSetRotation(particles[i].rot); + Graphics()->QuadsSetRotation(m_aParticles[i].m_Rot); Graphics()->SetColor( - particles[i].color.r, - particles[i].color.g, - particles[i].color.b, - particles[i].color.a); // pow(a, 0.75f) * + m_aParticles[i].m_Color.r, + m_aParticles[i].m_Color.g, + m_aParticles[i].m_Color.b, + m_aParticles[i].m_Color.a); // pow(a, 0.75f) * - Graphics()->QuadsDraw(p.x, p.y, size, size); + IGraphics::CQuadItem QuadItem(p.x, p.y, Size, Size); + Graphics()->QuadsDraw(&QuadItem, 1); - i = particles[i].next_part; + i = m_aParticles[i].m_NextPart; } Graphics()->QuadsEnd(); Graphics()->BlendNormal(); diff --git a/src/game/client/components/particles.h b/src/game/client/components/particles.h new file mode 100644 index 00000000..af9a9203 --- /dev/null +++ b/src/game/client/components/particles.h @@ -0,0 +1,94 @@ +#ifndef GAME_CLIENT_COMPONENTS_PARTICLES_H +#define GAME_CLIENT_COMPONENTS_PARTICLES_H +#include <base/vmath.h> +#include <game/client/component.h> + +// particles +struct CParticle +{ + void SetDefault() + { + m_Vel = vec2(0,0); + m_LifeSpan = 0; + m_StartSize = 32; + m_EndSize = 32; + m_Rot = 0; + m_Rotspeed = 0; + m_Gravity = 0; + m_Friction = 0; + m_FlowAffected = 1.0f; + m_Color = vec4(1,1,1,1); + } + + vec2 m_Pos; + vec2 m_Vel; + + int m_Spr; + + float m_FlowAffected; + + float m_LifeSpan; + + float m_StartSize; + float m_EndSize; + + float m_Rot; + float m_Rotspeed; + + float m_Gravity; + float m_Friction; + + vec4 m_Color; + + // set by the particle system + float m_Life; + int m_PrevPart; + int m_NextPart; +}; + +class CParticles : public CComponent +{ + friend class CGameClient; +public: + enum + { + GROUP_PROJECTILE_TRAIL=0, + GROUP_EXPLOSIONS, + GROUP_GENERAL, + NUM_GROUPS + }; + + CParticles(); + + void Add(int Group, CParticle *pPart); + + virtual void OnReset(); + virtual void OnRender(); + +private: + + enum + { + MAX_PARTICLES=1024*8, + }; + + CParticle m_aParticles[MAX_PARTICLES]; + int m_FirstFree; + int m_aFirstPart[NUM_GROUPS]; + + void RenderGroup(int Group); + void Update(float TimePassed); + + template<int TGROUP> + class CRenderGroup : public CComponent + { + public: + CParticles *m_pParts; + virtual void OnRender() { m_pParts->RenderGroup(TGROUP); } + }; + + CRenderGroup<GROUP_PROJECTILE_TRAIL> m_RenderTrail; + CRenderGroup<GROUP_EXPLOSIONS> m_RenderExplosions; + CRenderGroup<GROUP_GENERAL> m_RenderGeneral; +}; +#endif diff --git a/src/game/client/components/particles.hpp b/src/game/client/components/particles.hpp deleted file mode 100644 index 6c466d94..00000000 --- a/src/game/client/components/particles.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#include <base/vmath.hpp> -#include <game/client/component.hpp> - -// particles -struct PARTICLE -{ - void set_default() - { - vel = vec2(0,0); - life_span = 0; - start_size = 32; - end_size = 32; - rot = 0; - rotspeed = 0; - gravity = 0; - friction = 0; - flow_affected = 1.0f; - color = vec4(1,1,1,1); - } - - vec2 pos; - vec2 vel; - - int spr; - - float flow_affected; - - float life_span; - - float start_size; - float end_size; - - float rot; - float rotspeed; - - float gravity; - float friction; - - vec4 color; - - // set by the particle system - float life; - int prev_part; - int next_part; -}; - -class PARTICLES : public COMPONENT -{ - friend class GAMECLIENT; -public: - enum - { - GROUP_PROJECTILE_TRAIL=0, - GROUP_EXPLOSIONS, - GROUP_GENERAL, - NUM_GROUPS - }; - - PARTICLES(); - - void add(int group, PARTICLE *part); - - virtual void on_reset(); - virtual void on_render(); - -private: - - enum - { - MAX_PARTICLES=1024*8, - }; - - PARTICLE particles[MAX_PARTICLES]; - int first_free; - int first_part[NUM_GROUPS]; - - void render_group(int group); - void update(float time_passed); - - template<int TGROUP> - class RENDER_GROUP : public COMPONENT - { - public: - PARTICLES *parts; - virtual void on_render() { parts->render_group(TGROUP); } - }; - - RENDER_GROUP<GROUP_PROJECTILE_TRAIL> render_trail; - RENDER_GROUP<GROUP_EXPLOSIONS> render_explosions; - RENDER_GROUP<GROUP_GENERAL> render_general; -}; diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index e1fee3e8..8ad7b0cb 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -1,147 +1,269 @@ -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> - -#include <game/gamecore.hpp> // get_angle -#include <game/client/animstate.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/ui.hpp> -#include <game/client/render.hpp> - -#include <game/client/components/flow.hpp> -#include <game/client/components/skins.hpp> -#include <game/client/components/effects.hpp> -#include <game/client/components/sounds.hpp> -#include <game/client/components/controls.hpp> - -#include "players.hpp" - -void PLAYERS::render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset) +#include <engine/graphics.h> +#include <engine/shared/config.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> + +#include <game/gamecore.h> // get_angle +#include <game/client/animstate.h> +#include <game/client/gameclient.h> +#include <game/client/ui.h> +#include <game/client/render.h> + +#include <game/client/components/flow.h> +#include <game/client/components/skins.h> +#include <game/client/components/effects.h> +#include <game/client/components/sounds.h> +#include <game/client/components/controls.h> + +#include "players.h" + +void CPlayers::RenderHand(CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float AngleOffset, vec2 PostRotOffset) { // for drawing hand //const skin *s = skin_get(skin_id); - float basesize = 10.0f; + float BaseSize = 10.0f; //dir = normalize(hook_pos-pos); - vec2 hand_pos = center_pos + dir; - float angle = get_angle(dir); - if (dir.x < 0) - angle -= angle_offset; + vec2 HandPos = CenterPos + Dir; + float Angle = GetAngle(Dir); + if (Dir.x < 0) + Angle -= AngleOffset; else - angle += angle_offset; + Angle += AngleOffset; - vec2 dirx = dir; - vec2 diry(-dir.y,dir.x); + vec2 DirX = Dir; + vec2 DirY(-Dir.y,Dir.x); - if (dir.x < 0) - diry = -diry; + if (Dir.x < 0) + DirY = -DirY; - hand_pos += dirx * post_rot_offset.x; - hand_pos += diry * post_rot_offset.y; + HandPos += DirX * PostRotOffset.x; + HandPos += DirY * PostRotOffset.y; - //Graphics()->TextureSet(data->images[IMAGE_CHAR_DEFAULT].id); - Graphics()->TextureSet(info->texture); + //Graphics()->TextureSet(data->m_aImages[IMAGE_CHAR_DEFAULT].id); + Graphics()->TextureSet(pInfo->m_Texture); Graphics()->QuadsBegin(); - Graphics()->SetColor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a); + Graphics()->SetColor(pInfo->m_ColorBody.r, pInfo->m_ColorBody.g, pInfo->m_ColorBody.b, pInfo->m_ColorBody.a); // two passes for (int i = 0; i < 2; i++) { - bool outline = i == 0; + bool OutLine = i == 0; - RenderTools()->select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0); - Graphics()->QuadsSetRotation(angle); - Graphics()->QuadsDraw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize); + RenderTools()->SelectSprite(OutLine?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0); + Graphics()->QuadsSetRotation(Angle); + IGraphics::CQuadItem QuadItem(HandPos.x, HandPos.y, 2*BaseSize, 2*BaseSize); + Graphics()->QuadsDraw(&QuadItem, 1); } Graphics()->QuadsSetRotation(0); Graphics()->QuadsEnd(); } -inline float normalize_angular(float f) +inline float NormalizeAngular(float f) { return fmod(f+pi*2, pi*2); } -inline float angular_mix_direction(float src, float dst) { return sinf(dst-src) >0?1:-1; } -inline float angular_distance(float src, float dst) { return asinf(sinf(dst-src)); } +inline float AngularMixDirection (float Src, float Dst) { return sinf(Dst-Src) >0?1:-1; } +inline float AngularDistance(float Src, float Dst) { return asinf(sinf(Dst-Src)); } -inline float angular_approach(float src, float dst, float amount) +inline float AngularApproach(float Src, float Dst, float Amount) { - float d = angular_mix_direction(src, dst); - float n = src + amount*d; - if(angular_mix_direction(n, dst) != d) - return dst; + float d = AngularMixDirection (Src, Dst); + float n = Src + Amount*d; + if(AngularMixDirection (n, Dst) != d) + return Dst; return n; } -void PLAYERS::render_player( - const NETOBJ_CHARACTER *prev_char, - const NETOBJ_CHARACTER *player_char, - const NETOBJ_PLAYER_INFO *prev_info, - const NETOBJ_PLAYER_INFO *player_info +void CPlayers::RenderHook( + const CNetObj_Character *pPrevChar, + const CNetObj_Character *pPlayerChar, + const CNetObj_PlayerInfo *pPrevInfo, + const CNetObj_PlayerInfo *pPlayerInfo ) { - NETOBJ_CHARACTER prev; - NETOBJ_CHARACTER player; - prev = *prev_char; - player = *player_char; + CNetObj_Character Prev; + CNetObj_Character Player; + Prev = *pPrevChar; + Player = *pPlayerChar; - NETOBJ_PLAYER_INFO info = *player_info; - TEE_RENDER_INFO render_info = gameclient.clients[info.cid].render_info; + CNetObj_PlayerInfo pInfo = *pPlayerInfo; + CTeeRenderInfo RenderInfo = m_pClient->m_aClients[pInfo.m_ClientId].m_RenderInfo; // check for teamplay modes - bool is_teamplay = false; - bool new_tick = gameclient.new_tick; - if(gameclient.snap.gameobj) - is_teamplay = (gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) != 0; + bool IsTeamplay = false; + if(m_pClient->m_Snap.m_pGameobj) + IsTeamplay = (m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) != 0; // check for ninja - if (player.weapon == WEAPON_NINJA) + if (Player.m_Weapon == WEAPON_NINJA) { // change the skin for the player to the ninja - int skin = gameclient.skins->find("x_ninja"); - if(skin != -1) + int Skin = m_pClient->m_pSkins->Find("x_ninja"); + if(Skin != -1) { - if(is_teamplay) - render_info.texture = gameclient.skins->get(skin)->color_texture; + if(IsTeamplay) + RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; else { - render_info.texture = gameclient.skins->get(skin)->org_texture; - render_info.color_body = vec4(1,1,1,1); - render_info.color_feet = vec4(1,1,1,1); + RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; + RenderInfo.m_ColorBody = vec4(1,1,1,1); + RenderInfo.m_ColorFeet = vec4(1,1,1,1); + } + } + } + + float IntraTick = Client()->IntraGameTick(); + + if(Player.m_Health < 0) // dont render dead players + return; + + // set size + RenderInfo.m_Size = 64.0f; + + + // use preditect players if needed + if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) + { + if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pLocalCharacter->m_Health < 0) || (m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver)) + { + } + else + { + // apply predicted results + m_pClient->m_PredictedChar.Write(&Player); + m_pClient->m_PredictedPrevChar.Write(&Prev); + IntraTick = Client()->PredIntraGameTick(); + } + } + + vec2 Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + + if(Prev.m_Health < 0) // Don't flicker from previous position + Position = vec2(Player.m_X, Player.m_Y); + + // draw hook + if (Prev.m_HookState>0 && Player.m_HookState>0) + { + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); + Graphics()->QuadsBegin(); + //Graphics()->QuadsBegin(); + + vec2 Pos = Position; + vec2 HookPos; + + if(pPlayerChar->m_HookedPlayer != -1) + { + if(m_pClient->m_Snap.m_pLocalInfo && pPlayerChar->m_HookedPlayer == m_pClient->m_Snap.m_pLocalInfo->m_ClientId) + { + if(Client()->State() == IClient::STATE_DEMOPLAYBACK) // only use prediction if needed + HookPos = vec2(m_pClient->m_LocalCharacterPos.x, m_pClient->m_LocalCharacterPos.y); + else + HookPos = mix(vec2(m_pClient->m_PredictedPrevChar.m_Pos.x, m_pClient->m_PredictedPrevChar.m_Pos.y), + vec2(m_pClient->m_PredictedChar.m_Pos.x, m_pClient->m_PredictedChar.m_Pos.y), Client()->PredIntraGameTick()); + } + else + HookPos = mix(vec2(pPrevChar->m_HookX, pPrevChar->m_HookY), vec2(pPlayerChar->m_HookX, pPlayerChar->m_HookY), Client()->IntraGameTick()); + } + else + HookPos = mix(vec2(Prev.m_HookX, Prev.m_HookY), vec2(Player.m_HookX, Player.m_HookY), IntraTick); + + float d = distance(Pos, HookPos); + vec2 Dir = normalize(Pos-HookPos); + + Graphics()->QuadsSetRotation(GetAngle(Dir)+pi); + + // render head + RenderTools()->SelectSprite(SPRITE_HOOK_HEAD); + IGraphics::CQuadItem QuadItem(HookPos.x, HookPos.y, 24,16); + Graphics()->QuadsDraw(&QuadItem, 1); + + // render chain + RenderTools()->SelectSprite(SPRITE_HOOK_CHAIN); + IGraphics::CQuadItem Array[1024]; + int i = 0; + for(float f = 24; f < d && i < 1024; f += 24, i++) + { + vec2 p = HookPos + Dir*f; + Array[i] = IGraphics::CQuadItem(p.x, p.y,24,16); + } + + Graphics()->QuadsDraw(Array, i); + Graphics()->QuadsSetRotation(0); + Graphics()->QuadsEnd(); + + RenderHand(&RenderInfo, Position, normalize(HookPos-Pos), -pi/2, vec2(20, 0)); + } +} + +void CPlayers::RenderPlayer( + const CNetObj_Character *pPrevChar, + const CNetObj_Character *pPlayerChar, + const CNetObj_PlayerInfo *pPrevInfo, + const CNetObj_PlayerInfo *pPlayerInfo + ) +{ + CNetObj_Character Prev; + CNetObj_Character Player; + Prev = *pPrevChar; + Player = *pPlayerChar; + + CNetObj_PlayerInfo pInfo = *pPlayerInfo; + CTeeRenderInfo RenderInfo = m_pClient->m_aClients[pInfo.m_ClientId].m_RenderInfo; + + // check for teamplay modes + bool IsTeamplay = false; + bool NewTick = m_pClient->m_NewTick; + if(m_pClient->m_Snap.m_pGameobj) + IsTeamplay = (m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) != 0; + + // check for ninja + if (Player.m_Weapon == WEAPON_NINJA) + { + // change the skin for the player to the ninja + int Skin = m_pClient->m_pSkins->Find("x_ninja"); + if(Skin != -1) + { + if(IsTeamplay) + RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_ColorTexture; + else + { + RenderInfo.m_Texture = m_pClient->m_pSkins->Get(Skin)->m_OrgTexture; + RenderInfo.m_ColorBody = vec4(1,1,1,1); + RenderInfo.m_ColorFeet = vec4(1,1,1,1); } } } // set size - render_info.size = 64.0f; + RenderInfo.m_Size = 64.0f; - float intratick = client_intratick(); + float IntraTick = Client()->IntraGameTick(); - if(player.health < 0) // dont render dead players + if(Player.m_Health < 0) // dont render dead players return; - float angle = mix((float)prev.angle, (float)player.angle, intratick)/256.0f; + float Angle = mix((float)Prev.m_Angle, (float)Player.m_Angle, IntraTick)/256.0f; //float angle = 0; - if(info.local && client_state() != CLIENTSTATE_DEMOPLAYBACK) + if(pInfo.m_Local && Client()->State() != IClient::STATE_DEMOPLAYBACK) { // just use the direct input if it's local player we are rendering - angle = get_angle(gameclient.controls->mouse_pos); + Angle = GetAngle(m_pClient->m_pControls->m_MousePos); } else { /* - float mixspeed = client_frametime()*2.5f; + float mixspeed = Client()->FrameTime()*2.5f; if(player.attacktick != prev.attacktick) // shooting boosts the mixing speed mixspeed *= 15.0f; // move the delta on a constant speed on a x^2 curve - float current = gameclient.clients[info.cid].angle; + float current = g_GameClient.m_aClients[info.cid].angle; float target = player.angle/256.0f; float delta = angular_distance(current, target); float sign = delta < 0 ? -1 : 1; @@ -153,342 +275,305 @@ void PLAYERS::render_player( else angle = angular_approach(current, target, fabs(delta-new_delta)); - gameclient.clients[info.cid].angle = angle;*/ + g_GameClient.m_aClients[info.cid].angle = angle;*/ } // use preditect players if needed - if(info.local && config.cl_predict && client_state() != CLIENTSTATE_DEMOPLAYBACK) + if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) { - if(!gameclient.snap.local_character || (gameclient.snap.local_character->health < 0) || (gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)) + if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pLocalCharacter->m_Health < 0) || (m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver)) { } else { // apply predicted results - gameclient.predicted_char.write(&player); - gameclient.predicted_prev_char.write(&prev); - intratick = client_predintratick(); - new_tick = gameclient.new_predicted_tick; + m_pClient->m_PredictedChar.Write(&Player); + m_pClient->m_PredictedPrevChar.Write(&Prev); + IntraTick = Client()->PredIntraGameTick(); + NewTick = m_pClient->m_NewPredictedTick; } } - vec2 direction = get_direction((int)(angle*256.0f)); - vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick); - vec2 vel = mix(vec2(prev.vx/256.0f, prev.vy/256.0f), vec2(player.vx/256.0f, player.vy/256.0f), intratick); + vec2 Direction = GetDirection((int)(Angle*256.0f)); + vec2 Position = mix(vec2(Prev.m_X, Prev.m_Y), vec2(Player.m_X, Player.m_Y), IntraTick); + vec2 Vel = mix(vec2(Prev.m_VelX/256.0f, Prev.m_VelY/256.0f), vec2(Player.m_VelX/256.0f, Player.m_VelY/256.0f), IntraTick); - gameclient.flow->add(position, vel*100.0f, 10.0f); + m_pClient->m_pFlow->Add(Position, Vel*100.0f, 10.0f); - render_info.got_airjump = player.jumped&2?0:1; + RenderInfo.m_GotAirJump = Player.m_Jumped&2?0:1; // detect events - if(new_tick) + if(NewTick) { // detect air jump - if(!render_info.got_airjump && !(prev.jumped&2)) - gameclient.effects->air_jump(position); + if(!RenderInfo.m_GotAirJump && !(Prev.m_Jumped&2)) + m_pClient->m_pEffects->AirJump(Position); } - if(prev.health < 0) // Don't flicker from previous position - position = vec2(player.x, player.y); + if(Prev.m_Health < 0) // Don't flicker from previous position + Position = vec2(Player.m_X, Player.m_Y); - bool stationary = player.vx < 1 && player.vx > -1; - bool inair = col_check_point(player.x, player.y+16) == 0; - bool want_other_dir = (player.direction == -1 && vel.x > 0) || (player.direction == 1 && vel.x < 0); + bool Stationary = Player.m_VelX <= 1 && Player.m_VelX >= -1; + bool InAir = !Collision()->CheckPoint(Player.m_X, Player.m_Y+16); + bool WantOtherDir = (Player.m_Direction == -1 && Vel.x > 0) || (Player.m_Direction == 1 && Vel.x < 0); // evaluate animation - float walk_time = fmod(position.x, 100.0f)/100.0f; - ANIMSTATE state; - state.set(&data->animations[ANIM_BASE], 0); - - if(inair) - state.add(&data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here - else if(stationary) - state.add(&data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here - else if(!want_other_dir) - state.add(&data->animations[ANIM_WALK], walk_time, 1.0f); - - if (player.weapon == WEAPON_HAMMER) + float WalkTime = fmod(Position.x, 100.0f)/100.0f; + CAnimState State; + State.Set(&g_pData->m_aAnimations[ANIM_BASE], 0); + + if(InAir) + State.Add(&g_pData->m_aAnimations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here + else if(Stationary) + State.Add(&g_pData->m_aAnimations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here + else if(!WantOtherDir) + State.Add(&g_pData->m_aAnimations[ANIM_WALK], WalkTime, 1.0f); + + if (Player.m_Weapon == WEAPON_HAMMER) { - float ct = (client_prevtick()-player.attacktick)/(float)SERVER_TICK_SPEED + client_ticktime(); - state.add(&data->animations[ANIM_HAMMER_SWING], clamp(ct*5.0f,0.0f,1.0f), 1.0f); + float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + State.Add(&g_pData->m_aAnimations[ANIM_HAMMER_SWING], clamp(ct*5.0f,0.0f,1.0f), 1.0f); } - if (player.weapon == WEAPON_NINJA) + if (Player.m_Weapon == WEAPON_NINJA) { - float ct = (client_prevtick()-player.attacktick)/(float)SERVER_TICK_SPEED + client_ticktime(); - state.add(&data->animations[ANIM_NINJA_SWING], clamp(ct*2.0f,0.0f,1.0f), 1.0f); + float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime(); + State.Add(&g_pData->m_aAnimations[ANIM_NINJA_SWING], clamp(ct*2.0f,0.0f,1.0f), 1.0f); } // do skidding - if(!inair && want_other_dir && length(vel*50) > 500.0f) + if(!InAir && WantOtherDir && length(Vel*50) > 500.0f) { - static int64 skid_sound_time = 0; - if(time_get()-skid_sound_time > time_freq()/10) + static int64 SkidSoundTime = 0; + if(time_get()-SkidSoundTime > time_freq()/10) { - gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, position); - skid_sound_time = time_get(); + m_pClient->m_pSounds->Play(CSounds::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, Position); + SkidSoundTime = time_get(); } - gameclient.effects->skidtrail( - position+vec2(-player.direction*6,12), - vec2(-player.direction*100*length(vel),-50) + m_pClient->m_pEffects->SkidTrail( + Position+vec2(-Player.m_Direction*6,12), + vec2(-Player.m_Direction*100*length(Vel),-50) ); } - // draw hook - if (prev.hook_state>0 && player.hook_state>0) - { - Graphics()->TextureSet(data->images[IMAGE_GAME].id); - Graphics()->QuadsBegin(); - //Graphics()->QuadsBegin(); - - vec2 pos = position; - vec2 hook_pos; - - if(player_char->hooked_player != -1) - { - if(gameclient.snap.local_info && player_char->hooked_player == gameclient.snap.local_info->cid) - { - hook_pos = mix(vec2(gameclient.predicted_prev_char.pos.x, gameclient.predicted_prev_char.pos.y), - vec2(gameclient.predicted_char.pos.x, gameclient.predicted_char.pos.y), client_predintratick()); - } - else - hook_pos = mix(vec2(prev_char->hook_x, prev_char->hook_y), vec2(player_char->hook_x, player_char->hook_y), client_intratick()); - } - else - hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick); - - float d = distance(pos, hook_pos); - vec2 dir = normalize(pos-hook_pos); - - Graphics()->QuadsSetRotation(get_angle(dir)+pi); - - // render head - RenderTools()->select_sprite(SPRITE_HOOK_HEAD); - Graphics()->QuadsDraw(hook_pos.x, hook_pos.y, 24,16); - - // render chain - RenderTools()->select_sprite(SPRITE_HOOK_CHAIN); - int i = 0; - for(float f = 24; f < d && i < 1024; f += 24, i++) - { - vec2 p = hook_pos + dir*f; - Graphics()->QuadsDraw(p.x, p.y,24,16); - } - - Graphics()->QuadsSetRotation(0); - Graphics()->QuadsEnd(); - - render_hand(&render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0)); - } - // draw gun { - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - Graphics()->QuadsSetRotation(state.attach.angle*pi*2+angle); + Graphics()->QuadsSetRotation(State.GetAttach()->m_Angle*pi*2+Angle); // normal weapons - int iw = clamp(player.weapon, 0, NUM_WEAPONS-1); - RenderTools()->select_sprite(data->weapons.id[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + int iw = clamp(Player.m_Weapon, 0, NUM_WEAPONS-1); + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[iw].m_pSpriteBody, Direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); - vec2 dir = direction; - float recoil = 0.0f; + vec2 Dir = Direction; + float Recoil = 0.0f; vec2 p; - if (player.weapon == WEAPON_HAMMER) + if (Player.m_Weapon == WEAPON_HAMMER) { // Static position for hammer - p = position + vec2(state.attach.x, state.attach.y); - p.y += data->weapons.id[iw].offsety; + p = Position + vec2(State.GetAttach()->m_X, State.GetAttach()->m_Y); + p.y += g_pData->m_Weapons.m_aId[iw].m_Offsety; // if attack is under way, bash stuffs - if(direction.x < 0) + if(Direction.x < 0) { - Graphics()->QuadsSetRotation(-pi/2-state.attach.angle*pi*2); - p.x -= data->weapons.id[iw].offsetx; + Graphics()->QuadsSetRotation(-pi/2-State.GetAttach()->m_Angle*pi*2); + p.x -= g_pData->m_Weapons.m_aId[iw].m_Offsetx; } else { - Graphics()->QuadsSetRotation(-pi/2+state.attach.angle*pi*2); + Graphics()->QuadsSetRotation(-pi/2+State.GetAttach()->m_Angle*pi*2); } - RenderTools()->draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + RenderTools()->DrawSprite(p.x, p.y, g_pData->m_Weapons.m_aId[iw].m_VisualSize); } - else if (player.weapon == WEAPON_NINJA) + else if (Player.m_Weapon == WEAPON_NINJA) { - p = position; - p.y += data->weapons.id[iw].offsety; + p = Position; + p.y += g_pData->m_Weapons.m_aId[iw].m_Offsety; - if(direction.x < 0) + if(Direction.x < 0) { - Graphics()->QuadsSetRotation(-pi/2-state.attach.angle*pi*2); - p.x -= data->weapons.id[iw].offsetx; - gameclient.effects->powerupshine(p+vec2(32,0), vec2(32,12)); + Graphics()->QuadsSetRotation(-pi/2-State.GetAttach()->m_Angle*pi*2); + p.x -= g_pData->m_Weapons.m_aId[iw].m_Offsetx; + m_pClient->m_pEffects->PowerupShine(p+vec2(32,0), vec2(32,12)); } else { - Graphics()->QuadsSetRotation(-pi/2+state.attach.angle*pi*2); - gameclient.effects->powerupshine(p-vec2(32,0), vec2(32,12)); + Graphics()->QuadsSetRotation(-pi/2+State.GetAttach()->m_Angle*pi*2); + m_pClient->m_pEffects->PowerupShine(p-vec2(32,0), vec2(32,12)); } - RenderTools()->draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + RenderTools()->DrawSprite(p.x, p.y, g_pData->m_Weapons.m_aId[iw].m_VisualSize); // HADOKEN - if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons.id[iw].num_sprite_muzzles) + if ((Client()->GameTick()-Player.m_AttackTick) <= (SERVER_TICK_SPEED / 6) && g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles) { - int itex = rand() % data->weapons.id[iw].num_sprite_muzzles; - float alpha = 1.0f; - if (alpha > 0.0f && data->weapons.id[iw].sprite_muzzles[itex]) + int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles; + float Alpha = 1.0f; + if (Alpha > 0.0f && g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX]) { - vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y); - dir = normalize(dir); - float hadokenangle = get_angle(dir); - Graphics()->QuadsSetRotation(hadokenangle); + vec2 Dir = vec2(pPlayerChar->m_X,pPlayerChar->m_Y) - vec2(pPrevChar->m_X, pPrevChar->m_Y); + Dir = normalize(Dir); + float HadOkenAngle = GetAngle(Dir); + Graphics()->QuadsSetRotation(HadOkenAngle ); //float offsety = -data->weapons[iw].muzzleoffsety; - RenderTools()->select_sprite(data->weapons.id[iw].sprite_muzzles[itex], 0); - vec2 diry(-dir.y,dir.x); - p = position; - float offsetx = data->weapons.id[iw].muzzleoffsetx; - p -= dir * offsetx; - RenderTools()->draw_sprite(p.x, p.y, 160.0f); + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX], 0); + vec2 DirY(-Dir.y,Dir.x); + p = Position; + float OffsetX = g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsetx; + p -= Dir * OffsetX; + RenderTools()->DrawSprite(p.x, p.y, 160.0f); } } } else { // TODO: should be an animation - recoil = 0; - float a = (client_tick()-player.attacktick+intratick)/5.0f; + Recoil = 0; + float a = (Client()->GameTick()-Player.m_AttackTick+IntraTick)/5.0f; if(a < 1) - recoil = sinf(a*pi); - p = position + dir * data->weapons.id[iw].offsetx - dir*recoil*10.0f; - p.y += data->weapons.id[iw].offsety; - RenderTools()->draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size); + Recoil = sinf(a*pi); + p = Position + Dir * g_pData->m_Weapons.m_aId[iw].m_Offsetx - Dir*Recoil*10.0f; + p.y += g_pData->m_Weapons.m_aId[iw].m_Offsety; + RenderTools()->DrawSprite(p.x, p.y, g_pData->m_Weapons.m_aId[iw].m_VisualSize); } - if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN) + if (Player.m_Weapon == WEAPON_GUN || Player.m_Weapon == WEAPON_SHOTGUN) { // check if we're firing stuff - if(data->weapons.id[iw].num_sprite_muzzles)//prev.attackticks) + if(g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles)//prev.attackticks) { - float alpha = 0.0f; - int phase1tick = (client_tick() - player.attacktick); - if (phase1tick < (data->weapons.id[iw].muzzleduration + 3)) + float Alpha = 0.0f; + int Phase1Tick = (Client()->GameTick() - Player.m_AttackTick); + if (Phase1Tick < (g_pData->m_Weapons.m_aId[iw].m_Muzzleduration + 3)) { - float t = ((((float)phase1tick) + intratick)/(float)data->weapons.id[iw].muzzleduration); - alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); + float t = ((((float)Phase1Tick) + IntraTick)/(float)g_pData->m_Weapons.m_aId[iw].m_Muzzleduration); + Alpha = mix(2.0f, 0.0f, min(1.0f,max(0.0f,t))); } - int itex = rand() % data->weapons.id[iw].num_sprite_muzzles; - if (alpha > 0.0f && data->weapons.id[iw].sprite_muzzles[itex]) + int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles; + if (Alpha > 0.0f && g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX]) { - float offsety = -data->weapons.id[iw].muzzleoffsety; - RenderTools()->select_sprite(data->weapons.id[iw].sprite_muzzles[itex], direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); - if(direction.x < 0) - offsety = -offsety; + float OffsetY = -g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsety; + RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX], Direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0); + if(Direction.x < 0) + OffsetY = -OffsetY; - vec2 diry(-dir.y,dir.x); - vec2 muzzlepos = p + dir * data->weapons.id[iw].muzzleoffsetx + diry * offsety; + vec2 DirY(-Dir.y,Dir.x); + vec2 MuzzlePos = p + Dir * g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsetx + DirY * OffsetY; - RenderTools()->draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons.id[iw].visual_size); + RenderTools()->DrawSprite(MuzzlePos.x, MuzzlePos.y, g_pData->m_Weapons.m_aId[iw].m_VisualSize); } } } Graphics()->QuadsEnd(); - switch (player.weapon) + switch (Player.m_Weapon) { - case WEAPON_GUN: render_hand(&render_info, p, direction, -3*pi/4, vec2(-15, 4)); break; - case WEAPON_SHOTGUN: render_hand(&render_info, p, direction, -pi/2, vec2(-5, 4)); break; - case WEAPON_GRENADE: render_hand(&render_info, p, direction, -pi/2, vec2(-4, 7)); break; + case WEAPON_GUN: RenderHand(&RenderInfo, p, Direction, -3*pi/4, vec2(-15, 4)); break; + case WEAPON_SHOTGUN: RenderHand(&RenderInfo, p, Direction, -pi/2, vec2(-5, 4)); break; + case WEAPON_GRENADE: RenderHand(&RenderInfo, p, Direction, -pi/2, vec2(-4, 7)); break; } } // render the "shadow" tee - if(info.local && config.debug) + if(pInfo.m_Local && g_Config.m_Debug) { - vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick()); - TEE_RENDER_INFO ghost = render_info; - ghost.color_body.a = 0.5f; - ghost.color_feet.a = 0.5f; - RenderTools()->RenderTee(&state, &ghost, player.emote, direction, ghost_position); // render ghost + vec2 GhostPosition = mix(vec2(pPrevChar->m_X, pPrevChar->m_Y), vec2(pPlayerChar->m_X, pPlayerChar->m_Y), Client()->IntraGameTick()); + CTeeRenderInfo Ghost = RenderInfo; + Ghost.m_ColorBody.a = 0.5f; + Ghost.m_ColorFeet.a = 0.5f; + RenderTools()->RenderTee(&State, &Ghost, Player.m_Emote, Direction, GhostPosition); // render ghost } - render_info.size = 64.0f; // force some settings - render_info.color_body.a = 1.0f; - render_info.color_feet.a = 1.0f; - RenderTools()->RenderTee(&state, &render_info, player.emote, direction, position); + RenderInfo.m_Size = 64.0f; // force some settings + RenderInfo.m_ColorBody.a = 1.0f; + RenderInfo.m_ColorFeet.a = 1.0f; + RenderTools()->RenderTee(&State, &RenderInfo, Player.m_Emote, Direction, Position); - if(player.player_state == PLAYERSTATE_CHATTING) + if(Player.m_PlayerState == PLAYERSTATE_CHATTING) { - Graphics()->TextureSet(data->images[IMAGE_EMOTICONS].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_EMOTICONS].m_Id); Graphics()->QuadsBegin(); - RenderTools()->select_sprite(SPRITE_DOTDOT); - Graphics()->QuadsDraw(position.x + 24, position.y - 40, 64,64); + RenderTools()->SelectSprite(SPRITE_DOTDOT); + IGraphics::CQuadItem QuadItem(Position.x + 24, Position.y - 40, 64,64); + Graphics()->QuadsDraw(&QuadItem, 1); Graphics()->QuadsEnd(); } - if (gameclient.clients[info.cid].emoticon_start != -1 && gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() > client_tick()) + if (m_pClient->m_aClients[pInfo.m_ClientId].m_EmoticonStart != -1 && m_pClient->m_aClients[pInfo.m_ClientId].m_EmoticonStart + 2 * Client()->GameTickSpeed() > Client()->GameTick()) { - Graphics()->TextureSet(data->images[IMAGE_EMOTICONS].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_EMOTICONS].m_Id); Graphics()->QuadsBegin(); - int since_start = client_tick() - gameclient.clients[info.cid].emoticon_start; - int from_end = gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() - client_tick(); + int SinceStart = Client()->GameTick() - m_pClient->m_aClients[pInfo.m_ClientId].m_EmoticonStart; + int FromEnd = m_pClient->m_aClients[pInfo.m_ClientId].m_EmoticonStart + 2 * Client()->GameTickSpeed() - Client()->GameTick(); float a = 1; - if (from_end < client_tickspeed() / 5) - a = from_end / (client_tickspeed() / 5.0); + if (FromEnd < Client()->GameTickSpeed() / 5) + a = FromEnd / (Client()->GameTickSpeed() / 5.0); float h = 1; - if (since_start < client_tickspeed() / 10) - h = since_start / (client_tickspeed() / 10.0); + if (SinceStart < Client()->GameTickSpeed() / 10) + h = SinceStart / (Client()->GameTickSpeed() / 10.0); - float wiggle = 0; - if (since_start < client_tickspeed() / 5) - wiggle = since_start / (client_tickspeed() / 5.0); + float Wiggle = 0; + if (SinceStart < Client()->GameTickSpeed() / 5) + Wiggle = SinceStart / (Client()->GameTickSpeed() / 5.0); - float wiggle_angle = sin(5*wiggle); + float WiggleAngle = sinf(5*Wiggle); - Graphics()->QuadsSetRotation(pi/6*wiggle_angle); + Graphics()->QuadsSetRotation(pi/6*WiggleAngle); Graphics()->SetColor(1.0f,1.0f,1.0f,a); // client_datas::emoticon is an offset from the first emoticon - RenderTools()->select_sprite(SPRITE_OOP + gameclient.clients[info.cid].emoticon); - Graphics()->QuadsDraw(position.x, position.y - 23 - 32*h, 64, 64*h); + RenderTools()->SelectSprite(SPRITE_OOP + m_pClient->m_aClients[pInfo.m_ClientId].m_Emoticon); + IGraphics::CQuadItem QuadItem(Position.x, Position.y - 23 - 32*h, 64, 64*h); + Graphics()->QuadsDraw(&QuadItem, 1); Graphics()->QuadsEnd(); } } -void PLAYERS::on_render() +void CPlayers::OnRender() { // render other players in two passes, first pass we render the other, second pass we render our self - for(int p = 0; p < 2; p++) + for(int p = 0; p < 4; p++) { for(int i = 0; i < MAX_CLIENTS; i++) { // only render active characters - if(!gameclient.snap.characters[i].active) + if(!m_pClient->m_Snap.m_aCharacters[i].m_Active) continue; - const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, i); - const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, i); + const void *pPrevInfo = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_PLAYERINFO, i); + const void *pInfo = Client()->SnapFindItem(IClient::SNAP_CURRENT, NETOBJTYPE_PLAYERINFO, i); - if(prev_info && info) + if(pPrevInfo && pInfo) { // - bool local = ((const NETOBJ_PLAYER_INFO *)info)->local !=0; - if(p == 0 && local) continue; - if(p == 1 && !local) continue; + bool Local = ((const CNetObj_PlayerInfo *)pInfo)->m_Local !=0; + if((p % 2) == 0 && Local) continue; + if((p % 2) == 1 && !Local) continue; - NETOBJ_CHARACTER prev_char = gameclient.snap.characters[i].prev; - NETOBJ_CHARACTER cur_char = gameclient.snap.characters[i].cur; - - render_player( - &prev_char, - &cur_char, - (const NETOBJ_PLAYER_INFO *)prev_info, - (const NETOBJ_PLAYER_INFO *)info - ); + CNetObj_Character PrevChar = m_pClient->m_Snap.m_aCharacters[i].m_Prev; + CNetObj_Character CurChar = m_pClient->m_Snap.m_aCharacters[i].m_Cur; + + if(p<2) + RenderHook( + &PrevChar, + &CurChar, + (const CNetObj_PlayerInfo *)pPrevInfo, + (const CNetObj_PlayerInfo *)pInfo + ); + else + RenderPlayer( + &PrevChar, + &CurChar, + (const CNetObj_PlayerInfo *)pPrevInfo, + (const CNetObj_PlayerInfo *)pInfo + ); } } } diff --git a/src/game/client/components/players.h b/src/game/client/components/players.h new file mode 100644 index 00000000..57501380 --- /dev/null +++ b/src/game/client/components/players.h @@ -0,0 +1,25 @@ +#ifndef GAME_CLIENT_COMPONENTS_PLAYERS_H +#define GAME_CLIENT_COMPONENTS_PLAYERS_H +#include <game/client/component.h> + +class CPlayers : public CComponent +{ + void RenderHand(class CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float AngleOffset, vec2 PostRotOffset); + void RenderPlayer( + const class CNetObj_Character *pPrevChar, + const class CNetObj_Character *pPlayerChar, + const class CNetObj_PlayerInfo *pPrevInfo, + const class CNetObj_PlayerInfo *pPlayerInfo + ); + void RenderHook( + const CNetObj_Character *pPrevChar, + const CNetObj_Character *pPlayerChar, + const CNetObj_PlayerInfo *pPrevInfo, + const CNetObj_PlayerInfo *pPlayerInfo + ); + +public: + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/players.hpp b/src/game/client/components/players.hpp deleted file mode 100644 index bdce91de..00000000 --- a/src/game/client/components/players.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#include <game/client/component.hpp> - -class PLAYERS : public COMPONENT -{ - void render_hand(class TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset); - void render_player( - const class NETOBJ_CHARACTER *prev_char, - const class NETOBJ_CHARACTER *player_char, - const class NETOBJ_PLAYER_INFO *prev_info, - const class NETOBJ_PLAYER_INFO *player_info - ); - -public: - virtual void on_render(); -}; - diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp index e8db1aed..b7e8aa9a 100644 --- a/src/game/client/components/scoreboard.cpp +++ b/src/game/client/components/scoreboard.cpp @@ -1,36 +1,37 @@ -#include <string.h> -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/animstate.hpp> -#include <game/client/render.hpp> -#include <game/client/components/motd.hpp> -#include "scoreboard.hpp" - - -SCOREBOARD::SCOREBOARD() +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/shared/config.h> +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> +#include <game/client/gameclient.h> +#include <game/client/animstate.h> +#include <game/client/render.h> +#include <game/client/components/motd.h> +#include <game/localization.h> +#include "scoreboard.h" + + +CScoreboard::CScoreboard() { - on_reset(); + OnReset(); } -void SCOREBOARD::con_key_scoreboard(void *result, void *user_data) +void CScoreboard::ConKeyScoreboard(IConsole::IResult *pResult, void *pUserData) { - ((SCOREBOARD *)user_data)->active = console_arg_int(result, 0) != 0; + ((CScoreboard *)pUserData)->m_Active = pResult->GetInteger(0) != 0; } -void SCOREBOARD::on_reset() +void CScoreboard::OnReset() { - active = false; + m_Active = false; } -void SCOREBOARD::on_console_init() +void CScoreboard::OnConsoleInit() { - MACRO_REGISTER_COMMAND("+scoreboard", "", CFGFLAG_CLIENT, con_key_scoreboard, this, "Show scoreboard"); + Console()->Register("+scoreboard", "", CFGFLAG_CLIENT, ConKeyScoreboard, this, "Show scoreboard"); } -void SCOREBOARD::render_goals(float x, float y, float w) +void CScoreboard::RenderGoals(float x, float y, float w) { float h = 50.0f; @@ -38,74 +39,77 @@ void SCOREBOARD::render_goals(float x, float y, float w) Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); - RenderTools()->draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); + RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, h, 10.0f); Graphics()->QuadsEnd(); // render goals //y = ystart+h-54; float tw = 0.0f; - if(gameclient.snap.gameobj && gameclient.snap.gameobj->score_limit) + if(m_pClient->m_Snap.m_pGameobj) { - char buf[64]; - str_format(buf, sizeof(buf), "%s: %d", localize("Score limit"), gameclient.snap.gameobj->score_limit); - gfx_text(0, x+20.0f, y, 22.0f, buf, -1); - tw += gfx_text_width(0, 22.0f, buf, -1); - } - if(gameclient.snap.gameobj && gameclient.snap.gameobj->time_limit) - { - char buf[64]; - str_format(buf, sizeof(buf), "%s: %d min", localize("Time limit"), gameclient.snap.gameobj->time_limit); - gfx_text(0, x+220.0f, y, 22.0f, buf, -1); - tw += gfx_text_width(0, 22.0f, buf, -1); - } - if(gameclient.snap.gameobj && gameclient.snap.gameobj->round_num && gameclient.snap.gameobj->round_current) - { - char buf[64]; - str_format(buf, sizeof(buf), "%s %d/%d", localize("Round"), gameclient.snap.gameobj->round_current, gameclient.snap.gameobj->round_num); - gfx_text(0, x+450.0f, y, 22.0f, buf, -1); - - /*[48c3fd4c][game/scoreboard]: timelimit x:219.428558 - [48c3fd4c][game/scoreboard]: round x:453.142822*/ + if(m_pClient->m_Snap.m_pGameobj->m_ScoreLimit) + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Score limit"), m_pClient->m_Snap.m_pGameobj->m_ScoreLimit); + TextRender()->Text(0, x+20.0f, y, 22.0f, aBuf, -1); + tw += TextRender()->TextWidth(0, 22.0f, aBuf, -1); + } + if(m_pClient->m_Snap.m_pGameobj->m_TimeLimit) + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%s: %d min", Localize("Time limit"), m_pClient->m_Snap.m_pGameobj->m_TimeLimit); + TextRender()->Text(0, x+220.0f, y, 22.0f, aBuf, -1); + tw += TextRender()->TextWidth(0, 22.0f, aBuf, -1); + } + if(m_pClient->m_Snap.m_pGameobj->m_RoundNum && m_pClient->m_Snap.m_pGameobj->m_RoundCurrent) + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%s %d/%d", Localize("Round"), m_pClient->m_Snap.m_pGameobj->m_RoundCurrent, m_pClient->m_Snap.m_pGameobj->m_RoundNum); + TextRender()->Text(0, x+450.0f, y, 22.0f, aBuf, -1); + + /*[48c3fd4c][game/scoreboard]: timelimit x:219.428558 + [48c3fd4c][game/scoreboard]: round x:453.142822*/ + } } } -void SCOREBOARD::render_spectators(float x, float y, float w) +void CScoreboard::RenderSpectators(float x, float y, float w) { - char buffer[1024*4]; - int count = 0; + char aBuffer[1024*4]; + int Count = 0; float h = 120.0f; - str_format(buffer, sizeof(buffer), "%s: ", localize("Spectators")); + str_format(aBuffer, sizeof(aBuffer), "%s: ", Localize("Spectators")); Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); - RenderTools()->draw_round_rect(x-10.f, y-10.f, w, h, 10.0f); + RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, h, 10.0f); Graphics()->QuadsEnd(); - for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) + for(int i = 0; i < Client()->SnapNumItems(IClient::SNAP_CURRENT); i++) { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); - if(item.type == NETOBJTYPE_PLAYER_INFO) + if(Item.m_Type == NETOBJTYPE_PLAYERINFO) { - const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; - if(info->team == -1) + const CNetObj_PlayerInfo *pInfo = (const CNetObj_PlayerInfo *)pData; + if(pInfo->m_Team == -1) { - if(count) - strcat(buffer, ", "); - strcat(buffer, gameclient.clients[info->cid].name); - count++; + if(Count) + str_append(aBuffer, ", ", sizeof(aBuffer)); + str_append(aBuffer, m_pClient->m_aClients[pInfo->m_ClientId].m_aName, sizeof(aBuffer)); + Count++; } } } - gfx_text(0, x+10, y, 32, buffer, (int)w-20); + TextRender()->Text(0, x+10, y, 32, aBuffer, (int)w-20); } -void SCOREBOARD::render_scoreboard(float x, float y, float w, int team, const char *title) +void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const char *pTitle) { //float ystart = y; float h = 750.0f; @@ -114,202 +118,203 @@ void SCOREBOARD::render_scoreboard(float x, float y, float w, int team, const ch Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); - RenderTools()->draw_round_rect(x-10.f, y-10.f, w, h, 17.0f); + RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, h, 17.0f); Graphics()->QuadsEnd(); // render title - if(!title) + if(!pTitle) { - if(gameclient.snap.gameobj->game_over) - title = localize("Game over"); + if(m_pClient->m_Snap.m_pGameobj->m_GameOver) + pTitle = Localize("Game over"); else - title = localize("Score board"); + pTitle = Localize("Score board"); } - float tw = gfx_text_width(0, 48, title, -1); + float tw = TextRender()->TextWidth(0, 48, pTitle, -1); - if(team == -1) + if(Team == -1) { - gfx_text(0, x+w/2-tw/2, y, 48, title, -1); + TextRender()->Text(0, x+w/2-tw/2, y, 48, pTitle, -1); } else { - gfx_text(0, x+10, y, 48, title, -1); + TextRender()->Text(0, x+10, y, 48, pTitle, -1); - if(gameclient.snap.gameobj) + if(m_pClient->m_Snap.m_pGameobj) { - char buf[128]; - int score = team ? gameclient.snap.gameobj->teamscore_blue : gameclient.snap.gameobj->teamscore_red; - str_format(buf, sizeof(buf), "%d", score); - tw = gfx_text_width(0, 48, buf, -1); - gfx_text(0, x+w-tw-30, y, 48, buf, -1); + char aBuf[128]; + int Score = Team ? m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue : m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed; + str_format(aBuf, sizeof(aBuf), "%d", Score); + tw = TextRender()->TextWidth(0, 48, aBuf, -1); + TextRender()->Text(0, x+w-tw-30, y, 48, aBuf, -1); } } y += 54.0f; // find players - const NETOBJ_PLAYER_INFO *players[MAX_CLIENTS] = {0}; - int num_players = 0; - for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) + const CNetObj_PlayerInfo *paPlayers[MAX_CLIENTS] = {0}; + int NumPlayers = 0; + for(int i = 0; i < Client()->SnapNumItems(IClient::SNAP_CURRENT); i++) { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); - if(item.type == NETOBJTYPE_PLAYER_INFO) + if(Item.m_Type == NETOBJTYPE_PLAYERINFO) { - const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; - if(info->team == team) + const CNetObj_PlayerInfo *pInfo = (const CNetObj_PlayerInfo *)pData; + if(pInfo->m_Team == Team) { - players[num_players] = info; - num_players++; + paPlayers[NumPlayers] = pInfo; + NumPlayers++; } } } // sort players - for(int k = 0; k < num_players; k++) // ffs, bubblesort + for(int k = 0; k < NumPlayers; k++) // ffs, bubblesort { - for(int i = 0; i < num_players-k-1; i++) + for(int i = 0; i < NumPlayers-k-1; i++) { - if(players[i]->score < players[i+1]->score) + if(paPlayers[i]->m_Score < paPlayers[i+1]->m_Score) { - const NETOBJ_PLAYER_INFO *tmp = players[i]; - players[i] = players[i+1]; - players[i+1] = tmp; + const CNetObj_PlayerInfo *pTmp = paPlayers[i]; + paPlayers[i] = paPlayers[i+1]; + paPlayers[i+1] = pTmp; } } } // render headlines - gfx_text(0, x+10, y, 24.0f, localize("Score"), -1); - gfx_text(0, x+125, y, 24.0f, localize("Name"), -1); - gfx_text(0, x+w-70, y, 24.0f, localize("Ping"), -1); + TextRender()->Text(0, x+10, y, 24.0f, Localize("Score"), -1); + TextRender()->Text(0, x+125, y, 24.0f, Localize("Name"), -1); + TextRender()->Text(0, x+w-70, y, 24.0f, Localize("Ping"), -1); y += 29.0f; - float font_size = 35.0f; - float line_height = 50.0f; - float tee_sizemod = 1.0f; - float tee_offset = 0.0f; + float FontSize = 35.0f; + float LineHeight = 50.0f; + float TeeSizeMod = 1.0f; + float TeeOffset = 0.0f; - if(num_players > 13) + if(NumPlayers > 13) { - font_size = 30.0f; - line_height = 40.0f; - tee_sizemod = 0.8f; - tee_offset = -5.0f; + FontSize = 30.0f; + LineHeight = 40.0f; + TeeSizeMod = 0.8f; + TeeOffset = -5.0f; } // render player scores - for(int i = 0; i < num_players; i++) + for(int i = 0; i < NumPlayers; i++) { - const NETOBJ_PLAYER_INFO *info = players[i]; + const CNetObj_PlayerInfo *pInfo = paPlayers[i]; // make sure that we render the correct team - char buf[128]; - if(info->local) + char aBuf[128]; + if(pInfo->m_Local) { // background so it's easy to find the local player Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(1,1,1,0.25f); - RenderTools()->draw_round_rect(x, y, w-20, line_height*0.95f, 17.0f); + RenderTools()->DrawRoundRect(x, y, w-20, LineHeight*0.95f, 17.0f); Graphics()->QuadsEnd(); } - str_format(buf, sizeof(buf), "%4d", info->score); - gfx_text(0, x+60-gfx_text_width(0, font_size,buf,-1), y, font_size, buf, -1); + str_format(aBuf, sizeof(aBuf), "%4d", pInfo->m_Score); + TextRender()->Text(0, x+60-TextRender()->TextWidth(0, FontSize,aBuf,-1), y, FontSize, aBuf, -1); - gfx_text(0, x+128, y, font_size, gameclient.clients[info->cid].name, -1); + TextRender()->Text(0, x+128, y, FontSize, m_pClient->m_aClients[pInfo->m_ClientId].m_aName, -1); - str_format(buf, sizeof(buf), "%4d", info->latency); - float tw = gfx_text_width(0, font_size, buf, -1); - gfx_text(0, x+w-tw-35, y, font_size, buf, -1); + str_format(aBuf, sizeof(aBuf), "%4d", pInfo->m_Latency); + float tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); + TextRender()->Text(0, x+w-tw-35, y, FontSize, aBuf, -1); // render avatar - if((gameclient.snap.flags[0] && gameclient.snap.flags[0]->carried_by == info->cid) || - (gameclient.snap.flags[1] && gameclient.snap.flags[1]->carried_by == info->cid)) + if((m_pClient->m_Snap.m_paFlags[0] && m_pClient->m_Snap.m_paFlags[0]->m_CarriedBy == pInfo->m_ClientId) || + (m_pClient->m_Snap.m_paFlags[1] && m_pClient->m_Snap.m_paFlags[1]->m_CarriedBy == pInfo->m_ClientId)) { Graphics()->BlendNormal(); - Graphics()->TextureSet(data->images[IMAGE_GAME].id); + Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); - if(info->team == 0) RenderTools()->select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); - else RenderTools()->select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); + if(pInfo->m_Team == 0) RenderTools()->SelectSprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X); + else RenderTools()->SelectSprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); float size = 64.0f; - Graphics()->QuadsDrawTL(x+55, y-15, size/2, size); + IGraphics::CQuadItem QuadItem(x+55, y-15, size/2, size); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } - TEE_RENDER_INFO teeinfo = gameclient.clients[info->cid].render_info; - teeinfo.size *= tee_sizemod; - RenderTools()->RenderTee(ANIMSTATE::get_idle(), &teeinfo, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28+tee_offset)); + CTeeRenderInfo TeeInfo = m_pClient->m_aClients[pInfo->m_ClientId].m_RenderInfo; + TeeInfo.m_Size *= TeeSizeMod; + RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28+TeeOffset)); - y += line_height; + y += LineHeight; } } -void SCOREBOARD::on_render() +void CScoreboard::OnRender() { - bool do_scoreboard = false; + bool DoScoreBoard = false; // if we activly wanna look on the scoreboard - if(active) - do_scoreboard = true; + if(m_Active) + DoScoreBoard = true; - if(gameclient.snap.local_info && gameclient.snap.local_info->team != -1) + if(m_pClient->m_Snap.m_pLocalInfo && m_pClient->m_Snap.m_pLocalInfo->m_Team != -1) { // we are not a spectator, check if we are ead - if(!gameclient.snap.local_character || gameclient.snap.local_character->health < 0) - do_scoreboard = true; + if(!m_pClient->m_Snap.m_pLocalCharacter || m_pClient->m_Snap.m_pLocalCharacter->m_Health < 0) + DoScoreBoard = true; } // if we the game is over - if(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over) - do_scoreboard = true; + if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver) + DoScoreBoard = true; - if(!do_scoreboard) + if(!DoScoreBoard) return; // if the score board is active, then we should clear the motd message aswell - if(active) - gameclient.motd->clear(); + if(m_Active) + m_pClient->m_pMotd->Clear(); - float width = 400*3.0f*Graphics()->ScreenAspect(); - float height = 400*3.0f; + float Width = 400*3.0f*Graphics()->ScreenAspect(); + float Height = 400*3.0f; - Graphics()->MapScreen(0, 0, width, height); + Graphics()->MapScreen(0, 0, Width, Height); float w = 650.0f; - if(gameclient.snap.gameobj && !(gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS)) + if(m_pClient->m_Snap.m_pGameobj && !(m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS)) { - render_scoreboard(width/2-w/2, 150.0f, w, 0, 0); + RenderScoreboard(Width/2-w/2, 150.0f, w, 0, 0); //render_scoreboard(gameobj, 0, 0, -1, 0); } else { - if(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over) + if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver) { - const char *text = localize("Draw!"); - if(gameclient.snap.gameobj->teamscore_red > gameclient.snap.gameobj->teamscore_blue) - text = localize("Red team wins!"); - else if(gameclient.snap.gameobj->teamscore_blue > gameclient.snap.gameobj->teamscore_red) - text = localize("Blue team wins!"); + const char *pText = Localize("Draw!"); + if(m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed > m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue) + pText = Localize("Red team wins!"); + else if(m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue > m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed) + pText = Localize("Blue team wins!"); - float w = gfx_text_width(0, 92.0f, text, -1); - gfx_text(0, width/2-w/2, 45, 92.0f, text, -1); + float w = TextRender()->TextWidth(0, 92.0f, pText, -1); + TextRender()->Text(0, Width/2-w/2, 45, 92.0f, pText, -1); } - render_scoreboard(width/2-w-20, 150.0f, w, 0, localize("Red team")); - render_scoreboard(width/2 + 20, 150.0f, w, 1, localize("Blue team")); + RenderScoreboard(Width/2-w-20, 150.0f, w, 0, Localize("Red team")); + RenderScoreboard(Width/2 + 20, 150.0f, w, 1, Localize("Blue team")); } - render_goals(width/2-w/2, 150+750+25, w); - render_spectators(width/2-w/2, 150+750+25+50+25, w); + RenderGoals(Width/2-w/2, 150+750+25, w); + RenderSpectators(Width/2-w/2, 150+750+25+50+25, w); } diff --git a/src/game/client/components/scoreboard.h b/src/game/client/components/scoreboard.h new file mode 100644 index 00000000..5aa2f0a7 --- /dev/null +++ b/src/game/client/components/scoreboard.h @@ -0,0 +1,22 @@ +#ifndef GAME_CLIENT_COMPONENTS_SCOREBOARD_H +#define GAME_CLIENT_COMPONENTS_SCOREBOARD_H +#include <game/client/component.h> + +class CScoreboard : public CComponent +{ + void RenderGoals(float x, float y, float w); + void RenderSpectators(float x, float y, float w); + void RenderScoreboard(float x, float y, float w, int Team, const char *pTitle); + + static void ConKeyScoreboard(IConsole::IResult *pResult, void *pUserData); + + bool m_Active; + +public: + CScoreboard(); + virtual void OnReset(); + virtual void OnConsoleInit(); + virtual void OnRender(); +}; + +#endif diff --git a/src/game/client/components/scoreboard.hpp b/src/game/client/components/scoreboard.hpp deleted file mode 100644 index 222dab9d..00000000 --- a/src/game/client/components/scoreboard.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#include <game/client/component.hpp> - -class SCOREBOARD : public COMPONENT -{ - void render_goals(float x, float y, float w); - void render_spectators(float x, float y, float w); - void render_scoreboard(float x, float y, float w, int team, const char *title); - - static void con_key_scoreboard(void *result, void *user_data); - - bool active; - -public: - SCOREBOARD(); - virtual void on_reset(); - virtual void on_console_init(); - virtual void on_render(); -}; - diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index ad3607a1..582adb10 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -1,153 +1,150 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> -#include <stdio.h> +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <math.h> #include <base/system.h> -#include <base/math.hpp> +#include <base/math.h> -#include <engine/client/graphics.h> -#include <engine/e_client_interface.h> +#include <engine/graphics.h> +#include <engine/storage.h> +#include <engine/shared/engine.h> -#include <engine/e_engine.h> +#include "skins.h" -#include "skins.hpp" - -SKINS::SKINS() +CSkins::CSkins() { - num_skins = 0; + m_NumSkins = 0; } -void SKINS::skinscan(const char *name, int is_dir, void *user) +void CSkins::SkinScan(const char *pName, int IsDir, void *pUser) { - SKINS *self = (SKINS *)user; - int l = strlen(name); - if(l < 4 || is_dir || self->num_skins == MAX_SKINS) + CSkins *pSelf = (CSkins *)pUser; + int l = str_length(pName); + if(l < 4 || IsDir || pSelf->m_NumSkins == MAX_SKINS) return; - if(strcmp(name+l-4, ".png") != 0) + if(str_comp(pName+l-4, ".png") != 0) return; - char buf[512]; - str_format(buf, sizeof(buf), "skins/%s", name); - IMAGE_INFO info; - if(!self->Graphics()->LoadPNG(&info, buf)) + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "skins/%s", pName); + CImageInfo Info; + if(!pSelf->Graphics()->LoadPNG(&Info, aBuf)) { - dbg_msg("game", "failed to load skin from %s", name); + dbg_msg("game", "failed to load skin from %s", pName); return; } - self->skins[self->num_skins].org_texture = self->Graphics()->LoadTextureRaw(info.width, info.height, info.format, info.data, info.format, 0); + pSelf->m_aSkins[pSelf->m_NumSkins].m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); - int body_size = 96; // body size - unsigned char *d = (unsigned char *)info.data; - int pitch = info.width*4; + int BodySize = 96; // body size + unsigned char *d = (unsigned char *)Info.m_pData; + int Pitch = Info.m_Width*4; // dig out blood color { - int colors[3] = {0}; - for(int y = 0; y < body_size; y++) - for(int x = 0; x < body_size; x++) + int aColors[3] = {0}; + for(int y = 0; y < BodySize; y++) + for(int x = 0; x < BodySize; x++) { - if(d[y*pitch+x*4+3] > 128) + if(d[y*Pitch+x*4+3] > 128) { - colors[0] += d[y*pitch+x*4+0]; - colors[1] += d[y*pitch+x*4+1]; - colors[2] += d[y*pitch+x*4+2]; + aColors[0] += d[y*Pitch+x*4+0]; + aColors[1] += d[y*Pitch+x*4+1]; + aColors[2] += d[y*Pitch+x*4+2]; } } - self->skins[self->num_skins].blood_color = normalize(vec3(colors[0], colors[1], colors[2])); + pSelf->m_aSkins[pSelf->m_NumSkins].m_BloodColor = normalize(vec3(aColors[0], aColors[1], aColors[2])); } // create colorless version - int step = info.format == IMG_RGBA ? 4 : 3; + int Step = Info.m_Format == CImageInfo::FORMAT_RGBA ? 4 : 3; // make the texture gray scale - for(int i = 0; i < info.width*info.height; i++) + for(int i = 0; i < Info.m_Width*Info.m_Height; i++) { - int v = (d[i*step]+d[i*step+1]+d[i*step+2])/3; - d[i*step] = v; - d[i*step+1] = v; - d[i*step+2] = v; + int v = (d[i*Step]+d[i*Step+1]+d[i*Step+2])/3; + d[i*Step] = v; + d[i*Step+1] = v; + d[i*Step+2] = v; } if(1) { - int freq[256] = {0}; - int org_weight = 0; - int new_weight = 192; + int Freq[256] = {0}; + int OrgWeight = 0; + int NewWeight = 192; // find most common frequence - for(int y = 0; y < body_size; y++) - for(int x = 0; x < body_size; x++) + for(int y = 0; y < BodySize; y++) + for(int x = 0; x < BodySize; x++) { - if(d[y*pitch+x*4+3] > 128) - freq[d[y*pitch+x*4]]++; + if(d[y*Pitch+x*4+3] > 128) + Freq[d[y*Pitch+x*4]]++; } for(int i = 1; i < 256; i++) { - if(freq[org_weight] < freq[i]) - org_weight = i; + if(Freq[OrgWeight] < Freq[i]) + OrgWeight = i; } // reorder - int inv_org_weight = 255-org_weight; - int inv_new_weight = 255-new_weight; - for(int y = 0; y < body_size; y++) - for(int x = 0; x < body_size; x++) + int InvOrgWeight = 255-OrgWeight; + int InvNewWeight = 255-NewWeight; + for(int y = 0; y < BodySize; y++) + for(int x = 0; x < BodySize; x++) { - int v = d[y*pitch+x*4]; - if(v <= org_weight) - v = (int)(((v/(float)org_weight) * new_weight)); + int v = d[y*Pitch+x*4]; + if(v <= OrgWeight) + v = (int)(((v/(float)OrgWeight) * NewWeight)); else - v = (int)(((v-org_weight)/(float)inv_org_weight)*inv_new_weight + new_weight); - d[y*pitch+x*4] = v; - d[y*pitch+x*4+1] = v; - d[y*pitch+x*4+2] = v; + v = (int)(((v-OrgWeight)/(float)InvOrgWeight)*InvNewWeight + NewWeight); + d[y*Pitch+x*4] = v; + d[y*Pitch+x*4+1] = v; + d[y*Pitch+x*4+2] = v; } } - self->skins[self->num_skins].color_texture = self->Graphics()->LoadTextureRaw(info.width, info.height, info.format, info.data, info.format, 0); - mem_free(info.data); + pSelf->m_aSkins[pSelf->m_NumSkins].m_ColorTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0); + mem_free(Info.m_pData); // set skin data - strncpy(self->skins[self->num_skins].name, name, min((int)sizeof(self->skins[self->num_skins].name),l-4)); - dbg_msg("game", "load skin %s", self->skins[self->num_skins].name); - self->num_skins++; + str_copy(pSelf->m_aSkins[pSelf->m_NumSkins].m_aName, pName, min((int)sizeof(pSelf->m_aSkins[pSelf->m_NumSkins].m_aName),l-3)); + dbg_msg("game", "load skin %s", pSelf->m_aSkins[pSelf->m_NumSkins].m_aName); + pSelf->m_NumSkins++; } -void SKINS::init() +void CSkins::Init() { // load skins - num_skins = 0; - engine_listdir(LISTDIRTYPE_ALL, "skins", skinscan, this); + m_NumSkins = 0; + Storage()->ListDirectory(IStorage::TYPE_ALL, "skins", SkinScan, this); } -int SKINS::num() +int CSkins::Num() { - return num_skins; + return m_NumSkins; } -const SKINS::SKIN *SKINS::get(int index) +const CSkins::CSkin *CSkins::Get(int Index) { - return &skins[index%num_skins]; + return &m_aSkins[Index%m_NumSkins]; } -int SKINS::find(const char *name) +int CSkins::Find(const char *pName) { - for(int i = 0; i < num_skins; i++) + for(int i = 0; i < m_NumSkins; i++) { - if(strcmp(skins[i].name, name) == 0) + if(str_comp(m_aSkins[i].m_aName, pName) == 0) return i; } return -1; } // these converter functions were nicked from some random internet pages -static float hue_to_rgb(float v1, float v2, float h) +static float HueToRgb(float v1, float v2, float h) { if(h < 0) h += 1; if(h > 1) h -= 1; @@ -157,16 +154,16 @@ static float hue_to_rgb(float v1, float v2, float h) return v1; } -static vec3 hsl_to_rgb(vec3 in) +static vec3 HslToRgb(vec3 in) { float v1, v2; - vec3 out; + vec3 Out; if(in.s == 0) { - out.r = in.l; - out.g = in.l; - out.b = in.l; + Out.r = in.l; + Out.g = in.l; + Out.b = in.l; } else { @@ -177,16 +174,16 @@ static vec3 hsl_to_rgb(vec3 in) v1 = 2 * in.l - v2; - out.r = hue_to_rgb(v1, v2, in.h + (1.0f/3.0f)); - out.g = hue_to_rgb(v1, v2, in.h); - out.b = hue_to_rgb(v1, v2, in.h - (1.0f/3.0f)); + Out.r = HueToRgb(v1, v2, in.h + (1.0f/3.0f)); + Out.g = HueToRgb(v1, v2, in.h); + Out.b = HueToRgb(v1, v2, in.h - (1.0f/3.0f)); } - return out; + return Out; } -vec4 SKINS::get_color(int v) +vec4 CSkins::GetColor(int v) { - vec3 r = hsl_to_rgb(vec3((v>>16)/255.0f, ((v>>8)&0xff)/255.0f, 0.5f+(v&0xff)/255.0f*0.5f)); + vec3 r = HslToRgb(vec3(((v>>16)&0xff)/255.0f, ((v>>8)&0xff)/255.0f, 0.5f+(v&0xff)/255.0f*0.5f)); return vec4(r.r, r.g, r.b, 1.0f); } diff --git a/src/game/client/components/skins.h b/src/game/client/components/skins.h new file mode 100644 index 00000000..f733140f --- /dev/null +++ b/src/game/client/components/skins.h @@ -0,0 +1,39 @@ +#ifndef GAME_CLIENT_COMPONENTS_SKINS_H +#define GAME_CLIENT_COMPONENTS_SKINS_H +#include <base/vmath.h> +#include <game/client/component.h> + +class CSkins : public CComponent +{ +public: + // do this better and nicer + struct CSkin + { + int m_OrgTexture; + int m_ColorTexture; + char m_aName[31]; + char m_aTerm[1]; + vec3 m_BloodColor; + } ; + + CSkins(); + + void Init(); + + vec4 GetColor(int v); + int Num(); + const CSkin *Get(int Index); + int Find(const char *pName); + +private: + enum + { + MAX_SKINS=256, + }; + + CSkin m_aSkins[MAX_SKINS]; + int m_NumSkins; + + static void SkinScan(const char *pName, int IsDir, void *pUser); +}; +#endif diff --git a/src/game/client/components/skins.hpp b/src/game/client/components/skins.hpp deleted file mode 100644 index 078fd71d..00000000 --- a/src/game/client/components/skins.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#include <base/vmath.hpp> -#include <game/client/component.hpp> - -class SKINS : public COMPONENT -{ -public: - // do this better and nicer - typedef struct - { - int org_texture; - int color_texture; - char name[31]; - char term[1]; - vec3 blood_color; - } SKIN; - - SKINS(); - - void init(); - - vec4 get_color(int v); - int num(); - const SKIN *get(int index); - int find(const char *name); - -private: - enum - { - MAX_SKINS=256, - }; - - SKIN skins[MAX_SKINS]; - int num_skins; - - static void skinscan(const char *name, int is_dir, void *user); -}; diff --git a/src/game/client/components/sounds.cpp b/src/game/client/components/sounds.cpp index dfa7e31b..84e45efa 100644 --- a/src/game/client/components/sounds.cpp +++ b/src/game/client/components/sounds.cpp @@ -1,54 +1,91 @@ -#include <engine/e_client_interface.h> -#include <game/generated/gc_data.hpp> -#include <game/client/gameclient.hpp> -#include <game/client/components/camera.hpp> -#include "sounds.hpp" +#include <engine/sound.h> +#include <game/generated/client_data.h> +#include <game/client/gameclient.h> +#include <game/client/components/camera.h> +#include "sounds.h" -void SOUNDS::on_init() +void CSounds::OnInit() { // setup sound channels - snd_set_channel(SOUNDS::CHN_GUI, 1.0f, 0.0f); - snd_set_channel(SOUNDS::CHN_MUSIC, 1.0f, 0.0f); - snd_set_channel(SOUNDS::CHN_WORLD, 0.9f, 1.0f); - snd_set_channel(SOUNDS::CHN_GLOBAL, 1.0f, 0.0f); + Sound()->SetChannel(CSounds::CHN_GUI, 1.0f, 0.0f); + Sound()->SetChannel(CSounds::CHN_MUSIC, 1.0f, 0.0f); + Sound()->SetChannel(CSounds::CHN_WORLD, 0.9f, 1.0f); + Sound()->SetChannel(CSounds::CHN_GLOBAL, 1.0f, 0.0f); - snd_set_listener_pos(0.0f, 0.0f); + Sound()->SetListenerPos(0.0f, 0.0f); + + ClearQueue(); +} + +void CSounds::OnReset() +{ + Sound()->StopAll(); + ClearQueue(); } -void SOUNDS::on_render() +void CSounds::OnRender() { // set listner pos - snd_set_listener_pos(gameclient.camera->center.x, gameclient.camera->center.y); + Sound()->SetListenerPos(m_pClient->m_pCamera->m_Center.x, m_pClient->m_pCamera->m_Center.y); + + // play sound from queue + if(m_QueuePos > 0) + { + int64 Now = time_get(); + if(m_QueueWaitTime <= Now) + { + Play(CHN_GLOBAL, m_aQueue[0], 1.0f, vec2(0,0)); + m_QueueWaitTime = Now+time_freq()*3/10; // wait 300ms before playing the next one + if(--m_QueuePos > 0) + mem_move(m_aQueue, m_aQueue+1, m_QueuePos*sizeof(int)); + } + } } -void SOUNDS::play_and_record(int chn, int setid, float vol, vec2 pos) +void CSounds::ClearQueue() { - NETMSG_SV_SOUNDGLOBAL msg; - msg.soundid = setid; - msg.pack(MSGFLAG_NOSEND|MSGFLAG_RECORD); - client_send_msg(); + mem_zero(m_aQueue, sizeof(m_aQueue)); + m_QueuePos = 0; + m_QueueWaitTime = time_get(); +} + +void CSounds::Enqueue(int SetId) +{ + // add sound to the queue + if(m_QueuePos < QUEUE_SIZE) + m_aQueue[m_QueuePos++] = SetId; +} + +void CSounds::PlayAndRecord(int Chn, int SetId, float Vol, vec2 Pos) +{ + CNetMsg_Sv_SoundGlobal Msg; + Msg.m_Soundid = SetId; + Client()->SendPackMsg(&Msg, MSGFLAG_NOSEND|MSGFLAG_RECORD); - play(chn, setid, vol, pos); + Play(Chn, SetId, Vol, Pos); } -void SOUNDS::play(int chn, int setid, float vol, vec2 pos) +void CSounds::Play(int Chn, int SetId, float Vol, vec2 Pos) { - SOUNDSET *set = &data->sounds[setid]; + if(SetId < 0 || SetId >= g_pData->m_NumSounds) + return; + + SOUNDSET *pSet = &g_pData->m_aSounds[SetId]; - if(!set->num_sounds) + if(!pSet->m_NumSounds) return; - if(set->num_sounds == 1) + if(pSet->m_NumSounds == 1) { - snd_play_at(chn, set->sounds[0].id, 0, pos.x, pos.y); + Sound()->PlayAt(Chn, pSet->m_aSounds[0].m_Id, 0, Pos.x, Pos.y); return; } // play a random one int id; do { - id = rand() % set->num_sounds; - } while(id == set->last); - snd_play_at(chn, set->sounds[id].id, 0, pos.x, pos.y); - set->last = id; + id = rand() % pSet->m_NumSounds; + } while(id == pSet->m_Last); + Sound()->PlayAt(Chn, pSet->m_aSounds[id].m_Id, 0, Pos.x, Pos.y); + pSet->m_Last = id; } diff --git a/src/game/client/components/sounds.h b/src/game/client/components/sounds.h new file mode 100644 index 00000000..ce74b85e --- /dev/null +++ b/src/game/client/components/sounds.h @@ -0,0 +1,36 @@ +#ifndef GAME_CLIENT_COMPONENTS_SOUNDS_H +#define GAME_CLIENT_COMPONENTS_SOUNDS_H +#include <game/client/component.h> + +class CSounds : public CComponent +{ + enum + { + QUEUE_SIZE = 32, + }; + int m_aQueue[QUEUE_SIZE]; + int m_QueuePos; + int64 m_QueueWaitTime; + +public: + // sound channels + enum + { + CHN_GUI=0, + CHN_MUSIC, + CHN_WORLD, + CHN_GLOBAL, + }; + + virtual void OnInit(); + virtual void OnReset(); + virtual void OnRender(); + + void ClearQueue(); + void Enqueue(int SetId); + void Play(int Channel, int SetId, float Vol, vec2 Pos); + void PlayAndRecord(int Channel, int SetId, float Vol, vec2 Pos); +}; + + +#endif diff --git a/src/game/client/components/sounds.hpp b/src/game/client/components/sounds.hpp deleted file mode 100644 index 95ddb1ec..00000000 --- a/src/game/client/components/sounds.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#include <game/client/component.hpp> - -class SOUNDS : public COMPONENT -{ -public: - // sound channels - enum - { - CHN_GUI=0, - CHN_MUSIC, - CHN_WORLD, - CHN_GLOBAL, - }; - - virtual void on_init(); - virtual void on_render(); - - void play(int chn, int setid, float vol, vec2 pos); - void play_and_record(int chn, int setid, float vol, vec2 pos); -}; - - diff --git a/src/game/client/components/voting.cpp b/src/game/client/components/voting.cpp index dcf5c954..17c0fe31 100644 --- a/src/game/client/components/voting.cpp +++ b/src/game/client/components/voting.cpp @@ -1,200 +1,219 @@ -#include <engine/e_client_interface.h> -#include <game/generated/g_protocol.hpp> -#include <base/vmath.hpp> -#include <game/client/render.hpp> -//#include <game/client/gameclient.hpp> -#include "voting.hpp" - -void VOTING::con_callvote(void *result, void *user_data) +#include <engine/shared/config.h> + +#include <game/generated/protocol.h> +#include <base/vmath.h> +#include <game/client/render.h> +//#include <game/client/gameclient.h> +#include "voting.h" + +void CVoting::ConCallvote(IConsole::IResult *pResult, void *pUserData) +{ + CVoting *pSelf = (CVoting*)pUserData; + pSelf->Callvote(pResult->GetString(0), pResult->GetString(1)); +} + +void CVoting::ConVote(IConsole::IResult *pResult, void *pUserData) +{ + CVoting *pSelf = (CVoting *)pUserData; + if(str_comp_nocase(pResult->GetString(0), "yes") == 0) + pSelf->Vote(1); + else if(str_comp_nocase(pResult->GetString(0), "no") == 0) + pSelf->Vote(-1); +} + +void CVoting::Callvote(const char *pType, const char *pValue) { - VOTING *self = (VOTING*)user_data; - self->callvote(console_arg_string(result, 0), console_arg_string(result, 1)); + CNetMsg_Cl_CallVote Msg = {0}; + Msg.m_Type = pType; + Msg.m_Value = pValue; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } -void VOTING::con_vote(void *result, void *user_data) +void CVoting::CallvoteKick(int ClientId) { - VOTING *self = (VOTING *)user_data; - if(str_comp_nocase(console_arg_string(result, 0), "yes") == 0) - self->vote(1); - else if(str_comp_nocase(console_arg_string(result, 0), "no") == 0) - self->vote(-1); + char Buf[32]; + str_format(Buf, sizeof(Buf), "%d", ClientId); + Callvote("kick", Buf); } -void VOTING::callvote(const char *type, const char *value) +void CVoting::CallvoteOption(int OptionId) { - NETMSG_CL_CALLVOTE msg = {0}; - msg.type = type; - msg.value = value; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); + CVoteOption *pOption = m_pFirst; + while(pOption && OptionId >= 0) + { + if(OptionId == 0) + { + Callvote("option", pOption->m_aCommand); + break; + } + + OptionId--; + pOption = pOption->m_pNext; + } } -void VOTING::callvote_kick(int client_id) +void CVoting::ForcevoteKick(int ClientId) { - char buf[32]; - str_format(buf, sizeof(buf), "%d", client_id); - callvote("kick", buf); + char Buf[32]; + str_format(Buf, sizeof(Buf), "kick %d", ClientId); + Client()->Rcon(Buf); } -void VOTING::callvote_option(int option_id) +void CVoting::ForcevoteOption(int OptionId) { - VOTEOPTION *option = this->first; - while(option && option_id >= 0) + CVoteOption *pOption = m_pFirst; + while(pOption && OptionId >= 0) { - if(option_id == 0) + if(OptionId == 0) { - callvote("option", option->command); + Client()->Rcon(pOption->m_aCommand); break; } - option_id--; - option = option->next; + OptionId--; + pOption = pOption->m_pNext; } } -void VOTING::vote(int v) +void CVoting::Vote(int v) { - NETMSG_CL_VOTE msg = {v}; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); + CNetMsg_Cl_Vote Msg = {v}; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } -VOTING::VOTING() +CVoting::CVoting() { - heap = 0; - clearoptions(); - on_reset(); + ClearOptions(); + OnReset(); } -void VOTING::clearoptions() +void CVoting::ClearOptions() { - if(heap) - memheap_destroy(heap); - heap = memheap_create(); + m_Heap.Reset(); - first = 0; - last = 0; + m_pFirst = 0; + m_pLast = 0; } -void VOTING::on_reset() +void CVoting::OnReset() { - closetime = 0; - description[0] = 0; - command[0] = 0; - yes = no = pass = total = 0; - voted = 0; + m_Closetime = 0; + m_aDescription[0] = 0; + m_aCommand[0] = 0; + m_Yes = m_No = m_Pass = m_Total = 0; + m_Voted = 0; } -void VOTING::on_console_init() +void CVoting::OnConsoleInit() { - MACRO_REGISTER_COMMAND("callvote", "sr", CFGFLAG_CLIENT, con_callvote, this, "Call vote"); - MACRO_REGISTER_COMMAND("vote", "r", CFGFLAG_CLIENT, con_vote, this, "Vote yes/no"); + Console()->Register("callvote", "sr", CFGFLAG_CLIENT, ConCallvote, this, "Call vote"); + Console()->Register("vote", "r", CFGFLAG_CLIENT, ConVote, this, "Vote yes/no"); } -void VOTING::on_message(int msgtype, void *rawmsg) +void CVoting::OnMessage(int MsgType, void *pRawMsg) { - if(msgtype == NETMSGTYPE_SV_VOTE_SET) + if(MsgType == NETMSGTYPE_SV_VOTESET) { - NETMSG_SV_VOTE_SET *msg = (NETMSG_SV_VOTE_SET *)rawmsg; - if(msg->timeout) + CNetMsg_Sv_VoteSet *pMsg = (CNetMsg_Sv_VoteSet *)pRawMsg; + if(pMsg->m_Timeout) { - on_reset(); - str_copy(description, msg->description, sizeof(description)); - str_copy(command, msg->command, sizeof(description)); - closetime = time_get() + time_freq() * msg->timeout; + OnReset(); + str_copy(m_aDescription, pMsg->m_pDescription, sizeof(m_aDescription)); + str_copy(m_aCommand, pMsg->m_pCommand, sizeof(m_aCommand)); + m_Closetime = time_get() + time_freq() * pMsg->m_Timeout; } else - on_reset(); + OnReset(); } - else if(msgtype == NETMSGTYPE_SV_VOTE_STATUS) + else if(MsgType == NETMSGTYPE_SV_VOTESTATUS) { - NETMSG_SV_VOTE_STATUS *msg = (NETMSG_SV_VOTE_STATUS *)rawmsg; - yes = msg->yes; - no = msg->no; - pass = msg->pass; - total = msg->total; + CNetMsg_Sv_VoteStatus *pMsg = (CNetMsg_Sv_VoteStatus *)pRawMsg; + m_Yes = pMsg->m_Yes; + m_No = pMsg->m_No; + m_Pass = pMsg->m_Pass; + m_Total = pMsg->m_Total; } - else if(msgtype == NETMSGTYPE_SV_VOTE_CLEAROPTIONS) + else if(MsgType == NETMSGTYPE_SV_VOTECLEAROPTIONS) { - clearoptions(); + ClearOptions(); } - else if(msgtype == NETMSGTYPE_SV_VOTE_OPTION) + else if(MsgType == NETMSGTYPE_SV_VOTEOPTION) { - NETMSG_SV_VOTE_OPTION *msg = (NETMSG_SV_VOTE_OPTION *)rawmsg; - int len = str_length(msg->command); + CNetMsg_Sv_VoteOption *pMsg = (CNetMsg_Sv_VoteOption *)pRawMsg; + int Len = str_length(pMsg->m_pCommand); - VOTEOPTION *option = (VOTEOPTION *)memheap_allocate(heap, sizeof(VOTEOPTION) + len); - option->next = 0; - option->prev = last; - if(option->prev) - option->prev->next = option; - last = option; - if(!first) - first = option; + CVoteOption *pOption = (CVoteOption *)m_Heap.Allocate(sizeof(CVoteOption) + Len); + pOption->m_pNext = 0; + pOption->m_pPrev = m_pLast; + if(pOption->m_pPrev) + pOption->m_pPrev->m_pNext = pOption; + m_pLast = pOption; + if(!m_pFirst) + m_pFirst = pOption; - mem_copy(option->command, msg->command, len+1); + mem_copy(pOption->m_aCommand, pMsg->m_pCommand, Len+1); } } -void VOTING::on_render() +void CVoting::OnRender() { } -void VOTING::render_bars(CUIRect bars, bool text) +void CVoting::RenderBars(CUIRect Bars, bool Text) { - RenderTools()->DrawUIRect(&bars, vec4(0.8f,0.8f,0.8f,0.5f), CUI::CORNER_ALL, bars.h/3); + RenderTools()->DrawUIRect(&Bars, vec4(0.8f,0.8f,0.8f,0.5f), CUI::CORNER_ALL, Bars.h/3); - CUIRect splitter = bars; - splitter.x = splitter.x+splitter.w/2; - splitter.w = splitter.h/2.0f; - splitter.x -= splitter.w/2; - RenderTools()->DrawUIRect(&splitter, vec4(0.4f,0.4f,0.4f,0.5f), CUI::CORNER_ALL, splitter.h/4); + CUIRect Splitter = Bars; + Splitter.x = Splitter.x+Splitter.w/2; + Splitter.w = Splitter.h/2.0f; + Splitter.x -= Splitter.w/2; + RenderTools()->DrawUIRect(&Splitter, vec4(0.4f,0.4f,0.4f,0.5f), CUI::CORNER_ALL, Splitter.h/4); - if(total) + if(m_Total) { - CUIRect pass_area = bars; - if(yes) + CUIRect PassArea = Bars; + if(m_Yes) { - CUIRect yes_area = bars; - yes_area.w *= yes/(float)total; - RenderTools()->DrawUIRect(&yes_area, vec4(0.2f,0.9f,0.2f,0.85f), CUI::CORNER_ALL, bars.h/3); + CUIRect YesArea = Bars; + YesArea.w *= m_Yes/(float)m_Total; + RenderTools()->DrawUIRect(&YesArea, vec4(0.2f,0.9f,0.2f,0.85f), CUI::CORNER_ALL, Bars.h/3); - if(text) + if(Text) { - char buf[256]; - str_format(buf, sizeof(buf), "%d", yes); - UI()->DoLabel(&yes_area, buf, bars.h*0.75f, 0); + char Buf[256]; + str_format(Buf, sizeof(Buf), "%d", m_Yes); + UI()->DoLabel(&YesArea, Buf, Bars.h*0.75f, 0); } - pass_area.x += yes_area.w; - pass_area.w -= yes_area.w; + PassArea.x += YesArea.w; + PassArea.w -= YesArea.w; } - if(no) + if(m_No) { - CUIRect no_area = bars; - no_area.w *= no/(float)total; - no_area.x = (bars.x + bars.w)-no_area.w; - RenderTools()->DrawUIRect(&no_area, vec4(0.9f,0.2f,0.2f,0.85f), CUI::CORNER_ALL, bars.h/3); + CUIRect NoArea = Bars; + NoArea.w *= m_No/(float)m_Total; + NoArea.x = (Bars.x + Bars.w)-NoArea.w; + RenderTools()->DrawUIRect(&NoArea, vec4(0.9f,0.2f,0.2f,0.85f), CUI::CORNER_ALL, Bars.h/3); - if(text) + if(Text) { - char buf[256]; - str_format(buf, sizeof(buf), "%d", no); - UI()->DoLabel(&no_area, buf, bars.h*0.75f, 0); + char Buf[256]; + str_format(Buf, sizeof(Buf), "%d", m_No); + UI()->DoLabel(&NoArea, Buf, Bars.h*0.75f, 0); } - pass_area.w -= no_area.w; + PassArea.w -= NoArea.w; } - if(text && pass) + if(Text && m_Pass) { - char buf[256]; - str_format(buf, sizeof(buf), "%d", pass); - UI()->DoLabel(&pass_area, buf, bars.h*0.75f, 0); + char Buf[256]; + str_format(Buf, sizeof(Buf), "%d", m_Pass); + UI()->DoLabel(&PassArea, Buf, Bars.h*0.75f, 0); } } } diff --git a/src/game/client/components/voting.h b/src/game/client/components/voting.h new file mode 100644 index 00000000..1f5d2fc5 --- /dev/null +++ b/src/game/client/components/voting.h @@ -0,0 +1,58 @@ +#ifndef GAME_CLIENT_COMPONENTS_VOTING_H +#define GAME_CLIENT_COMPONENTS_VOTING_H +#include <game/client/component.h> +#include <game/client/ui.h> +#include <engine/shared/memheap.h> + +class CVoting : public CComponent +{ + CHeap m_Heap; + + static void ConCallvote(IConsole::IResult *pResult, void *pUserData); + static void ConVote(IConsole::IResult *pResult, void *pUserData); + + int64 m_Closetime; + char m_aDescription[512]; + char m_aCommand[512]; + int m_Voted; + + void ClearOptions(); + void Callvote(const char *pType, const char *pValue); + +public: + + struct CVoteOption + { + CVoteOption *m_pNext; + CVoteOption *m_pPrev; + char m_aCommand[1]; + }; + + CVoteOption *m_pFirst; + CVoteOption *m_pLast; + + CVoting(); + virtual void OnReset(); + virtual void OnConsoleInit(); + virtual void OnMessage(int Msgtype, void *pRawMsg); + virtual void OnRender(); + + void RenderBars(CUIRect Bars, bool Text); + + void CallvoteKick(int ClientId); + void CallvoteOption(int Option); + void ForcevoteKick(int ClientId); + void ForcevoteOption(int Option); + + void Vote(int v); // -1 = no, 1 = yes + + int SecondsLeft() { return (m_Closetime - time_get())/time_freq(); } + bool IsVoting() { return m_Closetime != 0; } + int TakenChoice() const { return m_Voted; } + const char *VoteDescription() const { return m_aDescription; } + const char *VoteCommand() const { return m_aCommand; } + + int m_Yes, m_No, m_Pass, m_Total; +}; + +#endif diff --git a/src/game/client/components/voting.hpp b/src/game/client/components/voting.hpp deleted file mode 100644 index e04e1840..00000000 --- a/src/game/client/components/voting.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#include <game/client/component.hpp> -#include <game/client/ui.hpp> -#include <engine/e_memheap.h> - -class VOTING : public COMPONENT -{ - HEAP *heap; - - static void con_callvote(void *result, void *user_data); - static void con_vote(void *result, void *user_data); - - int64 closetime; - char description[512]; - char command[512]; - int voted; - - void clearoptions(); - void callvote(const char *type, const char *value); - -public: - - struct VOTEOPTION - { - VOTEOPTION *next; - VOTEOPTION *prev; - char command[1]; - }; - VOTEOPTION *first; - VOTEOPTION *last; - - VOTING(); - virtual void on_reset(); - virtual void on_console_init(); - virtual void on_message(int msgtype, void *rawmsg); - virtual void on_render(); - - void render_bars(CUIRect bars, bool text); - - void callvote_kick(int client_id); - void callvote_option(int option); - - void vote(int v); // -1 = no, 1 = yes - - int seconds_left() { return (closetime - time_get())/time_freq(); } - bool is_voting() { return closetime != 0; } - int taken_choice() const { return voted; } - const char *vote_description() const { return description; } - const char *vote_command() const { return command; } - - int yes, no, pass, total; -}; - diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 25a42620..d59a7a04 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1,82 +1,87 @@ -#include <string.h> -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> -#include <engine/e_demorec.h> - -#include <game/generated/g_protocol.hpp> -#include <game/generated/gc_data.hpp> - -#include <game/layers.hpp> -#include <game/localization.hpp> -#include "render.hpp" - -#include "gameclient.hpp" - -#include "components/binds.hpp" -#include "components/broadcast.hpp" -#include "components/camera.hpp" -#include "components/chat.hpp" -#include "components/console.hpp" -#include "components/controls.hpp" -#include "components/damageind.hpp" -#include "components/debughud.hpp" -#include "components/effects.hpp" -#include "components/emoticon.hpp" -#include "components/flow.hpp" -#include "components/hud.hpp" -#include "components/items.hpp" -#include "components/killmessages.hpp" -#include "components/mapimages.hpp" -#include "components/maplayers.hpp" -#include "components/menus.hpp" -#include "components/motd.hpp" -#include "components/particles.hpp" -#include "components/players.hpp" -#include "components/nameplates.hpp" -#include "components/scoreboard.hpp" -#include "components/skins.hpp" -#include "components/sounds.hpp" -#include "components/voting.hpp" - -GAMECLIENT gameclient; +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/sound.h> +#include <engine/demo.h> +#include <engine/map.h> +#include <engine/storage.h> +#include <engine/serverbrowser.h> +#include <engine/shared/demorec.h> +#include <engine/shared/config.h> + +#include <game/generated/protocol.h> +#include <game/generated/client_data.h> + +#include <game/localization.h> +#include <game/version.h> +#include "render.h" + +#include "gameclient.h" + +#include "components/binds.h" +#include "components/broadcast.h" +#include "components/camera.h" +#include "components/chat.h" +#include "components/console.h" +#include "components/controls.h" +#include "components/damageind.h" +#include "components/debughud.h" +#include "components/effects.h" +#include "components/emoticon.h" +#include "components/flow.h" +#include "components/hud.h" +#include "components/items.h" +#include "components/killmessages.h" +#include "components/mapimages.h" +#include "components/maplayers.h" +#include "components/menus.h" +#include "components/motd.h" +#include "components/particles.h" +#include "components/players.h" +#include "components/nameplates.h" +#include "components/scoreboard.h" +#include "components/skins.h" +#include "components/sounds.h" +#include "components/voting.h" + +CGameClient g_GameClient; // instanciate all systems -static KILLMESSAGES killmessages; -static CAMERA camera; -static CHAT chat; -static MOTD motd; -static BROADCAST broadcast; -static CONSOLE console; -static BINDS binds; -static PARTICLES particles; -static MENUS menus; -static SKINS skins; -static FLOW flow; -static HUD hud; -static DEBUGHUD debughud; -static CONTROLS controls; -static EFFECTS effects; -static SCOREBOARD scoreboard; -static SOUNDS sounds; -static EMOTICON emoticon; -static DAMAGEIND damageind; -static VOTING voting; - -static PLAYERS players; -static NAMEPLATES nameplates; -static ITEMS items; -static MAPIMAGES mapimages; - -static MAPLAYERS maplayers_background(MAPLAYERS::TYPE_BACKGROUND); -static MAPLAYERS maplayers_foreground(MAPLAYERS::TYPE_FOREGROUND); - -GAMECLIENT::STACK::STACK() { num = 0; } -void GAMECLIENT::STACK::add(class COMPONENT *component) { components[num++] = component; } - -static int load_current; -static int load_total; - -static void load_sounds_thread(void *do_render) +static CKillMessages gs_KillMessages; +static CCamera gs_Camera; +static CChat gs_Chat; +static CMotd gs_Motd; +static CBroadcast gs_Broadcast; +static CGameConsole gs_GameConsole; +static CBinds gs_Binds; +static CParticles gs_Particles; +static CMenus gs_Menus; +static CSkins gs_Skins; +static CFlow gs_Flow; +static CHud gs_Hud; +static CDebugHud gs_DebugHud; +static CControls gs_Controls; +static CEffects gs_Effects; +static CScoreboard gs_Scoreboard; +static CSounds gs_Sounds; +static CEmoticon gs_Emoticon; +static CDamageInd gsDamageInd; +static CVoting gs_Voting; + +static CPlayers gs_Players; +static CNamePlates gs_NamePlates; +static CItems gs_Items; +static CMapImages gs_MapImages; + +static CMapLayers gs_MapLayersBackGround(CMapLayers::TYPE_BACKGROUND); +static CMapLayers gs_MapLayersForeGround(CMapLayers::TYPE_FOREGROUND); + +CGameClient::CStack::CStack() { m_Num = 0; } +void CGameClient::CStack::Add(class CComponent *pComponent) { m_paComponents[m_Num++] = pComponent; } + +static int gs_LoadCurrent; +static int gs_LoadTotal; + +/*static void load_sounds_thread(void *do_render) { // load sounds for(int s = 0; s < data->num_sounds; s++) @@ -85,274 +90,235 @@ static void load_sounds_thread(void *do_render) gameclient.menus->render_loading(load_current/(float)load_total); for(int i = 0; i < data->sounds[s].num_sounds; i++) { - int id = snd_load_wv(data->sounds[s].sounds[i].filename); + int id = Sound()->LoadWV(data->sounds[s].sounds[i].filename); data->sounds[s].sounds[i].id = id; } if(do_render) load_current++; } -} +}*/ -static void con_serverdummy(void *result, void *user_data) +static void ConServerDummy(IConsole::IResult *pResult, void *pUserData) { dbg_msg("client", "this command is not available on the client"); } -#include <base/tl/sorted_array.hpp> - -void GAMECLIENT::on_console_init() -{ - if(0) - { - int ints[4] = {0,1,2,3}; - for(int s = 1; s < 4; s++) - { - //s = 2; - plain_range_sorted<int> test_sorted_range(ints, ints+s); - plain_range_sorted<int> res1, res2; - - //res2 = partition_binary(test_sorted_range, 1); - - //for(int i = 0; i < 4; i++) - // dbg_assert(partition_linear(test_sorted_range, i).front() == i, "partition linear failed"); - - - dbg_msg("", "size %d", s); - - for(int i = -1; i < 5; i++) - { - res1 = partition_linear(test_sorted_range, i); - dbg_msg("", "\tlin %d == %d", i, res1.empty()?-1:res1.front()); +#include <base/tl/sorted_array.h> - res2 = partition_binary(test_sorted_range, i); - dbg_msg("", "\tbin %d == %d", i, res2.empty()?-1:res2.front()); - //dbg_assert(partition_binary(plain_range_sorted<int>(ints, ints+6), i).front() == i+1, "partition binary failed"); - } - } //*/ +const char *CGameClient::Version() { return GAME_VERSION; } +const char *CGameClient::NetVersion() { return GAME_NETVERSION; } +const char *CGameClient::GetItemName(int Type) { return m_NetObjHandler.GetObjName(Type); } - sorted_array<int> test; - test.add(4); - test.add(1); - - for(int i = 0; i < 100; i++) - { - int this_add = rand(); - test.add(this_add); - if(!sort_verify(test.all())) - { - dbg_msg("", "error inserting %d", this_add); - for(sorted_array<int>::range r = test.all(); !r.empty(); r.pop_front()) - dbg_msg("", "%d", r.front()); - exit(-1); - } - }/* - - - test.add(1); - test.add(4); - test.add(3); - test.add(4); - test.add(3); - test.add(2); - //test.insert(1, 1); - for(sorted_array<int>::range r = test.all(); !r.empty(); r.pop_front()) - dbg_msg("", "%d", r.front()); - */ - - sort_verify(test.all()); - /* - for(int i = 0; i < 15; i++) - { - dbg_msg("", "found %d == %d", i, !find_binary(test.all(), i).empty()); - }*/ - - exit(-1); - } +void CGameClient::OnConsoleInit() +{ + m_pClient = Kernel()->RequestInterface<IClient>(); + m_pGraphics = Kernel()->RequestInterface<IGraphics>(); + m_pTextRender = Kernel()->RequestInterface<ITextRender>(); + m_pSound = Kernel()->RequestInterface<ISound>(); + m_pInput = Kernel()->RequestInterface<IInput>(); + m_pConsole = Kernel()->RequestInterface<IConsole>(); + m_pStorage = Kernel()->RequestInterface<IStorage>(); + m_pDemoPlayer = Kernel()->RequestInterface<IDemoPlayer>(); + m_pServerBrowser = Kernel()->RequestInterface<IServerBrowser>(); // setup pointers - binds = &::binds; - console = &::console; - particles = &::particles; - menus = &::menus; - skins = &::skins; - chat = &::chat; - flow = &::flow; - camera = &::camera; - controls = &::controls; - effects = &::effects; - sounds = &::sounds; - motd = &::motd; - damageind = &::damageind; - mapimages = &::mapimages; - voting = &::voting; + m_pBinds = &::gs_Binds; + m_pGameConsole = &::gs_GameConsole; + m_pParticles = &::gs_Particles; + m_pMenus = &::gs_Menus; + m_pSkins = &::gs_Skins; + m_pChat = &::gs_Chat; + m_pFlow = &::gs_Flow; + m_pCamera = &::gs_Camera; + m_pControls = &::gs_Controls; + m_pEffects = &::gs_Effects; + m_pSounds = &::gs_Sounds; + m_pMotd = &::gs_Motd; + m_pDamageind = &::gsDamageInd; + m_pMapimages = &::gs_MapImages; + m_pVoting = &::gs_Voting; // make a list of all the systems, make sure to add them in the corrent render order - all.add(skins); - all.add(mapimages); - all.add(effects); // doesn't render anything, just updates effects - all.add(particles); - all.add(binds); - all.add(controls); - all.add(camera); - all.add(sounds); - all.add(voting); - all.add(particles); // doesn't render anything, just updates all the particles - - all.add(&maplayers_background); // first to render - all.add(&particles->render_trail); - all.add(&particles->render_explosions); - all.add(&items); - all.add(&players); - all.add(&maplayers_foreground); - all.add(&nameplates); - all.add(&particles->render_general); - all.add(damageind); - all.add(&hud); - all.add(&emoticon); - all.add(&killmessages); - all.add(chat); - all.add(&broadcast); - all.add(&debughud); - all.add(&scoreboard); - all.add(motd); - all.add(menus); - all.add(console); + m_All.Add(m_pSkins); + m_All.Add(m_pMapimages); + m_All.Add(m_pEffects); // doesn't render anything, just updates effects + m_All.Add(m_pParticles); + m_All.Add(m_pBinds); + m_All.Add(m_pControls); + m_All.Add(m_pCamera); + m_All.Add(m_pSounds); + m_All.Add(m_pVoting); + m_All.Add(m_pParticles); // doesn't render anything, just updates all the particles + + m_All.Add(&gs_MapLayersBackGround); // first to render + m_All.Add(&m_pParticles->m_RenderTrail); + m_All.Add(&m_pParticles->m_RenderExplosions); + m_All.Add(&gs_Items); + m_All.Add(&gs_Players); + m_All.Add(&gs_MapLayersForeGround); + m_All.Add(&gs_NamePlates); + m_All.Add(&m_pParticles->m_RenderGeneral); + m_All.Add(m_pDamageind); + m_All.Add(&gs_Hud); + m_All.Add(&gs_Emoticon); + m_All.Add(&gs_KillMessages); + m_All.Add(m_pChat); + m_All.Add(&gs_Broadcast); + m_All.Add(&gs_DebugHud); + m_All.Add(&gs_Scoreboard); + m_All.Add(m_pMotd); + m_All.Add(m_pMenus); + m_All.Add(m_pGameConsole); // build the input stack - input.add(&menus->binder); // this will take over all input when we want to bind a key - input.add(&binds->special_binds); - input.add(console); - input.add(chat); // chat has higher prio due to tha you can quit it by pressing esc - input.add(motd); // for pressing esc to remove it - input.add(menus); - input.add(&emoticon); - input.add(controls); - input.add(binds); + m_Input.Add(&m_pMenus->m_Binder); // this will take over all input when we want to bind a key + m_Input.Add(&m_pBinds->m_SpecialBinds); + m_Input.Add(m_pGameConsole); + m_Input.Add(m_pChat); // chat has higher prio due to tha you can quit it by pressing esc + m_Input.Add(m_pMotd); // for pressing esc to remove it + m_Input.Add(m_pMenus); + m_Input.Add(&gs_Emoticon); + m_Input.Add(m_pControls); + m_Input.Add(m_pBinds); // add the some console commands - MACRO_REGISTER_COMMAND("team", "i", CFGFLAG_CLIENT, con_team, this, "Switch team"); - MACRO_REGISTER_COMMAND("kill", "", CFGFLAG_CLIENT, con_kill, this, "Kill yourself"); + Console()->Register("team", "i", CFGFLAG_CLIENT, ConTeam, this, "Switch team"); + Console()->Register("kill", "", CFGFLAG_CLIENT, ConKill, this, "Kill yourself"); // register server dummy commands for tab completion - MACRO_REGISTER_COMMAND("tune", "si", CFGFLAG_SERVER, con_serverdummy, 0, "Tune variable to value"); - MACRO_REGISTER_COMMAND("tune_reset", "", CFGFLAG_SERVER, con_serverdummy, 0, "Reset tuning"); - MACRO_REGISTER_COMMAND("tune_dump", "", CFGFLAG_SERVER, con_serverdummy, 0, "Dump tuning"); - MACRO_REGISTER_COMMAND("change_map", "r", CFGFLAG_SERVER, con_serverdummy, 0, "Change map"); - MACRO_REGISTER_COMMAND("restart", "?i", CFGFLAG_SERVER, con_serverdummy, 0, "Restart in x seconds"); - MACRO_REGISTER_COMMAND("broadcast", "r", CFGFLAG_SERVER, con_serverdummy, 0, "Broadcast message"); - /*MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_SERVER, con_serverdummy, 0);*/ - MACRO_REGISTER_COMMAND("set_team", "ii", CFGFLAG_SERVER, con_serverdummy, 0, "Set team of player to team"); - MACRO_REGISTER_COMMAND("addvote", "r", CFGFLAG_SERVER, con_serverdummy, 0, "Add a voting option"); - /*MACRO_REGISTER_COMMAND("vote", "", CFGFLAG_SERVER, con_serverdummy, 0);*/ + Console()->Register("tune", "si", CFGFLAG_SERVER, ConServerDummy, 0, "Tune variable to value"); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConServerDummy, 0, "Reset tuning"); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConServerDummy, 0, "Dump tuning"); + Console()->Register("change_map", "r", CFGFLAG_SERVER, ConServerDummy, 0, "Change map"); + Console()->Register("restart", "?i", CFGFLAG_SERVER, ConServerDummy, 0, "Restart in x seconds"); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConServerDummy, 0, "Broadcast message"); + //MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_SERVER, con_serverdummy, 0); + Console()->Register("set_team", "ii", CFGFLAG_SERVER, ConServerDummy, 0, "Set team of player to team"); + Console()->Register("addvote", "r", CFGFLAG_SERVER, ConServerDummy, 0, "Add a voting option"); + //MACRO_REGISTER_COMMAND("vote", "", CFGFLAG_SERVER, con_serverdummy, 0); + + + // propagate pointers + m_UI.SetGraphics(Graphics(), TextRender()); + m_RenderTools.m_pGraphics = Graphics(); + m_RenderTools.m_pUI = UI(); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->m_pClient = this; // let all the other components register their console commands - for(int i = 0; i < all.num; i++) - all.components[i]->on_console_init(); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnConsoleInit(); // - { static COMMANDCHAIN chain; console_chain_command("player_name", &chain, conchain_special_infoupdate, this); } - { static COMMANDCHAIN chain; console_chain_command("player_use_custom_color", &chain, conchain_special_infoupdate, this); } - { static COMMANDCHAIN chain; console_chain_command("player_color_body", &chain, conchain_special_infoupdate, this); } - { static COMMANDCHAIN chain; console_chain_command("player_color_feet", &chain, conchain_special_infoupdate, this); } - { static COMMANDCHAIN chain; console_chain_command("player_skin", &chain, conchain_special_infoupdate, this); } - - - + Console()->Chain("player_name", ConchainSpecialInfoupdate, this); + Console()->Chain("player_use_custom_color", ConchainSpecialInfoupdate, this); + Console()->Chain("player_color_body", ConchainSpecialInfoupdate, this); + Console()->Chain("player_color_feet", ConchainSpecialInfoupdate, this); + Console()->Chain("player_skin", ConchainSpecialInfoupdate, this); // - suppress_events = false; + m_SuppressEvents = false; } -void GAMECLIENT::on_init() +void CGameClient::OnInit() { - // set the language - localization.load(config.cl_languagefile); + //m_pServerBrowser = Kernel()->RequestInterface<IServerBrowser>(); - // propagate pointers - m_UI.SetGraphics(Graphics()); - m_RenderTools.m_pGraphics = Graphics(); - m_RenderTools.m_pUI = UI(); - for(int i = 0; i < all.num; i++) - all.components[i]->client = this; + // set the language + g_Localization.Load(g_Config.m_ClLanguagefile); // init all components - for(int i = 0; i < all.num; i++) - all.components[i]->on_init(); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnInit(); // setup item sizes for(int i = 0; i < NUM_NETOBJTYPES; i++) - snap_set_staticsize(i, netobj_get_size(i)); + Client()->SnapSetStaticsize(i, m_NetObjHandler.GetObjSize(i)); - int64 start = time_get(); + int64 Start = time_get(); // load default font - static FONT *default_font; + static CFont *pDefaultFont; //default_font = gfx_font_load("data/fonts/sazanami-gothic.ttf"); - default_font = gfx_font_load("data/fonts/vera.ttf"); - gfx_text_set_default_font(default_font); + pDefaultFont = TextRender()->LoadFont("data/fonts/vera.ttf"); + TextRender()->SetDefaultFont(pDefaultFont); - config.cl_threadsoundloading = 0; + g_Config.m_ClThreadsoundloading = 0; // setup load amount - load_total = data->num_images; - load_current = 0; - if(!config.cl_threadsoundloading) - load_total += data->num_sounds; + gs_LoadTotal = g_pData->m_NumImages; + gs_LoadCurrent = 0; + if(!g_Config.m_ClThreadsoundloading) + gs_LoadTotal += g_pData->m_NumSounds; // load textures - for(int i = 0; i < data->num_images; i++) + for(int i = 0; i < g_pData->m_NumImages; i++) { - gameclient.menus->render_loading(load_current/load_total); - data->images[i].id = Graphics()->LoadTexture(data->images[i].filename, IMG_AUTO, 0); - load_current++; + g_GameClient.m_pMenus->RenderLoading(gs_LoadCurrent/gs_LoadTotal); + g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, CImageInfo::FORMAT_AUTO, 0); + gs_LoadCurrent++; } - ::skins.init(); + ::gs_Skins.Init(); + - if(config.cl_threadsoundloading) + // TODO: Refactor: fix threaded loading of sounds again + // load sounds + { + bool DoRender = true; + for(int s = 0; s < g_pData->m_NumSounds; s++) + { + if(DoRender) + g_GameClient.m_pMenus->RenderLoading(gs_LoadCurrent/(float)gs_LoadTotal); + for(int i = 0; i < g_pData->m_aSounds[s].m_NumSounds; i++) + { + int id = Sound()->LoadWV(g_pData->m_aSounds[s].m_aSounds[i].m_pFilename); + g_pData->m_aSounds[s].m_aSounds[i].m_Id = id; + } + + if(DoRender) + gs_LoadCurrent++; + } + } + + /*if(config.cl_threadsoundloading) thread_create(load_sounds_thread, 0); else - load_sounds_thread((void*)1); + load_sounds_thread((void*)1);*/ - for(int i = 0; i < all.num; i++) - all.components[i]->on_reset(); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnReset(); - int64 end = time_get(); - dbg_msg("", "%f.2ms", ((end-start)*1000)/(float)time_freq()); + int64 End = time_get(); + dbg_msg("", "%f.2ms", ((End-Start)*1000)/(float)time_freq()); - servermode = SERVERMODE_PURE; + m_ServerMode = SERVERMODE_PURE; } -void GAMECLIENT::on_save() -{ - for(int i = 0; i < all.num; i++) - all.components[i]->on_save(); -} - -void GAMECLIENT::dispatch_input() +void CGameClient::DispatchInput() { // handle mouse movement int x=0, y=0; - inp_mouse_relative(&x, &y); + Input()->MouseRelative(&x, &y); if(x || y) { - for(int h = 0; h < input.num; h++) + for(int h = 0; h < m_Input.m_Num; h++) { - if(input.components[h]->on_mousemove(x, y)) + if(m_Input.m_paComponents[h]->OnMouseMove(x, y)) break; } } // handle key presses - for(int i = 0; i < inp_num_events(); i++) + for(int i = 0; i < Input()->NumEvents(); i++) { - INPUT_EVENT e = inp_get_event(i); + IInput::CEvent e = Input()->GetEvent(i); - for(int h = 0; h < input.num; h++) + for(int h = 0; h < m_Input.m_Num; h++) { - if(input.components[h]->on_input(e)) + if(m_Input.m_paComponents[h]->OnInput(e)) { //dbg_msg("", "%d char=%d key=%d flags=%d", h, e.ch, e.key, e.flags); break; @@ -361,102 +327,104 @@ void GAMECLIENT::dispatch_input() } // clear all events for this frame - inp_clear_events(); + Input()->ClearEvents(); } -int GAMECLIENT::on_snapinput(int *data) +int CGameClient::OnSnapInput(int *pData) { - return controls->snapinput(data); + return m_pControls->SnapInput(pData); } -void GAMECLIENT::on_connected() +void CGameClient::OnConnected() { - layers_init(); - col_init(); - RenderTools()->render_tilemap_generate_skip(); + m_Layers.Init(Kernel()); + m_Collision.Init(Layers()); + + RenderTools()->RenderTilemapGenerateSkip(Layers()); - for(int i = 0; i < all.num; i++) + for(int i = 0; i < m_All.m_Num; i++) { - all.components[i]->on_mapload(); - all.components[i]->on_reset(); + m_All.m_paComponents[i]->OnMapLoad(); + m_All.m_paComponents[i]->OnReset(); } - SERVER_INFO current_server_info; - client_serverinfo(¤t_server_info); + CServerInfo CurrentServerInfo; + Client()->GetServerInfo(&CurrentServerInfo); - servermode = SERVERMODE_PURE; + m_ServerMode = SERVERMODE_PURE; + m_LastSendInfo = 0; // send the inital info - send_info(true); + SendInfo(true); } -void GAMECLIENT::on_reset() +void CGameClient::OnReset() { // clear out the invalid pointers - last_new_predicted_tick = -1; - mem_zero(&gameclient.snap, sizeof(gameclient.snap)); + m_LastNewPredictedTick = -1; + mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap)); for(int i = 0; i < MAX_CLIENTS; i++) { - clients[i].name[0] = 0; - clients[i].skin_id = 0; - clients[i].team = 0; - clients[i].angle = 0; - clients[i].emoticon = 0; - clients[i].emoticon_start = -1; - clients[i].skin_info.texture = gameclient.skins->get(0)->color_texture; - clients[i].skin_info.color_body = vec4(1,1,1,1); - clients[i].skin_info.color_feet = vec4(1,1,1,1); - clients[i].update_render_info(); + m_aClients[i].m_aName[0] = 0; + m_aClients[i].m_SkinId = 0; + m_aClients[i].m_Team = 0; + m_aClients[i].m_Angle = 0; + m_aClients[i].m_Emoticon = 0; + m_aClients[i].m_EmoticonStart = -1; + m_aClients[i].m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(0)->m_ColorTexture; + m_aClients[i].m_SkinInfo.m_ColorBody = vec4(1,1,1,1); + m_aClients[i].m_SkinInfo.m_ColorFeet = vec4(1,1,1,1); + m_aClients[i].UpdateRenderInfo(); } - for(int i = 0; i < all.num; i++) - all.components[i]->on_reset(); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnReset(); } -void GAMECLIENT::update_local_character_pos() +void CGameClient::UpdateLocalCharacterPos() { - if(config.cl_predict && client_state() != CLIENTSTATE_DEMOPLAYBACK) + if(g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) { - if(!snap.local_character || (snap.local_character->health < 0) || (snap.gameobj && snap.gameobj->game_over)) + if(!m_Snap.m_pLocalCharacter || (m_Snap.m_pLocalCharacter->m_Health < 0) || (m_Snap.m_pGameobj && m_Snap.m_pGameobj->m_GameOver)) { // don't use predicted } else - local_character_pos = mix(predicted_prev_char.pos, predicted_char.pos, client_predintratick()); + m_LocalCharacterPos = mix(m_PredictedPrevChar.m_Pos, m_PredictedChar.m_Pos, Client()->PredIntraGameTick()); } - else if(snap.local_character && snap.local_prev_character) + else if(m_Snap.m_pLocalCharacter && m_Snap.m_pLocalPrevCharacter) { - local_character_pos = mix( - vec2(snap.local_prev_character->x, snap.local_prev_character->y), - vec2(snap.local_character->x, snap.local_character->y), client_intratick()); + m_LocalCharacterPos = mix( + vec2(m_Snap.m_pLocalPrevCharacter->m_X, m_Snap.m_pLocalPrevCharacter->m_Y), + vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y), Client()->IntraGameTick()); } } -static void evolve(NETOBJ_CHARACTER *character, int tick) +static void Evolve(CNetObj_Character *pCharacter, int Tick) { - WORLD_CORE tempworld; - CHARACTER_CORE tempcore; - mem_zero(&tempcore, sizeof(tempcore)); - tempcore.world = &tempworld; - tempcore.read(character); + CWorldCore TempWorld; + CCharacterCore TempCore; + mem_zero(&TempCore, sizeof(TempCore)); + TempCore.Init(&TempWorld, g_GameClient.Collision()); + TempCore.Read(pCharacter); - while(character->tick < tick) + while(pCharacter->m_Tick < Tick) { - character->tick++; - tempcore.tick(false); - tempcore.move(); - tempcore.quantize(); + pCharacter->m_Tick++; + TempCore.Tick(false); + TempCore.Move(); + TempCore.Quantize(); } - tempcore.write(character); + TempCore.Write(pCharacter); } -void GAMECLIENT::on_render() +void CGameClient::OnRender() { /*Graphics()->Clear(1,0,0); @@ -474,25 +442,41 @@ void GAMECLIENT::on_render() return;*/ // update the local character position - update_local_character_pos(); + UpdateLocalCharacterPos(); // dispatch all input to systems - dispatch_input(); + DispatchInput(); // render all systems - for(int i = 0; i < all.num; i++) - all.components[i]->on_render(); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnRender(); // clear new tick flags - new_tick = false; - new_predicted_tick = false; + m_NewTick = false; + m_NewPredictedTick = false; + + // check if client info has to be resent + if(m_LastSendInfo && Client()->State() == IClient::STATE_ONLINE && !m_pMenus->IsActive() && m_LastSendInfo+time_freq()*5 < time_get()) + { + // resend if client info differs + if(str_comp(g_Config.m_PlayerName, m_aClients[m_Snap.m_LocalCid].m_aName) || + str_comp(g_Config.m_PlayerSkin, m_aClients[m_Snap.m_LocalCid].m_aSkinName) || + (g_GameClient.m_Snap.m_pGameobj && !(g_GameClient.m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) && // no teamgame? + (g_Config.m_PlayerUseCustomColor != m_aClients[m_Snap.m_LocalCid].m_UseCustomColor || + g_Config.m_PlayerColorBody != m_aClients[m_Snap.m_LocalCid].m_ColorBody || + g_Config.m_PlayerColorFeet != m_aClients[m_Snap.m_LocalCid].m_ColorFeet))) + { + SendInfo(false); + } + m_LastSendInfo = 0; + } } -void GAMECLIENT::on_message(int msgtype) +void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) { // special messages - if(msgtype == NETMSGTYPE_SV_EXTRAPROJECTILE) + if(MsgId == NETMSGTYPE_SV_EXTRAPROJECTILE) { /* int num = msg_unpack_int(); @@ -515,497 +499,492 @@ void GAMECLIENT::on_message(int msgtype) return;*/ } - else if(msgtype == NETMSGTYPE_SV_TUNEPARAMS) + else if(MsgId == NETMSGTYPE_SV_TUNEPARAMS) { // unpack the new tuning - TUNING_PARAMS new_tuning; - int *params = (int *)&new_tuning; - for(unsigned i = 0; i < sizeof(TUNING_PARAMS)/sizeof(int); i++) - params[i] = msg_unpack_int(); + CTuningParams NewTuning; + int *pParams = (int *)&NewTuning; + for(unsigned i = 0; i < sizeof(CTuningParams)/sizeof(int); i++) + pParams[i] = pUnpacker->GetInt(); // check for unpacking errors - if(msg_unpack_error()) + if(pUnpacker->Error()) return; - servermode = SERVERMODE_PURE; + m_ServerMode = SERVERMODE_PURE; // apply new tuning - tuning = new_tuning; + m_Tuning = NewTuning; return; } - void *rawmsg = netmsg_secure_unpack(msgtype); - if(!rawmsg) + void *pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgId, pUnpacker); + if(!pRawMsg) { - dbg_msg("client", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on()); + dbg_msg("client", "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler.GetMsgName(MsgId), MsgId, m_NetObjHandler.FailedMsgOn()); return; } // TODO: this should be done smarter - for(int i = 0; i < all.num; i++) - all.components[i]->on_message(msgtype, rawmsg); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnMessage(MsgId, pRawMsg); - if(msgtype == NETMSGTYPE_SV_READYTOENTER) + if(MsgId == NETMSGTYPE_SV_READYTOENTER) { - client_entergame(); + Client()->EnterGame(); } - else if (msgtype == NETMSGTYPE_SV_EMOTICON) + else if (MsgId == NETMSGTYPE_SV_EMOTICON) { - NETMSG_SV_EMOTICON *msg = (NETMSG_SV_EMOTICON *)rawmsg; + CNetMsg_Sv_Emoticon *pMsg = (CNetMsg_Sv_Emoticon *)pRawMsg; // apply - clients[msg->cid].emoticon = msg->emoticon; - clients[msg->cid].emoticon_start = client_tick(); + m_aClients[pMsg->m_Cid].m_Emoticon = pMsg->m_Emoticon; + m_aClients[pMsg->m_Cid].m_EmoticonStart = Client()->GameTick(); } - else if(msgtype == NETMSGTYPE_SV_SOUNDGLOBAL) + else if(MsgId == NETMSGTYPE_SV_SOUNDGLOBAL) { - if(suppress_events) + if(m_SuppressEvents) return; - NETMSG_SV_SOUNDGLOBAL *msg = (NETMSG_SV_SOUNDGLOBAL *)rawmsg; - gameclient.sounds->play(SOUNDS::CHN_GLOBAL, msg->soundid, 1.0f, vec2(0,0)); + CNetMsg_Sv_SoundGlobal *pMsg = (CNetMsg_Sv_SoundGlobal *)pRawMsg; + g_GameClient.m_pSounds->Enqueue(pMsg->m_Soundid); } } -void GAMECLIENT::on_statechange(int new_state, int old_state) +void CGameClient::OnStateChange(int NewState, int OldState) { - if(demorec_isrecording()) - demorec_record_stop(); - - // reset everything - on_reset(); + // reset everything when not already connected (to keep gathered stuff) + if(NewState < IClient::STATE_ONLINE) + OnReset(); // then change the state - for(int i = 0; i < all.num; i++) - all.components[i]->on_statechange(new_state, old_state); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnStateChange(NewState, OldState); } +void CGameClient::OnShutdown() {} +void CGameClient::OnEnterGame() {} +void CGameClient::OnRconLine(const char *pLine) +{ + m_pGameConsole->PrintLine(1, pLine); +} -void GAMECLIENT::process_events() +void CGameClient::ProcessEvents() { - if(suppress_events) + if(m_SuppressEvents) return; - int snaptype = SNAP_CURRENT; - int num = snap_num_items(snaptype); - for(int index = 0; index < num; index++) + int SnapType = IClient::SNAP_CURRENT; + int Num = Client()->SnapNumItems(SnapType); + for(int Index = 0; Index < Num; Index++) { - SNAP_ITEM item; - const void *data = snap_get_item(snaptype, index, &item); + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(SnapType, Index, &Item); - if(item.type == NETEVENTTYPE_DAMAGEIND) + if(Item.m_Type == NETEVENTTYPE_DAMAGEIND) { - NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)data; - gameclient.effects->damage_indicator(vec2(ev->x, ev->y), get_direction(ev->angle)); + NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)pData; + g_GameClient.m_pEffects->DamageIndicator(vec2(ev->m_X, ev->m_Y), GetDirection(ev->m_Angle)); } - else if(item.type == NETEVENTTYPE_EXPLOSION) + else if(Item.m_Type == NETEVENTTYPE_EXPLOSION) { - NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)data; - gameclient.effects->explosion(vec2(ev->x, ev->y)); + NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)pData; + g_GameClient.m_pEffects->Explosion(vec2(ev->m_X, ev->m_Y)); } - else if(item.type == NETEVENTTYPE_HAMMERHIT) + else if(Item.m_Type == NETEVENTTYPE_HAMMERHIT) { - NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)data; - gameclient.effects->hammerhit(vec2(ev->x, ev->y)); + NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)pData; + g_GameClient.m_pEffects->HammerHit(vec2(ev->m_X, ev->m_Y)); } - else if(item.type == NETEVENTTYPE_SPAWN) + else if(Item.m_Type == NETEVENTTYPE_SPAWN) { - NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)data; - gameclient.effects->playerspawn(vec2(ev->x, ev->y)); + NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)pData; + g_GameClient.m_pEffects->PlayerSpawn(vec2(ev->m_X, ev->m_Y)); } - else if(item.type == NETEVENTTYPE_DEATH) + else if(Item.m_Type == NETEVENTTYPE_DEATH) { - NETEVENT_DEATH *ev = (NETEVENT_DEATH *)data; - gameclient.effects->playerdeath(vec2(ev->x, ev->y), ev->cid); + NETEVENT_DEATH *ev = (NETEVENT_DEATH *)pData; + g_GameClient.m_pEffects->PlayerDeath(vec2(ev->m_X, ev->m_Y), ev->m_ClientId); } - else if(item.type == NETEVENTTYPE_SOUNDWORLD) + else if(Item.m_Type == NETEVENTTYPE_SOUNDWORLD) { - NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)data; - gameclient.sounds->play(SOUNDS::CHN_WORLD, ev->soundid, 1.0f, vec2(ev->x, ev->y)); + NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)pData; + g_GameClient.m_pSounds->Play(CSounds::CHN_WORLD, ev->m_SoundId, 1.0f, vec2(ev->m_X, ev->m_Y)); } } } -void GAMECLIENT::on_snapshot() +void CGameClient::OnNewSnapshot() { - new_tick = true; + m_NewTick = true; // clear out the invalid pointers - mem_zero(&gameclient.snap, sizeof(gameclient.snap)); - snap.local_cid = -1; + mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap)); + m_Snap.m_LocalCid = -1; // secure snapshot { - int num = snap_num_items(SNAP_CURRENT); - for(int index = 0; index < num; index++) + int Num = Client()->SnapNumItems(IClient::SNAP_CURRENT); + for(int Index = 0; Index < Num; Index++) { - SNAP_ITEM item; - void *data = snap_get_item(SNAP_CURRENT, index, &item); - if(netobj_validate(item.type, data, item.datasize) != 0) + IClient::CSnapItem Item; + void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, Index, &Item); + if(m_NetObjHandler.ValidateObj(Item.m_Type, pData, Item.m_DataSize) != 0) { - if(config.debug) - dbg_msg("game", "invalidated index=%d type=%d (%s) size=%d id=%d", index, item.type, netobj_get_name(item.type), item.datasize, item.id); - snap_invalidate_item(SNAP_CURRENT, index); + if(g_Config.m_Debug) + dbg_msg("game", "invalidated index=%d type=%d (%s) size=%d id=%d", Index, Item.m_Type, m_NetObjHandler.GetObjName(Item.m_Type), Item.m_DataSize, Item.m_Id); + Client()->SnapInvalidateItem(IClient::SNAP_CURRENT, Index); } } } - process_events(); + ProcessEvents(); - if(config.dbg_stress) + if(g_Config.m_DbgStress) { - if((client_tick()%100) == 0) + if((Client()->GameTick()%100) == 0) { - char message[64]; - int msglen = rand()%(sizeof(message)-1); - for(int i = 0; i < msglen; i++) - message[i] = 'a'+(rand()%('z'-'a')); - message[msglen] = 0; + char aMessage[64]; + int MsgLen = rand()%(sizeof(aMessage)-1); + for(int i = 0; i < MsgLen; i++) + aMessage[i] = 'a'+(rand()%('z'-'a')); + aMessage[MsgLen] = 0; - NETMSG_CL_SAY msg; - msg.team = rand()&1; - msg.message = message; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); + CNetMsg_Cl_Say Msg; + Msg.m_Team = rand()&1; + Msg.m_pMessage = aMessage; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } } // go trough all the items in the snapshot and gather the info we want { - snap.team_size[0] = snap.team_size[1] = 0; + m_Snap.m_aTeamSize[0] = m_Snap.m_aTeamSize[1] = 0; - int num = snap_num_items(SNAP_CURRENT); - for(int i = 0; i < num; i++) + int Num = Client()->SnapNumItems(IClient::SNAP_CURRENT); + for(int i = 0; i < Num; i++) { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); - if(item.type == NETOBJTYPE_CLIENT_INFO) + if(Item.m_Type == NETOBJTYPE_CLIENTINFO) { - const NETOBJ_CLIENT_INFO *info = (const NETOBJ_CLIENT_INFO *)data; - int cid = item.id; - ints_to_str(&info->name0, 6, clients[cid].name); - ints_to_str(&info->skin0, 6, clients[cid].skin_name); + const CNetObj_ClientInfo *pInfo = (const CNetObj_ClientInfo *)pData; + int Cid = Item.m_Id; + IntsToStr(&pInfo->m_Name0, 6, m_aClients[Cid].m_aName); + IntsToStr(&pInfo->m_Skin0, 6, m_aClients[Cid].m_aSkinName); - clients[cid].use_custom_color = info->use_custom_color; - clients[cid].color_body = info->color_body; - clients[cid].color_feet = info->color_feet; + m_aClients[Cid].m_UseCustomColor = pInfo->m_UseCustomColor; + m_aClients[Cid].m_ColorBody = pInfo->m_ColorBody; + m_aClients[Cid].m_ColorFeet = pInfo->m_ColorFeet; // prepare the info - if(clients[cid].skin_name[0] == 'x' || clients[cid].skin_name[1] == '_') - str_copy(clients[cid].skin_name, "default", 64); + if(m_aClients[Cid].m_aSkinName[0] == 'x' || m_aClients[Cid].m_aSkinName[1] == '_') + str_copy(m_aClients[Cid].m_aSkinName, "default", 64); - clients[cid].skin_info.color_body = skins->get_color(clients[cid].color_body); - clients[cid].skin_info.color_feet = skins->get_color(clients[cid].color_feet); - clients[cid].skin_info.size = 64; + m_aClients[Cid].m_SkinInfo.m_ColorBody = m_pSkins->GetColor(m_aClients[Cid].m_ColorBody); + m_aClients[Cid].m_SkinInfo.m_ColorFeet = m_pSkins->GetColor(m_aClients[Cid].m_ColorFeet); + m_aClients[Cid].m_SkinInfo.m_Size = 64; // find new skin - clients[cid].skin_id = gameclient.skins->find(clients[cid].skin_name); - if(clients[cid].skin_id < 0) + m_aClients[Cid].m_SkinId = g_GameClient.m_pSkins->Find(m_aClients[Cid].m_aSkinName); + if(m_aClients[Cid].m_SkinId < 0) { - clients[cid].skin_id = gameclient.skins->find("default"); - if(clients[cid].skin_id < 0) - clients[cid].skin_id = 0; + m_aClients[Cid].m_SkinId = g_GameClient.m_pSkins->Find("default"); + if(m_aClients[Cid].m_SkinId < 0) + m_aClients[Cid].m_SkinId = 0; } - if(clients[cid].use_custom_color) - clients[cid].skin_info.texture = gameclient.skins->get(clients[cid].skin_id)->color_texture; + if(m_aClients[Cid].m_UseCustomColor) + m_aClients[Cid].m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(m_aClients[Cid].m_SkinId)->m_ColorTexture; else { - clients[cid].skin_info.texture = gameclient.skins->get(clients[cid].skin_id)->org_texture; - clients[cid].skin_info.color_body = vec4(1,1,1,1); - clients[cid].skin_info.color_feet = vec4(1,1,1,1); + m_aClients[Cid].m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(m_aClients[Cid].m_SkinId)->m_OrgTexture; + m_aClients[Cid].m_SkinInfo.m_ColorBody = vec4(1,1,1,1); + m_aClients[Cid].m_SkinInfo.m_ColorFeet = vec4(1,1,1,1); } - clients[cid].update_render_info(); - gameclient.snap.num_players++; + m_aClients[Cid].UpdateRenderInfo(); + g_GameClient.m_Snap.m_NumPlayers++; } - else if(item.type == NETOBJTYPE_PLAYER_INFO) + else if(Item.m_Type == NETOBJTYPE_PLAYERINFO) { - const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data; + const CNetObj_PlayerInfo *pInfo = (const CNetObj_PlayerInfo *)pData; - clients[info->cid].team = info->team; - snap.player_infos[info->cid] = info; + m_aClients[pInfo->m_ClientId].m_Team = pInfo->m_Team; + m_Snap.m_paPlayerInfos[pInfo->m_ClientId] = pInfo; - if(info->local) + if(pInfo->m_Local) { - snap.local_cid = item.id; - snap.local_info = info; + m_Snap.m_LocalCid = Item.m_Id; + m_Snap.m_pLocalInfo = pInfo; - if (info->team == -1) - snap.spectate = true; + if (pInfo->m_Team == -1) + m_Snap.m_Spectate = true; } // calculate team-balance - if(info->team != -1) - snap.team_size[info->team]++; + if(pInfo->m_Team != -1) + m_Snap.m_aTeamSize[pInfo->m_Team]++; } - else if(item.type == NETOBJTYPE_CHARACTER) + else if(Item.m_Type == NETOBJTYPE_CHARACTER) { - const void *old = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id); - if(old) + const void *pOld = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_CHARACTER, Item.m_Id); + if(pOld) { - snap.characters[item.id].active = true; - snap.characters[item.id].prev = *((const NETOBJ_CHARACTER *)old); - snap.characters[item.id].cur = *((const NETOBJ_CHARACTER *)data); - - if(snap.characters[item.id].prev.tick) - evolve(&snap.characters[item.id].prev, client_prevtick()); - if(snap.characters[item.id].cur.tick) - evolve(&snap.characters[item.id].cur, client_tick()); + m_Snap.m_aCharacters[Item.m_Id].m_Active = true; + m_Snap.m_aCharacters[Item.m_Id].m_Prev = *((const CNetObj_Character *)pOld); + m_Snap.m_aCharacters[Item.m_Id].m_Cur = *((const CNetObj_Character *)pData); + + if(m_Snap.m_aCharacters[Item.m_Id].m_Prev.m_Tick) + Evolve(&m_Snap.m_aCharacters[Item.m_Id].m_Prev, Client()->PrevGameTick()); + if(m_Snap.m_aCharacters[Item.m_Id].m_Cur.m_Tick) + Evolve(&m_Snap.m_aCharacters[Item.m_Id].m_Cur, Client()->GameTick()); } } - else if(item.type == NETOBJTYPE_GAME) - snap.gameobj = (NETOBJ_GAME *)data; - else if(item.type == NETOBJTYPE_FLAG) - snap.flags[item.id%2] = (const NETOBJ_FLAG *)data; + else if(Item.m_Type == NETOBJTYPE_GAME) + m_Snap.m_pGameobj = (CNetObj_Game *)pData; + else if(Item.m_Type == NETOBJTYPE_FLAG) + m_Snap.m_paFlags[Item.m_Id%2] = (const CNetObj_Flag *)pData; } } // setup local pointers - if(snap.local_cid >= 0) + if(m_Snap.m_LocalCid >= 0) { - SNAPSTATE::CHARACTERINFO *c = &snap.characters[snap.local_cid]; - if(c->active) + CSnapState::CCharacterInfo *c = &m_Snap.m_aCharacters[m_Snap.m_LocalCid]; + if(c->m_Active) { - snap.local_character = &c->cur; - snap.local_prev_character = &c->prev; - local_character_pos = vec2(snap.local_character->x, snap.local_character->y); + m_Snap.m_pLocalCharacter = &c->m_Cur; + m_Snap.m_pLocalPrevCharacter = &c->m_Prev; + m_LocalCharacterPos = vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y); } } else - snap.spectate = true; + m_Snap.m_Spectate = true; - TUNING_PARAMS standard_tuning; - SERVER_INFO current_server_info; - client_serverinfo(¤t_server_info); - if(current_server_info.gametype[0] != '0') + CTuningParams StandardTuning; + CServerInfo CurrentServerInfo; + Client()->GetServerInfo(&CurrentServerInfo); + if(CurrentServerInfo.m_aGameType[0] != '0') { - if(strcmp(current_server_info.gametype, "DM") != 0 && strcmp(current_server_info.gametype, "TDM") != 0 && strcmp(current_server_info.gametype, "CTF") != 0) - servermode = SERVERMODE_MOD; - else if(memcmp(&standard_tuning, &tuning, sizeof(TUNING_PARAMS)) == 0) - servermode = SERVERMODE_PURE; + if(str_comp(CurrentServerInfo.m_aGameType, "DM") != 0 && str_comp(CurrentServerInfo.m_aGameType, "TDM") != 0 && str_comp(CurrentServerInfo.m_aGameType, "CTF") != 0) + m_ServerMode = SERVERMODE_MOD; + else if(mem_comp(&StandardTuning, &m_Tuning, sizeof(CTuningParams)) == 0) + m_ServerMode = SERVERMODE_PURE; else - servermode = SERVERMODE_PUREMOD; + m_ServerMode = SERVERMODE_PUREMOD; } // update render info for(int i = 0; i < MAX_CLIENTS; i++) - clients[i].update_render_info(); + m_aClients[i].UpdateRenderInfo(); } -void GAMECLIENT::on_predict() +void CGameClient::OnPredict() { // store the previous values so we can detect prediction errors - CHARACTER_CORE before_prev_char = predicted_prev_char; - CHARACTER_CORE before_char = predicted_char; + CCharacterCore BeforePrevChar = m_PredictedPrevChar; + CCharacterCore BeforeChar = m_PredictedChar; // we can't predict without our own id or own character - if(snap.local_cid == -1 || !snap.characters[snap.local_cid].active) + if(m_Snap.m_LocalCid == -1 || !m_Snap.m_aCharacters[m_Snap.m_LocalCid].m_Active) return; // don't predict anything if we are paused - if(snap.gameobj && snap.gameobj->paused) + if(m_Snap.m_pGameobj && m_Snap.m_pGameobj->m_Paused) { - if(snap.local_character) - predicted_char.read(snap.local_character); - if(snap.local_prev_character) - predicted_prev_char.read(snap.local_prev_character); + if(m_Snap.m_pLocalCharacter) + m_PredictedChar.Read(m_Snap.m_pLocalCharacter); + if(m_Snap.m_pLocalPrevCharacter) + m_PredictedPrevChar.Read(m_Snap.m_pLocalPrevCharacter); return; } // repredict character - WORLD_CORE world; - world.tuning = tuning; + CWorldCore World; + World.m_Tuning = m_Tuning; // search for players for(int i = 0; i < MAX_CLIENTS; i++) { - if(!snap.characters[i].active) + if(!m_Snap.m_aCharacters[i].m_Active) continue; - gameclient.clients[i].predicted.world = &world; - world.characters[i] = &gameclient.clients[i].predicted; - gameclient.clients[i].predicted.read(&snap.characters[i].cur); + g_GameClient.m_aClients[i].m_Predicted.Init(&World, Collision()); + World.m_apCharacters[i] = &g_GameClient.m_aClients[i].m_Predicted; + g_GameClient.m_aClients[i].m_Predicted.Read(&m_Snap.m_aCharacters[i].m_Cur); } // predict - for(int tick = client_tick()+1; tick <= client_predtick(); tick++) + for(int Tick = Client()->GameTick()+1; Tick <= Client()->PredGameTick(); Tick++) { // fetch the local - if(tick == client_predtick() && world.characters[snap.local_cid]) - predicted_prev_char = *world.characters[snap.local_cid]; + if(Tick == Client()->PredGameTick() && World.m_apCharacters[m_Snap.m_LocalCid]) + m_PredictedPrevChar = *World.m_apCharacters[m_Snap.m_LocalCid]; // first calculate where everyone should move for(int c = 0; c < MAX_CLIENTS; c++) { - if(!world.characters[c]) + if(!World.m_apCharacters[c]) continue; - mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input)); - if(snap.local_cid == c) + mem_zero(&World.m_apCharacters[c]->m_Input, sizeof(World.m_apCharacters[c]->m_Input)); + if(m_Snap.m_LocalCid == c) { // apply player input - int *input = client_get_input(tick); - if(input) - world.characters[c]->input = *((NETOBJ_PLAYER_INPUT*)input); - world.characters[c]->tick(true); + int *pInput = Client()->GetInput(Tick); + if(pInput) + World.m_apCharacters[c]->m_Input = *((CNetObj_PlayerInput*)pInput); + World.m_apCharacters[c]->Tick(true); } else - world.characters[c]->tick(false); + World.m_apCharacters[c]->Tick(false); } // move all players and quantize their data for(int c = 0; c < MAX_CLIENTS; c++) { - if(!world.characters[c]) + if(!World.m_apCharacters[c]) continue; - world.characters[c]->move(); - world.characters[c]->quantize(); + World.m_apCharacters[c]->Move(); + World.m_apCharacters[c]->Quantize(); } // check if we want to trigger effects - if(tick > last_new_predicted_tick) + if(Tick > m_LastNewPredictedTick) { - last_new_predicted_tick = tick; - new_predicted_tick = true; + m_LastNewPredictedTick = Tick; + m_NewPredictedTick = true; - if(snap.local_cid != -1 && world.characters[snap.local_cid]) + if(m_Snap.m_LocalCid != -1 && World.m_apCharacters[m_Snap.m_LocalCid]) { - vec2 pos = world.characters[snap.local_cid]->pos; - int events = world.characters[snap.local_cid]->triggered_events; - if(events&COREEVENT_GROUND_JUMP) gameclient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + vec2 Pos = World.m_apCharacters[m_Snap.m_LocalCid]->m_Pos; + int Events = World.m_apCharacters[m_Snap.m_LocalCid]->m_TriggeredEvents; + if(Events&COREEVENT_GROUND_JUMP) g_GameClient.m_pSounds->PlayAndRecord(CSounds::CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, Pos); /*if(events&COREEVENT_AIR_JUMP) { - gameclient.effects->air_jump(pos); - gameclient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos); + GameClient.effects->air_jump(pos); + GameClient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos); }*/ //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); //if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos); - if(events&COREEVENT_HOOK_ATTACH_GROUND) gameclient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos); - if(events&COREEVENT_HOOK_HIT_NOHOOK) gameclient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_HOOK_NOATTACH, 1.0f, pos); + if(Events&COREEVENT_HOOK_ATTACH_GROUND) g_GameClient.m_pSounds->PlayAndRecord(CSounds::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, Pos); + if(Events&COREEVENT_HOOK_HIT_NOHOOK) g_GameClient.m_pSounds->PlayAndRecord(CSounds::CHN_WORLD, SOUND_HOOK_NOATTACH, 1.0f, Pos); //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); } } - if(tick == client_predtick() && world.characters[snap.local_cid]) - predicted_char = *world.characters[snap.local_cid]; + if(Tick == Client()->PredGameTick() && World.m_apCharacters[m_Snap.m_LocalCid]) + m_PredictedChar = *World.m_apCharacters[m_Snap.m_LocalCid]; } - if(config.debug && config.cl_predict && predicted_tick == client_predtick()) + if(g_Config.m_Debug && g_Config.m_ClPredict && m_PredictedTick == Client()->PredGameTick()) { - NETOBJ_CHARACTER_CORE before = {0}, now = {0}, before_prev = {0}, now_prev = {0}; - before_char.write(&before); - before_prev_char.write(&before_prev); - predicted_char.write(&now); - predicted_prev_char.write(&now_prev); + CNetObj_CharacterCore Before = {0}, Now = {0}, BeforePrev = {0}, NowPrev = {0}; + BeforeChar.Write(&Before); + BeforePrevChar.Write(&BeforePrev); + m_PredictedChar.Write(&Now); + m_PredictedPrevChar.Write(&NowPrev); - if(mem_comp(&before, &now, sizeof(NETOBJ_CHARACTER_CORE)) != 0) + if(mem_comp(&Before, &Now, sizeof(CNetObj_CharacterCore)) != 0) { dbg_msg("client", "prediction error"); - for(unsigned i = 0; i < sizeof(NETOBJ_CHARACTER_CORE)/sizeof(int); i++) - if(((int *)&before)[i] != ((int *)&now)[i]) + for(unsigned i = 0; i < sizeof(CNetObj_CharacterCore)/sizeof(int); i++) + if(((int *)&Before)[i] != ((int *)&Now)[i]) { - dbg_msg("", "\t%d %d %d (%d %d)", i, ((int *)&before)[i], ((int *)&now)[i], ((int *)&before_prev)[i], ((int *)&now_prev)[i]); + dbg_msg("", "\t%d %d %d (%d %d)", i, ((int *)&Before)[i], ((int *)&Now)[i], ((int *)&BeforePrev)[i], ((int *)&NowPrev)[i]); } } } - predicted_tick = client_predtick(); + m_PredictedTick = Client()->PredGameTick(); } -void GAMECLIENT::CLIENT_DATA::update_render_info() +void CGameClient::CClientData::UpdateRenderInfo() { - render_info = skin_info; + m_RenderInfo = m_SkinInfo; // force team colors - if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) + if(g_GameClient.m_Snap.m_pGameobj && g_GameClient.m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) { - const int team_colors[2] = {65387, 10223467}; - if(team >= 0 || team <= 1) + const int TeamColors[2] = {65387, 10223467}; + if(m_Team >= 0 || m_Team <= 1) { - render_info.texture = gameclient.skins->get(skin_id)->color_texture; - render_info.color_body = gameclient.skins->get_color(team_colors[team]); - render_info.color_feet = gameclient.skins->get_color(team_colors[team]); + m_RenderInfo.m_Texture = g_GameClient.m_pSkins->Get(m_SkinId)->m_ColorTexture; + m_RenderInfo.m_ColorBody = g_GameClient.m_pSkins->GetColor(TeamColors[m_Team]); + m_RenderInfo.m_ColorFeet = g_GameClient.m_pSkins->GetColor(TeamColors[m_Team]); } } } -void GAMECLIENT::send_switch_team(int team) +void CGameClient::SendSwitchTeam(int Team) { - NETMSG_CL_SETTEAM msg; - msg.team = team; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); + CNetMsg_Cl_SetTeam Msg; + Msg.m_Team = Team; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } -void GAMECLIENT::send_info(bool start) +void CGameClient::SendInfo(bool Start) { - if(start) + if(Start) { - NETMSG_CL_STARTINFO msg; - msg.name = config.player_name; - msg.skin = config.player_skin; - msg.use_custom_color = config.player_use_custom_color; - msg.color_body = config.player_color_body; - msg.color_feet = config.player_color_feet; - msg.pack(MSGFLAG_VITAL); + CNetMsg_Cl_StartInfo Msg; + Msg.m_pName = g_Config.m_PlayerName; + Msg.m_pSkin = g_Config.m_PlayerSkin; + Msg.m_UseCustomColor = g_Config.m_PlayerUseCustomColor; + Msg.m_ColorBody = g_Config.m_PlayerColorBody; + Msg.m_ColorFeet = g_Config.m_PlayerColorFeet; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } else { - NETMSG_CL_CHANGEINFO msg; - msg.name = config.player_name; - msg.skin = config.player_skin; - msg.use_custom_color = config.player_use_custom_color; - msg.color_body = config.player_color_body; - msg.color_feet = config.player_color_feet; - msg.pack(MSGFLAG_VITAL); + CNetMsg_Cl_ChangeInfo Msg; + Msg.m_pName = g_Config.m_PlayerName; + Msg.m_pSkin = g_Config.m_PlayerSkin; + Msg.m_UseCustomColor = g_Config.m_PlayerUseCustomColor; + Msg.m_ColorBody = g_Config.m_PlayerColorBody; + Msg.m_ColorFeet = g_Config.m_PlayerColorFeet; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + + // activate timer to resend the info if it gets filtered + if(!m_LastSendInfo || m_LastSendInfo+time_freq()*5 < time_get()) + m_LastSendInfo = time_get(); } - client_send_msg(); } -void GAMECLIENT::send_kill(int client_id) +void CGameClient::SendKill(int ClientId) { - NETMSG_CL_KILL msg; - msg.pack(MSGFLAG_VITAL); - client_send_msg(); + CNetMsg_Cl_Kill Msg; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } -void GAMECLIENT::con_team(void *result, void *user_data) +void CGameClient::ConTeam(IConsole::IResult *pResult, void *pUserData) { - ((GAMECLIENT*)user_data)->send_switch_team(console_arg_int(result, 0)); + ((CGameClient*)pUserData)->SendSwitchTeam(pResult->GetInteger(0)); } -void GAMECLIENT::con_kill(void *result, void *user_data) +void CGameClient::ConKill(IConsole::IResult *pResult, void *pUserData) { - ((GAMECLIENT*)user_data)->send_kill(-1); + ((CGameClient*)pUserData)->SendKill(-1); } -void GAMECLIENT::conchain_special_infoupdate(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser) +void CGameClient::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { - cb(result, cbuser); - if(console_arg_num(result)) - ((GAMECLIENT*)user_data)->send_info(false); -} - -void GAMECLIENT::SetEngine(class IEngine *pEngine) -{ - m_pEngine = pEngine; - - // digg out some pointers - m_pGraphics = m_pEngine->Graphics(); + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments()) + ((CGameClient*)pUserData)->SendInfo(false); } -IGameClient *CreateGameClient(IEngine *pEngine) +IGameClient *CreateGameClient() { - gameclient.SetEngine(pEngine); - return &gameclient; + return &g_GameClient; } diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h new file mode 100644 index 00000000..b91b4b50 --- /dev/null +++ b/src/game/client/gameclient.h @@ -0,0 +1,208 @@ +#ifndef GAME_CLIENT_GAMECLIENT_H +#define GAME_CLIENT_GAMECLIENT_H + +#include <base/vmath.h> +#include <engine/client.h> +#include <engine/console.h> +#include <game/layers.h> +#include <game/gamecore.h> +#include "render.h" + +class CGameClient : public IGameClient +{ + class CStack + { + public: + enum + { + MAX_COMPONENTS = 64, + }; + + CStack(); + void Add(class CComponent *pComponent); + + class CComponent *m_paComponents[MAX_COMPONENTS]; + int m_Num; + }; + + CStack m_All; + CStack m_Input; + CNetObjHandler m_NetObjHandler; + + class IInput *m_pInput; + class IGraphics *m_pGraphics; + class ITextRender *m_pTextRender; + class IClient *m_pClient; + class ISound *m_pSound; + class IConsole *m_pConsole; + class IStorage *m_pStorage; + class IDemoPlayer *m_pDemoPlayer; + class IServerBrowser *m_pServerBrowser; + + CLayers m_Layers; + class CCollision m_Collision; + CUI m_UI; + + void DispatchInput(); + void ProcessEvents(); + void UpdateLocalCharacterPos(); + + int m_PredictedTick; + int m_LastNewPredictedTick; + + int64 m_LastSendInfo; + + static void ConTeam(IConsole::IResult *pResult, void *pUserData); + static void ConKill(IConsole::IResult *pResult, void *pUserData); + + static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + +public: + IKernel *Kernel() { return IInterface::Kernel(); } + class IGraphics *Graphics() const { return m_pGraphics; } + class IClient *Client() const { return m_pClient; } + class CUI *UI() { return &m_UI; } + class ISound *Sound() const { return m_pSound; } + class IInput *Input() const { return m_pInput; } + class IStorage *Storage() const { return m_pStorage; } + class IConsole *Console() { return m_pConsole; } + class ITextRender *TextRender() const { return m_pTextRender; } + class IDemoPlayer *DemoPlayer() const { return m_pDemoPlayer; } + class IServerBrowser *ServerBrowser() const { return m_pServerBrowser; } + class CRenderTools *RenderTools() { return &m_RenderTools; } + class CLayers *Layers() { return &m_Layers; }; + class CCollision *Collision() { return &m_Collision; }; + + + bool m_SuppressEvents; + bool m_NewTick; + bool m_NewPredictedTick; + + // TODO: move this + CTuningParams m_Tuning; + + enum + { + SERVERMODE_PURE=0, + SERVERMODE_MOD, + SERVERMODE_PUREMOD, + }; + int m_ServerMode; + + vec2 m_LocalCharacterPos; + + // predicted players + CCharacterCore m_PredictedPrevChar; + CCharacterCore m_PredictedChar; + + // snap pointers + struct CSnapState + { + const CNetObj_Character *m_pLocalCharacter; + const CNetObj_Character *m_pLocalPrevCharacter; + const CNetObj_PlayerInfo *m_pLocalInfo; + const CNetObj_Flag *m_paFlags[2]; + const CNetObj_Game *m_pGameobj; + + const CNetObj_PlayerInfo *m_paPlayerInfos[MAX_CLIENTS]; + const CNetObj_PlayerInfo *m_paInfoByScore[MAX_CLIENTS]; + + int m_LocalCid; + int m_NumPlayers; + int m_aTeamSize[2]; + bool m_Spectate; + + // + struct CCharacterInfo + { + bool m_Active; + + // snapshots + CNetObj_Character m_Prev; + CNetObj_Character m_Cur; + + // interpolated position + vec2 m_Position; + }; + + CCharacterInfo m_aCharacters[MAX_CLIENTS]; + }; + + CSnapState m_Snap; + + // client data + struct CClientData + { + int m_UseCustomColor; + int m_ColorBody; + int m_ColorFeet; + + char m_aName[64]; + char m_aSkinName[64]; + int m_SkinId; + int m_SkinColor; + int m_Team; + int m_Emoticon; + int m_EmoticonStart; + CCharacterCore m_Predicted; + + CTeeRenderInfo m_SkinInfo; // this is what the server reports + CTeeRenderInfo m_RenderInfo; // this is what we use + + float m_Angle; + + void UpdateRenderInfo(); + }; + + CClientData m_aClients[MAX_CLIENTS]; + + CRenderTools m_RenderTools; + + void OnReset(); + + // hooks + virtual void OnConnected(); + virtual void OnRender(); + virtual void OnInit(); + virtual void OnConsoleInit(); + virtual void OnStateChange(int NewState, int OldState); + virtual void OnMessage(int MsgId, CUnpacker *pUnpacker); + virtual void OnNewSnapshot(); + virtual void OnPredict(); + virtual int OnSnapInput(int *pData); + virtual void OnShutdown(); + virtual void OnEnterGame(); + virtual void OnRconLine(const char *pLine); + + virtual const char *GetItemName(int Type); + virtual const char *Version(); + virtual const char *NetVersion(); + + + // actions + // TODO: move these + void SendSwitchTeam(int Team); + void SendInfo(bool Start); + void SendKill(int ClientId); + + // pointers to all systems + class CGameConsole *m_pGameConsole; + class CBinds *m_pBinds; + class CParticles *m_pParticles; + class CMenus *m_pMenus; + class CSkins *m_pSkins; + class CFlow *m_pFlow; + class CChat *m_pChat; + class CDamageInd *m_pDamageind; + class CCamera *m_pCamera; + class CControls *m_pControls; + class CEffects *m_pEffects; + class CSounds *m_pSounds; + class CMotd *m_pMotd; + class CMapImages *m_pMapimages; + class CVoting *m_pVoting; +}; + +extern const char *Localize(const char *Str); + +#endif diff --git a/src/game/client/gameclient.hpp b/src/game/client/gameclient.hpp deleted file mode 100644 index 1da40de0..00000000 --- a/src/game/client/gameclient.hpp +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef FILE_GAMECLIENT_HPP -#define FILE_GAMECLIENT_HPP - -#include <base/vmath.hpp> -#include <engine/e_console.h> -#include <engine/client/client.h> -#include <game/gamecore.hpp> -#include "render.hpp" - -class GAMECLIENT : public IGameClient -{ - class STACK - { - public: - enum - { - MAX_COMPONENTS = 64, - }; - - STACK(); - void add(class COMPONENT *component); - - class COMPONENT *components[MAX_COMPONENTS]; - int num; - }; - - STACK all; - STACK input; - - class IGraphics *m_pGraphics; - class IEngine *m_pEngine; - CUI m_UI; - - void dispatch_input(); - void process_events(); - void update_local_character_pos(); - - int predicted_tick; - int last_new_predicted_tick; - - static void con_team(void *result, void *user_data); - static void con_kill(void *result, void *user_data); - - static void conchain_special_infoupdate(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser); - -public: - class IGraphics *Graphics() const { return m_pGraphics; } - class CUI *UI() { return &m_UI; } - class CRenderTools *RenderTools() { return &m_RenderTools; } - - void SetEngine(class IEngine *pEngine); - - bool suppress_events; - bool new_tick; - bool new_predicted_tick; - - // TODO: move this - TUNING_PARAMS tuning; - - enum - { - SERVERMODE_PURE=0, - SERVERMODE_MOD, - SERVERMODE_PUREMOD, - }; - int servermode; - - vec2 local_character_pos; - - // predicted players - CHARACTER_CORE predicted_prev_char; - CHARACTER_CORE predicted_char; - - // snap pointers - struct SNAPSTATE - { - const NETOBJ_CHARACTER *local_character; - const NETOBJ_CHARACTER *local_prev_character; - const NETOBJ_PLAYER_INFO *local_info; - const NETOBJ_FLAG *flags[2]; - const NETOBJ_GAME *gameobj; - - const NETOBJ_PLAYER_INFO *player_infos[MAX_CLIENTS]; - const NETOBJ_PLAYER_INFO *info_by_score[MAX_CLIENTS]; - - int local_cid; - int num_players; - int team_size[2]; - bool spectate; - - // - struct CHARACTERINFO - { - bool active; - - // snapshots - NETOBJ_CHARACTER prev; - NETOBJ_CHARACTER cur; - - // interpolated position - vec2 position; - }; - - CHARACTERINFO characters[MAX_CLIENTS]; - }; - - SNAPSTATE snap; - - // client data - struct CLIENT_DATA - { - int use_custom_color; - int color_body; - int color_feet; - - char name[64]; - char skin_name[64]; - int skin_id; - int skin_color; - int team; - int emoticon; - int emoticon_start; - CHARACTER_CORE predicted; - - TEE_RENDER_INFO skin_info; // this is what the server reports - TEE_RENDER_INFO render_info; // this is what we use - - float angle; - - void update_render_info(); - }; - - CLIENT_DATA clients[MAX_CLIENTS]; - - CRenderTools m_RenderTools; - - void on_reset(); - - // hooks - void on_connected(); - void on_render(); - void on_init(); - void on_save(); - void on_console_init(); - void on_statechange(int new_state, int old_state); - void on_message(int msgtype); - void on_snapshot(); - void on_predict(); - int on_snapinput(int *data); - - // actions - // TODO: move these - void send_switch_team(int team); - void send_info(bool start); - void send_kill(int client_id); - - // pointers to all systems - class CONSOLE *console; - class BINDS *binds; - class PARTICLES *particles; - class MENUS *menus; - class SKINS *skins; - class FLOW *flow; - class CHAT *chat; - class DAMAGEIND *damageind; - class CAMERA *camera; - class CONTROLS *controls; - class EFFECTS *effects; - class SOUNDS *sounds; - class MOTD *motd; - class MAPIMAGES *mapimages; - class VOTING *voting; -}; - - -// TODO: Refactor: Remove this -extern GAMECLIENT gameclient; - -extern const char *localize(const char *str); - -#endif diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp index f8c9d7e7..f1159caf 100644 --- a/src/game/client/lineinput.cpp +++ b/src/game/client/lineinput.cpp @@ -1,85 +1,90 @@ -#include <engine/e_client_interface.h> -#include <string.h> // strlen -#include "lineinput.hpp" +#include <engine/keys.h> +#include "lineinput.h" -LINEINPUT::LINEINPUT() +CLineInput::CLineInput() { - clear(); + Clear(); } -void LINEINPUT::clear() +void CLineInput::Clear() { - mem_zero(str, sizeof(str)); - len = 0; - cursor_pos = 0; + mem_zero(m_Str, sizeof(m_Str)); + m_Len = 0; + m_CursorPos = 0; } -void LINEINPUT::set(const char *string) +void CLineInput::Set(const char *pString) { - str_copy(str, string, sizeof(str)); - len = strlen(str); - cursor_pos = len; + str_copy(m_Str, pString, sizeof(m_Str)); + m_Len = str_length(m_Str); + m_CursorPos = m_Len; } -void LINEINPUT::manipulate(INPUT_EVENT e, char *str, int str_max_size, int *str_len_ptr, int *cursor_pos_ptr) +bool CLineInput::Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *pStrLenPtr, int *pCursorPosPtr) { - int cursor_pos = *cursor_pos_ptr; - int len = *str_len_ptr; + int CursorPos = *pCursorPosPtr; + int Len = *pStrLenPtr; + bool Changes = false; - if(cursor_pos > len) - cursor_pos = len; + if(CursorPos > Len) + CursorPos = Len; - int code = e.unicode; - int k = e.key; + int Code = e.m_Unicode; + int k = e.m_Key; // 127 is produced on Mac OS X and corresponds to the delete key - if (!(code >= 0 && code < 32) && code != 127) + if (!(Code >= 0 && Code < 32) && Code != 127) { - char tmp[8]; - int charsize = str_utf8_encode(tmp, code); + char Tmp[8]; + int CharSize = str_utf8_encode(Tmp, Code); - if (len < str_max_size - charsize && cursor_pos < str_max_size - charsize) + if (Len < StrMaxSize - CharSize && CursorPos < StrMaxSize - CharSize) { - memmove(str + cursor_pos + charsize, str + cursor_pos, len - cursor_pos + charsize); - for(int i = 0; i < charsize; i++) - str[cursor_pos+i] = tmp[i]; - cursor_pos += charsize; - len += charsize; + mem_move(pStr + CursorPos + CharSize, pStr + CursorPos, Len - CursorPos + CharSize); + for(int i = 0; i < CharSize; i++) + pStr[CursorPos+i] = Tmp[i]; + CursorPos += CharSize; + Len += CharSize; + Changes = true; } } - if(e.flags&INPFLAG_PRESS) + if(e.m_Flags&IInput::FLAG_PRESS) { - if (k == KEY_BACKSPACE && cursor_pos > 0) + if (k == KEY_BACKSPACE && CursorPos > 0) { - int new_cursor_pos = str_utf8_rewind(str, cursor_pos); - int charsize = cursor_pos-new_cursor_pos; - memmove(str+new_cursor_pos, str+cursor_pos, len - charsize + 1); // +1 == null term - cursor_pos = new_cursor_pos; - len -= charsize; + int NewCursorPos = str_utf8_rewind(pStr, CursorPos); + int CharSize = CursorPos-NewCursorPos; + mem_move(pStr+NewCursorPos, pStr+CursorPos, Len - NewCursorPos - CharSize + 1); // +1 == null term + CursorPos = NewCursorPos; + Len -= CharSize; + Changes = true; } - else if (k == KEY_DELETE && cursor_pos < len) + else if (k == KEY_DELETE && CursorPos < Len) { - int p = str_utf8_forward(str, cursor_pos); - int charsize = p-cursor_pos; - memmove(str + cursor_pos, str + cursor_pos + charsize, len - cursor_pos - charsize + 1); // +1 == null term - len -= charsize; + int p = str_utf8_forward(pStr, CursorPos); + int CharSize = p-CursorPos; + mem_move(pStr + CursorPos, pStr + CursorPos + CharSize, Len - CursorPos - CharSize + 1); // +1 == null term + Len -= CharSize; + Changes = true; } - else if (k == KEY_LEFT && cursor_pos > 0) - cursor_pos = str_utf8_rewind(str, cursor_pos); - else if (k == KEY_RIGHT && cursor_pos < len) - cursor_pos = str_utf8_forward(str, cursor_pos); + else if (k == KEY_LEFT && CursorPos > 0) + CursorPos = str_utf8_rewind(pStr, CursorPos); + else if (k == KEY_RIGHT && CursorPos < Len) + CursorPos = str_utf8_forward(pStr, CursorPos); else if (k == KEY_HOME) - cursor_pos = 0; + CursorPos = 0; else if (k == KEY_END) - cursor_pos = len; + CursorPos = Len; } - *cursor_pos_ptr = cursor_pos; - *str_len_ptr = len; + *pCursorPosPtr = CursorPos; + *pStrLenPtr = Len; + + return Changes; } -void LINEINPUT::process_input(INPUT_EVENT e) +void CLineInput::ProcessInput(IInput::CEvent e) { - manipulate(e, str, sizeof(str), &len, &cursor_pos); + Manipulate(e, m_Str, sizeof(m_Str), &m_Len, &m_CursorPos); } diff --git a/src/game/client/lineinput.h b/src/game/client/lineinput.h new file mode 100644 index 00000000..f5c65282 --- /dev/null +++ b/src/game/client/lineinput.h @@ -0,0 +1,31 @@ +#ifndef GAME_CLIENT_LINEINPUT_H +#define GAME_CLIENT_LINEINPUT_H + +#include <engine/input.h> + +// line input helter +class CLineInput +{ + char m_Str[256]; + int m_Len; + int m_CursorPos; +public: + static bool Manipulate(IInput::CEvent e, char *pStr, int StrMaxSize, int *pStrLenPtr, int *pCursorPosPtr); + + class CCallback + { + public: + virtual ~CCallback() {} + virtual bool Event(IInput::CEvent e) = 0; + }; + + CLineInput(); + void Clear(); + void ProcessInput(IInput::CEvent e); + void Set(const char *pString); + const char *GetString() const { return m_Str; } + int GetLength() const { return m_Len; } + unsigned GetCursorOffset() const { return m_CursorPos; } +}; + +#endif diff --git a/src/game/client/lineinput.hpp b/src/game/client/lineinput.hpp deleted file mode 100644 index 75b2bd1d..00000000 --- a/src/game/client/lineinput.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef GAME_CLIENT_LINEINPUT_H -#define GAME_CLIENT_LINEINPUT_H - -// line input helter -class LINEINPUT -{ - char str[256]; - int len; - int cursor_pos; -public: - static void manipulate(INPUT_EVENT e, char *str, int str_max_size, int *str_len, int *cursor_pos); - - class CALLBACK - { - public: - virtual ~CALLBACK() {} - virtual bool event(INPUT_EVENT e) = 0; - }; - - LINEINPUT(); - void clear(); - void process_input(INPUT_EVENT e); - void set(const char *string); - const char *get_string() const { return str; } - int get_length() const { return len; } - unsigned cursor_offset() const { return cursor_pos; } -}; - -#endif diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index f271c7d2..ee4dc9d9 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -1,19 +1,19 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <math.h> -#include <base/math.hpp> +#include <base/math.h> -#include <engine/e_client_interface.h> -#include <engine/e_config.h> -#include <engine/client/graphics.h> -#include <game/generated/gc_data.hpp> -#include <game/generated/g_protocol.hpp> -#include <game/layers.hpp> -#include "animstate.hpp" -#include "render.hpp" +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/map.h> +#include <game/generated/client_data.h> +#include <game/generated/protocol.h> +#include <game/layers.h> +#include "animstate.h" +#include "render.h" -static float sprite_w_scale; -static float sprite_h_scale; +static float gs_SpriteWScale; +static float gs_SpriteHScale; /* @@ -36,133 +36,141 @@ static void layershot_end() config.cl_layershot++; }*/ -void CRenderTools::select_sprite(SPRITE *spr, int flags, int sx, int sy) +void CRenderTools::SelectSprite(SPRITE *pSpr, int Flags, int sx, int sy) { - int x = spr->x+sx; - int y = spr->y+sy; - int w = spr->w; - int h = spr->h; - int cx = spr->set->gridx; - int cy = spr->set->gridy; + int x = pSpr->m_X+sx; + int y = pSpr->m_Y+sy; + int w = pSpr->m_W; + int h = pSpr->m_H; + int cx = pSpr->m_pSet->m_Gridx; + int cy = pSpr->m_pSet->m_Gridy; float f = sqrtf(h*h + w*w); - sprite_w_scale = w/f; - sprite_h_scale = h/f; + gs_SpriteWScale = w/f; + gs_SpriteHScale = h/f; float x1 = x/(float)cx; float x2 = (x+w)/(float)cx; float y1 = y/(float)cy; float y2 = (y+h)/(float)cy; - float temp = 0; + float Temp = 0; - if(flags&SPRITE_FLAG_FLIP_Y) + if(Flags&SPRITE_FLAG_FLIP_Y) { - temp = y1; + Temp = y1; y1 = y2; - y2 = temp; + y2 = Temp; } - if(flags&SPRITE_FLAG_FLIP_X) + if(Flags&SPRITE_FLAG_FLIP_X) { - temp = x1; + Temp = x1; x1 = x2; - x2 = temp; + x2 = Temp; } Graphics()->QuadsSetSubset(x1, y1, x2, y2); } -void CRenderTools::select_sprite(int id, int flags, int sx, int sy) +void CRenderTools::SelectSprite(int Id, int Flags, int sx, int sy) { - if(id < 0 || id > data->num_sprites) + if(Id < 0 || Id > g_pData->m_NumSprites) return; - select_sprite(&data->sprites[id], flags, sx, sy); + SelectSprite(&g_pData->m_aSprites[Id], Flags, sx, sy); } -void CRenderTools::draw_sprite(float x, float y, float size) +void CRenderTools::DrawSprite(float x, float y, float Size) { - Graphics()->QuadsDraw(x, y, size*sprite_w_scale, size*sprite_h_scale); + IGraphics::CQuadItem QuadItem(x, y, Size*gs_SpriteWScale, Size*gs_SpriteHScale); + Graphics()->QuadsDraw(&QuadItem, 1); } -void CRenderTools::draw_round_rect_ext(float x, float y, float w, float h, float r, int corners) +void CRenderTools::DrawRoundRectExt(float x, float y, float w, float h, float r, int Corners) { - int num = 8; - for(int i = 0; i < num; i+=2) + IGraphics::CFreeformItem ArrayF[32]; + int NumItems = 0; + int Num = 8; + for(int i = 0; i < Num; i+=2) { - float a1 = i/(float)num * pi/2; - float a2 = (i+1)/(float)num * pi/2; - float a3 = (i+2)/(float)num * pi/2; - float ca1 = cosf(a1); - float ca2 = cosf(a2); - float ca3 = cosf(a3); - float sa1 = sinf(a1); - float sa2 = sinf(a2); - float sa3 = sinf(a3); - - if(corners&1) // TL - Graphics()->QuadsDrawFreeform( + float a1 = i/(float)Num * pi/2; + float a2 = (i+1)/(float)Num * pi/2; + float a3 = (i+2)/(float)Num * pi/2; + float Ca1 = cosf(a1); + float Ca2 = cosf(a2); + float Ca3 = cosf(a3); + float Sa1 = sinf(a1); + float Sa2 = sinf(a2); + float Sa3 = sinf(a3); + + if(Corners&1) // TL + ArrayF[NumItems++] = IGraphics::CFreeformItem( x+r, y+r, - x+(1-ca1)*r, y+(1-sa1)*r, - x+(1-ca3)*r, y+(1-sa3)*r, - x+(1-ca2)*r, y+(1-sa2)*r); + x+(1-Ca1)*r, y+(1-Sa1)*r, + x+(1-Ca3)*r, y+(1-Sa3)*r, + x+(1-Ca2)*r, y+(1-Sa2)*r); - if(corners&2) // TR - Graphics()->QuadsDrawFreeform( + if(Corners&2) // TR + ArrayF[NumItems++] = IGraphics::CFreeformItem( x+w-r, y+r, - x+w-r+ca1*r, y+(1-sa1)*r, - x+w-r+ca3*r, y+(1-sa3)*r, - x+w-r+ca2*r, y+(1-sa2)*r); + x+w-r+Ca1*r, y+(1-Sa1)*r, + x+w-r+Ca3*r, y+(1-Sa3)*r, + x+w-r+Ca2*r, y+(1-Sa2)*r); - if(corners&4) // BL - Graphics()->QuadsDrawFreeform( + if(Corners&4) // BL + ArrayF[NumItems++] = IGraphics::CFreeformItem( x+r, y+h-r, - x+(1-ca1)*r, y+h-r+sa1*r, - x+(1-ca3)*r, y+h-r+sa3*r, - x+(1-ca2)*r, y+h-r+sa2*r); + x+(1-Ca1)*r, y+h-r+Sa1*r, + x+(1-Ca3)*r, y+h-r+Sa3*r, + x+(1-Ca2)*r, y+h-r+Sa2*r); - if(corners&8) // BR - Graphics()->QuadsDrawFreeform( + if(Corners&8) // BR + ArrayF[NumItems++] = IGraphics::CFreeformItem( x+w-r, y+h-r, - x+w-r+ca1*r, y+h-r+sa1*r, - x+w-r+ca3*r, y+h-r+sa3*r, - x+w-r+ca2*r, y+h-r+sa2*r); + x+w-r+Ca1*r, y+h-r+Sa1*r, + x+w-r+Ca3*r, y+h-r+Sa3*r, + x+w-r+Ca2*r, y+h-r+Sa2*r); } - - Graphics()->QuadsDrawTL(x+r, y+r, w-r*2, h-r*2); // center - Graphics()->QuadsDrawTL(x+r, y, w-r*2, r); // top - Graphics()->QuadsDrawTL(x+r, y+h-r, w-r*2, r); // bottom - Graphics()->QuadsDrawTL(x, y+r, r, h-r*2); // left - Graphics()->QuadsDrawTL(x+w-r, y+r, r, h-r*2); // right + Graphics()->QuadsDrawFreeform(ArrayF, NumItems); + + IGraphics::CQuadItem ArrayQ[9]; + NumItems = 0; + ArrayQ[NumItems++] = IGraphics::CQuadItem(x+r, y+r, w-r*2, h-r*2); // center + ArrayQ[NumItems++] = IGraphics::CQuadItem(x+r, y, w-r*2, r); // top + ArrayQ[NumItems++] = IGraphics::CQuadItem(x+r, y+h-r, w-r*2, r); // bottom + ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y+r, r, h-r*2); // left + ArrayQ[NumItems++] = IGraphics::CQuadItem(x+w-r, y+r, r, h-r*2); // right - if(!(corners&1)) Graphics()->QuadsDrawTL(x, y, r, r); // TL - if(!(corners&2)) Graphics()->QuadsDrawTL(x+w, y, -r, r); // TR - if(!(corners&4)) Graphics()->QuadsDrawTL(x, y+h, r, -r); // BL - if(!(corners&8)) Graphics()->QuadsDrawTL(x+w, y+h, -r, -r); // BR + if(!(Corners&1)) ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y, r, r); // TL + if(!(Corners&2)) ArrayQ[NumItems++] = IGraphics::CQuadItem(x+w, y, -r, r); // TR + if(!(Corners&4)) ArrayQ[NumItems++] = IGraphics::CQuadItem(x, y+h, r, -r); // BL + if(!(Corners&8)) ArrayQ[NumItems++] = IGraphics::CQuadItem(x+w, y+h, -r, -r); // BR + + Graphics()->QuadsDrawTL(ArrayQ, NumItems); } -void CRenderTools::draw_round_rect(float x, float y, float w, float h, float r) +void CRenderTools::DrawRoundRect(float x, float y, float w, float h, float r) { - draw_round_rect_ext(x,y,w,h,r,0xf); + DrawRoundRectExt(x,y,w,h,r,0xf); } -void CRenderTools::DrawUIRect(const CUIRect *r, vec4 color, int corners, float rounding) +void CRenderTools::DrawUIRect(const CUIRect *r, vec4 Color, int Corners, float Rounding) { Graphics()->TextureSet(-1); // TODO: FIX US Graphics()->QuadsBegin(); - Graphics()->SetColor(color.r, color.g, color.b, color.a); - draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*UI()->Scale(), corners); + Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a); + DrawRoundRectExt(r->x,r->y,r->w,r->h,Rounding*UI()->Scale(), Corners); Graphics()->QuadsEnd(); } -void CRenderTools::RenderTee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos) +void CRenderTools::RenderTee(CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos) { - vec2 direction = dir; - vec2 position = pos; + vec2 Direction = Dir; + vec2 Position = Pos; //Graphics()->TextureSet(data->images[IMAGE_CHAR_DEFAULT].id); - Graphics()->TextureSet(info->texture); + Graphics()->TextureSet(pInfo->m_Texture); // TODO: FIX ME Graphics()->QuadsBegin(); @@ -172,75 +180,79 @@ void CRenderTools::RenderTee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, // second pass we draw the filling for(int p = 0; p < 2; p++) { - int outline = p==0 ? 1 : 0; + int OutLine = p==0 ? 1 : 0; for(int f = 0; f < 2; f++) { - float animscale = info->size * 1.0f/64.0f; - float basesize = info->size; + float AnimScale = pInfo->m_Size * 1.0f/64.0f; + float BaseSize = pInfo->m_Size; if(f == 1) { - Graphics()->QuadsSetRotation(anim->body.angle*pi*2); + Graphics()->QuadsSetRotation(pAnim->GetBody()->m_Angle*pi*2); // draw body - Graphics()->SetColor(info->color_body.r, info->color_body.g, info->color_body.b, 1.0f); - vec2 body_pos = position + vec2(anim->body.x, anim->body.y)*animscale; - select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0); - Graphics()->QuadsDraw(body_pos.x, body_pos.y, basesize, basesize); + Graphics()->SetColor(pInfo->m_ColorBody.r, pInfo->m_ColorBody.g, pInfo->m_ColorBody.b, 1.0f); + vec2 BodyPos = Position + vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y)*AnimScale; + SelectSprite(OutLine?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0); + IGraphics::CQuadItem QuadItem(BodyPos.x, BodyPos.y, BaseSize, BaseSize); + Graphics()->QuadsDraw(&QuadItem, 1); // draw eyes if(p == 1) { - switch (emote) + switch (Emote) { case EMOTE_PAIN: - select_sprite(SPRITE_TEE_EYE_PAIN, 0, 0, 0); + SelectSprite(SPRITE_TEE_EYE_PAIN, 0, 0, 0); break; case EMOTE_HAPPY: - select_sprite(SPRITE_TEE_EYE_HAPPY, 0, 0, 0); + SelectSprite(SPRITE_TEE_EYE_HAPPY, 0, 0, 0); break; case EMOTE_SURPRISE: - select_sprite(SPRITE_TEE_EYE_SURPRISE, 0, 0, 0); + SelectSprite(SPRITE_TEE_EYE_SURPRISE, 0, 0, 0); break; case EMOTE_ANGRY: - select_sprite(SPRITE_TEE_EYE_ANGRY, 0, 0, 0); + SelectSprite(SPRITE_TEE_EYE_ANGRY, 0, 0, 0); break; default: - select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, 0); + SelectSprite(SPRITE_TEE_EYE_NORMAL, 0, 0, 0); break; } - float eyescale = basesize*0.40f; - float h = emote == EMOTE_BLINK ? basesize*0.15f : eyescale; - float eyeseparation = (0.075f - 0.010f*fabs(direction.x))*basesize; - vec2 offset = vec2(direction.x*0.125f, -0.05f+direction.y*0.10f)*basesize; - Graphics()->QuadsDraw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h); - Graphics()->QuadsDraw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h); + float EyeScale = BaseSize*0.40f; + float h = Emote == EMOTE_BLINK ? BaseSize*0.15f : EyeScale; + float EyeSeparation = (0.075f - 0.010f*absolute(Direction.x))*BaseSize; + vec2 Offset = vec2(Direction.x*0.125f, -0.05f+Direction.y*0.10f)*BaseSize; + IGraphics::CQuadItem Array[2] = { + IGraphics::CQuadItem(BodyPos.x-EyeSeparation+Offset.x, BodyPos.y+Offset.y, EyeScale, h), + IGraphics::CQuadItem(BodyPos.x+EyeSeparation+Offset.x, BodyPos.y+Offset.y, -EyeScale, h)}; + Graphics()->QuadsDraw(Array, 2); } } // draw feet - ANIM_KEYFRAME *foot = f ? &anim->front_foot : &anim->back_foot; + ANIM_KEYFRAME *pFoot = f ? pAnim->GetFrontFoot() : pAnim->GetBackFoot(); - float w = basesize; - float h = basesize/2; + float w = BaseSize; + float h = BaseSize/2; - Graphics()->QuadsSetRotation(foot->angle*pi*2); + Graphics()->QuadsSetRotation(pFoot->m_Angle*pi*2); - bool indicate = !info->got_airjump && config.cl_airjumpindicator; + bool Indicate = !pInfo->m_GotAirJump && g_Config.m_ClAirjumpindicator; float cs = 1.0f; // color scale - if(outline) - select_sprite(SPRITE_TEE_FOOT_OUTLINE, 0, 0, 0); + if(OutLine) + SelectSprite(SPRITE_TEE_FOOT_OUTLINE, 0, 0, 0); else { - select_sprite(SPRITE_TEE_FOOT, 0, 0, 0); - if(indicate) + SelectSprite(SPRITE_TEE_FOOT, 0, 0, 0); + if(Indicate) cs = 0.5f; } - Graphics()->SetColor(info->color_feet.r*cs, info->color_feet.g*cs, info->color_feet.b*cs, 1.0f); - Graphics()->QuadsDraw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h); + Graphics()->SetColor(pInfo->m_ColorFeet.r*cs, pInfo->m_ColorFeet.g*cs, pInfo->m_ColorFeet.b*cs, 1.0f); + IGraphics::CQuadItem QuadItem(Position.x+pFoot->m_X*AnimScale, Position.y+pFoot->m_Y*AnimScale, w, h); + Graphics()->QuadsDraw(&QuadItem, 1); } } @@ -249,67 +261,68 @@ void CRenderTools::RenderTee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, } -static void calc_screen_params(float amount, float wmax, float hmax, float aspect, float *w, float *h) +static void CalcScreenParams(float Amount, float WMax, float HMax, float Aspect, float *w, float *h) { - float f = sqrt(amount) / sqrt(aspect); - *w = f*aspect; + float f = sqrtf(Amount) / sqrtf(Aspect); + *w = f*Aspect; *h = f; // limit the view - if(*w > wmax) + if(*w > WMax) { - *w = wmax; - *h = *w/aspect; + *w = WMax; + *h = *w/Aspect; } - if(*h > hmax) + if(*h > HMax) { - *h = hmax; - *w = *h*aspect; + *h = HMax; + *w = *h*Aspect; } } -void CRenderTools::mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y, - float offset_x, float offset_y, float aspect, float zoom, float *points) +void CRenderTools::MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, + float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints) { - float width, height; - calc_screen_params(1150*1000, 1500, 1050, aspect, &width, &height); - center_x *= parallax_x; - center_y *= parallax_y; - width *= zoom; - height *= zoom; - points[0] = offset_x+center_x-width/2; - points[1] = offset_y+center_y-height/2; - points[2] = offset_x+center_x+width/2; - points[3] = offset_y+center_y+height/2; + float Width, Height; + CalcScreenParams(1150*1000, 1500, 1050, Aspect, &Width, &Height); + CenterX *= ParallaxX; + CenterY *= ParallaxY; + Width *= Zoom; + Height *= Zoom; + pPoints[0] = OffsetX+CenterX-Width/2; + pPoints[1] = OffsetY+CenterY-Height/2; + pPoints[2] = OffsetX+CenterX+Width/2; + pPoints[3] = OffsetY+CenterY+Height/2; } -void CRenderTools::render_tilemap_generate_skip() +void CRenderTools::RenderTilemapGenerateSkip(class CLayers *pLayers) { - for(int g = 0; g < layers_num_groups(); g++) + + for(int g = 0; g < pLayers->NumGroups(); g++) { - MAPITEM_GROUP *group = layers_get_group(g); + CMapItemGroup *pGroup = pLayers->GetGroup(g); - for(int l = 0; l < group->num_layers; l++) + for(int l = 0; l < pGroup->m_NumLayers; l++) { - MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l); + CMapItemLayer *pLayer = pLayers->GetLayer(pGroup->m_StartLayer+l); - if(layer->type == LAYERTYPE_TILES) + if(pLayer->m_Type == LAYERTYPE_TILES) { - MAPITEM_LAYER_TILEMAP *tmap = (MAPITEM_LAYER_TILEMAP *)layer; - TILE *tiles = (TILE *)map_get_data(tmap->data); - for(int y = 0; y < tmap->height; y++) + CMapItemLayerTilemap *pTmap = (CMapItemLayerTilemap *)pLayer; + CTile *pTiles = (CTile *)pLayers->Map()->GetData(pTmap->m_Data); + for(int y = 0; y < pTmap->m_Height; y++) { - for(int x = 1; x < tmap->width; x++) + for(int x = 1; x < pTmap->m_Width; x++) { int sx; - for(sx = 1; x+sx < tmap->width && sx < 255; sx++) + for(sx = 1; x+sx < pTmap->m_Width && sx < 255; sx++) { - if(tiles[y*tmap->width+x+sx].index) + if(pTiles[y*pTmap->m_Width+x+sx].m_Index) break; } - tiles[y*tmap->width+x].skip = sx-1; + pTiles[y*pTmap->m_Width+x].m_Skip = sx-1; } } } diff --git a/src/game/client/render.h b/src/game/client/render.h new file mode 100644 index 00000000..a546b3eb --- /dev/null +++ b/src/game/client/render.h @@ -0,0 +1,79 @@ +#ifndef GAME_CLIENT_RENDER_H +#define GAME_CLIENT_RENDER_H + +#include <base/vmath.h> +#include <game/mapitems.h> +#include "ui.h" + + +struct CTeeRenderInfo +{ + CTeeRenderInfo() + { + m_Texture = -1; + m_ColorBody = vec4(1,1,1,1); + m_ColorFeet = vec4(1,1,1,1); + m_Size = 1.0f; + m_GotAirJump = 1; + }; + + int m_Texture; + vec4 m_ColorBody; + vec4 m_ColorFeet; + float m_Size; + int m_GotAirJump; +}; + +// sprite renderings +enum +{ + SPRITE_FLAG_FLIP_Y=1, + SPRITE_FLAG_FLIP_X=2, + + LAYERRENDERFLAG_OPAQUE=1, + LAYERRENDERFLAG_TRANSPARENT=2, + + TILERENDERFLAG_EXTEND=4, +}; + + +class CRenderTools +{ +public: + class IGraphics *m_pGraphics; + class CUI *m_pUI; + + class IGraphics *Graphics() const { return m_pGraphics; } + class CUI *UI() const { return m_pUI; } + + //typedef struct SPRITE; + + void SelectSprite(struct SPRITE *pSprite, int Flags=0, int sx=0, int sy=0); + void SelectSprite(int id, int Flags=0, int sx=0, int sy=0); + + void DrawSprite(float x, float y, float size); + + // rects + void DrawRoundRect(float x, float y, float w, float h, float r); + void DrawRoundRectExt(float x, float y, float w, float h, float r, int Corners); + + void DrawUIRect(const CUIRect *pRect, vec4 Color, int Corners, float Rounding); + + // larger rendering methods + void RenderTilemapGenerateSkip(class CLayers *pLayers); + + // object render methods (gc_render_obj.cpp) + void RenderTee(class CAnimState *pAnim, CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos); + + // map render methods (gc_render_map.cpp) + static void RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, float Time, float *pResult); + void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser); + void RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int Flags); + + // helpers + void MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, + float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints); + +}; + +#endif diff --git a/src/game/client/render.hpp b/src/game/client/render.hpp deleted file mode 100644 index 8e99b432..00000000 --- a/src/game/client/render.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef GAME_CLIENT_RENDER_H -#define GAME_CLIENT_RENDER_H - -#include <base/vmath.hpp> - -#include "../mapitems.hpp" -#include "ui.hpp" - - -struct TEE_RENDER_INFO -{ - TEE_RENDER_INFO() - { - texture = -1; - color_body = vec4(1,1,1,1); - color_feet = vec4(1,1,1,1); - size = 1.0f; - got_airjump = 1; - }; - - int texture; - vec4 color_body; - vec4 color_feet; - float size; - int got_airjump; -}; - -// sprite renderings -enum -{ - SPRITE_FLAG_FLIP_Y=1, - SPRITE_FLAG_FLIP_X=2, - - LAYERRENDERFLAG_OPAQUE=1, - LAYERRENDERFLAG_TRANSPARENT=2, - - TILERENDERFLAG_EXTEND=4, -}; - - -class CRenderTools -{ -public: - class IGraphics *m_pGraphics; - class CUI *m_pUI; - - class IGraphics *Graphics() const { return m_pGraphics; } - class CUI *UI() const { return m_pUI; } - - //typedef struct SPRITE; - - void select_sprite(struct SPRITE *spr, int flags=0, int sx=0, int sy=0); - void select_sprite(int id, int flags=0, int sx=0, int sy=0); - - void draw_sprite(float x, float y, float size); - - // rects - void draw_round_rect(float x, float y, float w, float h, float r); - void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners); - - void DrawUIRect(const CUIRect *r, vec4 color, int corners, float rounding); - - // larger rendering methods - void render_tilemap_generate_skip(); - - // object render methods (gc_render_obj.cpp) - void RenderTee(class ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos); - - // map render methods (gc_render_map.cpp) - static void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result); - void render_quads(QUAD *quads, int num_quads, int flags, void (*eval)(float time_offset, int env, float *channels, void *user), void *user); - void render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int flags); - - // helpers - void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y, - float offset_x, float offset_y, float aspect, float zoom, float *points); - -}; - -#endif diff --git a/src/game/client/render_map.cpp b/src/game/client/render_map.cpp index ea3b8420..0354b9d5 100644 --- a/src/game/client/render_map.cpp +++ b/src/game/client/render_map.cpp @@ -1,198 +1,198 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <math.h> -#include <base/math.hpp> -#include <engine/e_client_interface.h> -#include <engine/client/graphics.h> +#include <base/math.h> +#include <engine/graphics.h> -#include "render.hpp" +#include "render.h" -void CRenderTools::render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result) +void CRenderTools::RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, float Time, float *pResult) { - if(num_points == 0) + if(NumPoints == 0) { - result[0] = 0; - result[1] = 0; - result[2] = 0; - result[3] = 0; + pResult[0] = 0; + pResult[1] = 0; + pResult[2] = 0; + pResult[3] = 0; return; } - if(num_points == 1) + if(NumPoints == 1) { - result[0] = fx2f(points[0].values[0]); - result[1] = fx2f(points[0].values[1]); - result[2] = fx2f(points[0].values[2]); - result[3] = fx2f(points[0].values[3]); + pResult[0] = fx2f(pPoints[0].m_aValues[0]); + pResult[1] = fx2f(pPoints[0].m_aValues[1]); + pResult[2] = fx2f(pPoints[0].m_aValues[2]); + pResult[3] = fx2f(pPoints[0].m_aValues[3]); return; } - time = fmod(time, points[num_points-1].time/1000.0f)*1000.0f; - for(int i = 0; i < num_points-1; i++) + Time = fmod(Time, pPoints[NumPoints-1].m_Time/1000.0f)*1000.0f; + for(int i = 0; i < NumPoints-1; i++) { - if(time >= points[i].time && time <= points[i+1].time) + if(Time >= pPoints[i].m_Time && Time <= pPoints[i+1].m_Time) { - float delta = points[i+1].time-points[i].time; - float a = (time-points[i].time)/delta; + float Delta = pPoints[i+1].m_Time-pPoints[i].m_Time; + float a = (Time-pPoints[i].m_Time)/Delta; - if(points[i].curvetype == CURVETYPE_SMOOTH) + if(pPoints[i].m_Curvetype == CURVETYPE_SMOOTH) a = -2*a*a*a + 3*a*a; // second hermite basis - else if(points[i].curvetype == CURVETYPE_SLOW) + else if(pPoints[i].m_Curvetype == CURVETYPE_SLOW) a = a*a*a; - else if(points[i].curvetype == CURVETYPE_FAST) + else if(pPoints[i].m_Curvetype == CURVETYPE_FAST) { a = 1-a; a = 1-a*a*a; } - else if (points[i].curvetype == CURVETYPE_STEP) + else if (pPoints[i].m_Curvetype == CURVETYPE_STEP) a = 0; else { // linear } - for(int c = 0; c < channels; c++) + for(int c = 0; c < Channels; c++) { - float v0 = fx2f(points[i].values[c]); - float v1 = fx2f(points[i+1].values[c]); - result[c] = v0 + (v1-v0) * a; + float v0 = fx2f(pPoints[i].m_aValues[c]); + float v1 = fx2f(pPoints[i+1].m_aValues[c]); + pResult[c] = v0 + (v1-v0) * a; } return; } } - result[0] = fx2f(points[num_points-1].values[0]); - result[1] = fx2f(points[num_points-1].values[1]); - result[2] = fx2f(points[num_points-1].values[2]); - result[3] = fx2f(points[num_points-1].values[3]); + pResult[0] = fx2f(pPoints[NumPoints-1].m_aValues[0]); + pResult[1] = fx2f(pPoints[NumPoints-1].m_aValues[1]); + pResult[2] = fx2f(pPoints[NumPoints-1].m_aValues[2]); + pResult[3] = fx2f(pPoints[NumPoints-1].m_aValues[3]); return; } -static void rotate(POINT *center, POINT *point, float rotation) +static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) { - int x = point->x - center->x; - int y = point->y - center->y; - point->x = (int)(x * cosf(rotation) - y * sinf(rotation) + center->x); - point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y); + int x = pPoint->x - pCenter->x; + int y = pPoint->y - pCenter->y; + pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x); + pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y); } -void CRenderTools::render_quads(QUAD *quads, int num_quads, int renderflags, void (*eval)(float time_offset, int env, float *channels, void *user), void *user) +void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, void (*pfnEval)(float TimeOffset, int Env, float *pChannels, void *pUser), void *pUser) { Graphics()->QuadsBegin(); - float conv = 1/255.0f; - for(int i = 0; i < num_quads; i++) + float Conv = 1/255.0f; + for(int i = 0; i < NumQuads; i++) { - QUAD *q = &quads[i]; + CQuad *q = &pQuads[i]; float r=1, g=1, b=1, a=1; - if(q->color_env >= 0) + if(q->m_ColorEnv >= 0) { - float channels[4]; - eval(q->color_env_offset/1000.0f, q->color_env, channels, user); - r = channels[0]; - g = channels[1]; - b = channels[2]; - a = channels[3]; + float aChannels[4]; + pfnEval(q->m_ColorEnvOffset/1000.0f, q->m_ColorEnv, aChannels, pUser); + r = aChannels[0]; + g = aChannels[1]; + b = aChannels[2]; + a = aChannels[3]; } - bool opaque = false; - if(a < 0.01f || (q->colors[0].a < 0.01f && q->colors[1].a < 0.01f && q->colors[2].a < 0.01f && q->colors[3].a < 0.01f)) - opaque = true; + bool Opaque = false; + if(a < 0.01f || (q->m_aColors[0].a < 0.01f && q->m_aColors[1].a < 0.01f && q->m_aColors[2].a < 0.01f && q->m_aColors[3].a < 0.01f)) + Opaque = true; - if(opaque && !(renderflags&LAYERRENDERFLAG_OPAQUE)) + if(Opaque && !(RenderFlags&LAYERRENDERFLAG_OPAQUE)) continue; - if(!opaque && !(renderflags&LAYERRENDERFLAG_TRANSPARENT)) + if(!Opaque && !(RenderFlags&LAYERRENDERFLAG_TRANSPARENT)) continue; Graphics()->QuadsSetSubsetFree( - fx2f(q->texcoords[0].x), fx2f(q->texcoords[0].y), - fx2f(q->texcoords[1].x), fx2f(q->texcoords[1].y), - fx2f(q->texcoords[2].x), fx2f(q->texcoords[2].y), - fx2f(q->texcoords[3].x), fx2f(q->texcoords[3].y) + fx2f(q->m_aTexcoords[0].x), fx2f(q->m_aTexcoords[0].y), + fx2f(q->m_aTexcoords[1].x), fx2f(q->m_aTexcoords[1].y), + fx2f(q->m_aTexcoords[2].x), fx2f(q->m_aTexcoords[2].y), + fx2f(q->m_aTexcoords[3].x), fx2f(q->m_aTexcoords[3].y) ); - float offset_x = 0; - float offset_y = 0; - float rot = 0; + float OffsetX = 0; + float OffsetY = 0; + float Rot = 0; // TODO: fix this - if(q->pos_env >= 0) + if(q->m_PosEnv >= 0) { - float channels[4]; - eval(q->pos_env_offset/1000.0f, q->pos_env, channels, user); - offset_x = channels[0]; - offset_y = channels[1]; - rot = channels[2]/360.0f*pi*2; + float aChannels[4]; + pfnEval(q->m_PosEnvOffset/1000.0f, q->m_PosEnv, aChannels, pUser); + OffsetX = aChannels[0]; + OffsetY = aChannels[1]; + Rot = aChannels[2]/360.0f*pi*2; } - - Graphics()->SetColorVertex(0, q->colors[0].r*conv*r, q->colors[0].g*conv*g, q->colors[0].b*conv*b, q->colors[0].a*conv*a); - Graphics()->SetColorVertex(1, q->colors[1].r*conv*r, q->colors[1].g*conv*g, q->colors[1].b*conv*b, q->colors[1].a*conv*a); - Graphics()->SetColorVertex(2, q->colors[2].r*conv*r, q->colors[2].g*conv*g, q->colors[2].b*conv*b, q->colors[2].a*conv*a); - Graphics()->SetColorVertex(3, q->colors[3].r*conv*r, q->colors[3].g*conv*g, q->colors[3].b*conv*b, q->colors[3].a*conv*a); + IGraphics::CColorVertex Array[4] = { + IGraphics::CColorVertex(0, q->m_aColors[0].r*Conv*r, q->m_aColors[0].g*Conv*g, q->m_aColors[0].b*Conv*b, q->m_aColors[0].a*Conv*a), + IGraphics::CColorVertex(1, q->m_aColors[1].r*Conv*r, q->m_aColors[1].g*Conv*g, q->m_aColors[1].b*Conv*b, q->m_aColors[1].a*Conv*a), + IGraphics::CColorVertex(2, q->m_aColors[2].r*Conv*r, q->m_aColors[2].g*Conv*g, q->m_aColors[2].b*Conv*b, q->m_aColors[2].a*Conv*a), + IGraphics::CColorVertex(3, q->m_aColors[3].r*Conv*r, q->m_aColors[3].g*Conv*g, q->m_aColors[3].b*Conv*b, q->m_aColors[3].a*Conv*a)}; + Graphics()->SetColorVertex(Array, 4); - POINT *points = q->points; + CPoint *pPoints = q->m_aPoints; - if(rot != 0) + if(Rot != 0) { - static POINT rotated[4]; - rotated[0] = q->points[0]; - rotated[1] = q->points[1]; - rotated[2] = q->points[2]; - rotated[3] = q->points[3]; - points = rotated; + static CPoint aRotated[4]; + aRotated[0] = q->m_aPoints[0]; + aRotated[1] = q->m_aPoints[1]; + aRotated[2] = q->m_aPoints[2]; + aRotated[3] = q->m_aPoints[3]; + pPoints = aRotated; - rotate(&q->points[4], &rotated[0], rot); - rotate(&q->points[4], &rotated[1], rot); - rotate(&q->points[4], &rotated[2], rot); - rotate(&q->points[4], &rotated[3], rot); + Rotate(&q->m_aPoints[4], &aRotated[0], Rot); + Rotate(&q->m_aPoints[4], &aRotated[1], Rot); + Rotate(&q->m_aPoints[4], &aRotated[2], Rot); + Rotate(&q->m_aPoints[4], &aRotated[3], Rot); } - Graphics()->QuadsDrawFreeform( - fx2f(points[0].x)+offset_x, fx2f(points[0].y)+offset_y, - fx2f(points[1].x)+offset_x, fx2f(points[1].y)+offset_y, - fx2f(points[2].x)+offset_x, fx2f(points[2].y)+offset_y, - fx2f(points[3].x)+offset_x, fx2f(points[3].y)+offset_y - ); + IGraphics::CFreeformItem Freeform( + fx2f(pPoints[0].x)+OffsetX, fx2f(pPoints[0].y)+OffsetY, + fx2f(pPoints[1].x)+OffsetX, fx2f(pPoints[1].y)+OffsetY, + fx2f(pPoints[2].x)+OffsetX, fx2f(pPoints[2].y)+OffsetY, + fx2f(pPoints[3].x)+OffsetX, fx2f(pPoints[3].y)+OffsetY); + Graphics()->QuadsDrawFreeform(&Freeform, 1); } Graphics()->QuadsEnd(); } -void CRenderTools::render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int renderflags) +void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, vec4 Color, int RenderFlags) { //Graphics()->TextureSet(img_get(tmap->image)); - float screen_x0, screen_y0, screen_x1, screen_y1; - Graphics()->GetScreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); //Graphics()->MapScreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50); // calculate the final pixelsize for the tiles - float tile_pixelsize = 1024/32.0f; - float final_tilesize = scale/(screen_x1-screen_x0) * Graphics()->ScreenWidth(); - float final_tilesize_scale = final_tilesize/tile_pixelsize; + float TilePixelSize = 1024/32.0f; + float FinalTileSize = Scale/(ScreenX1-ScreenX0) * Graphics()->ScreenWidth(); + float FinalTilesetScale = FinalTileSize/TilePixelSize; Graphics()->QuadsBegin(); - Graphics()->SetColor(color.r, color.g, color.b, color.a); + Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a); - int starty = (int)(screen_y0/scale)-1; - int startx = (int)(screen_x0/scale)-1; - int endy = (int)(screen_y1/scale)+1; - int endx = (int)(screen_x1/scale)+1; + int StartY = (int)(ScreenY0/Scale)-1; + int StartX = (int)(ScreenX0/Scale)-1; + int EndY = (int)(ScreenY1/Scale)+1; + int EndX = (int)(ScreenX1/Scale)+1; // adjust the texture shift according to mipmap level - float texsize = 1024.0f; - float frac = (1.25f/texsize) * (1/final_tilesize_scale); - float nudge = (0.5f/texsize) * (1/final_tilesize_scale); + float TexSize = 1024.0f; + float Frac = (1.25f/TexSize) * (1/FinalTilesetScale); + float Nudge = (0.5f/TexSize) * (1/FinalTilesetScale); - for(int y = starty; y < endy; y++) - for(int x = startx; x < endx; x++) + for(int y = StartY; y < EndY; y++) + for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; - if(renderflags&TILERENDERFLAG_EXTEND) + if(RenderFlags&TILERENDERFLAG_EXTEND) { if(mx<0) mx = 0; @@ -217,59 +217,60 @@ void CRenderTools::render_tilemap(TILE *tiles, int w, int h, float scale, vec4 c int c = mx + my*w; - unsigned char index = tiles[c].index; - if(index) + unsigned char Index = pTiles[c].m_Index; + if(Index) { - unsigned char flags = tiles[c].flags; + unsigned char Flags = pTiles[c].m_Flags; - bool render = false; - if(flags&TILEFLAG_OPAQUE) + bool Render = false; + if(Flags&TILEFLAG_OPAQUE) { - if(renderflags&LAYERRENDERFLAG_OPAQUE) - render = true; + if(RenderFlags&LAYERRENDERFLAG_OPAQUE) + Render = true; } else { - if(renderflags&LAYERRENDERFLAG_TRANSPARENT) - render = true; + if(RenderFlags&LAYERRENDERFLAG_TRANSPARENT) + Render = true; } - if(render) + if(Render) { - int tx = index%16; - int ty = index/16; - int px0 = tx*(1024/16); - int py0 = ty*(1024/16); - int px1 = (tx+1)*(1024/16)-1; - int py1 = (ty+1)*(1024/16)-1; + int tx = Index%16; + int ty = Index/16; + int Px0 = tx*(1024/16); + int Py0 = ty*(1024/16); + int Px1 = (tx+1)*(1024/16)-1; + int Py1 = (ty+1)*(1024/16)-1; - float u0 = nudge + px0/texsize+frac; - float v0 = nudge + py0/texsize+frac; - float u1 = nudge + px1/texsize-frac; - float v1 = nudge + py1/texsize-frac; + float u0 = Nudge + Px0/TexSize+Frac; + float v0 = Nudge + Py0/TexSize+Frac; + float u1 = Nudge + Px1/TexSize-Frac; + float v1 = Nudge + Py1/TexSize-Frac; - if(flags&TILEFLAG_VFLIP) + if(Flags&TILEFLAG_VFLIP) { - float tmp = u0; + float Tmp = u0; u0 = u1; - u1 = tmp; + u1 = Tmp; } - if(flags&TILEFLAG_HFLIP) + if(Flags&TILEFLAG_HFLIP) { - float tmp = v0; + float Tmp = v0; v0 = v1; - v1 = tmp; + v1 = Tmp; } Graphics()->QuadsSetSubset(u0,v0,u1,v1); - Graphics()->QuadsDrawTL(x*scale, y*scale, scale, scale); + IGraphics::CQuadItem QuadItem(x*Scale, y*Scale, Scale, Scale); + Graphics()->QuadsDrawTL(&QuadItem, 1); } } - x += tiles[c].skip; + x += pTiles[c].m_Skip; } Graphics()->QuadsEnd(); - Graphics()->MapScreen(screen_x0, screen_y0, screen_x1, screen_y1); + Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 4aaaf32f..2895b717 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -1,10 +1,10 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> -#include <engine/e_client_interface.h> -#include <engine/e_config.h> -#include <engine/client/graphics.h> -#include "ui.hpp" +#include <engine/shared/config.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include "ui.h" /******************************************************** UI @@ -30,12 +30,12 @@ CUI::CUI() m_Screen.h = 480.0f; } -int CUI::Update(float mx, float my, float mwx, float mwy, int Buttons) +int CUI::Update(float Mx, float My, float Mwx, float Mwy, int Buttons) { - m_MouseX = mx; - m_MouseY = my; - m_MouseWorldX = mwx; - m_MouseWorldY = mwy; + m_MouseX = Mx; + m_MouseY = My; + m_MouseWorldX = Mwx; + m_MouseWorldY = Mwy; m_LastMouseButtons = m_MouseButtons; m_MouseButtons = Buttons; m_pHotItem = m_pBecommingHotItem; @@ -54,11 +54,11 @@ int CUI::MouseInside(const CUIRect *r) CUIRect *CUI::Screen() { - float aspect = Graphics()->ScreenAspect(); + float Aspect = Graphics()->ScreenAspect(); float w, h; h = 600; - w = aspect*h; + w = Aspect*h; m_Screen.w = w; m_Screen.h = h; @@ -78,9 +78,9 @@ void CUI::SetScale(float s) void CUI::ClipEnable(const CUIRect *r) { - float xscale = Graphics()->ScreenWidth()/Screen()->w; - float yscale = Graphics()->ScreenHeight()/Screen()->h; - Graphics()->ClipEnable((int)(r->x*xscale), (int)(r->y*yscale), (int)(r->w*xscale), (int)(r->h*yscale)); + float XScale = Graphics()->ScreenWidth()/Screen()->w; + float YScale = Graphics()->ScreenHeight()/Screen()->h; + Graphics()->ClipEnable((int)(r->x*XScale), (int)(r->y*YScale), (int)(r->w*XScale), (int)(r->h*YScale)); } void CUI::ClipDisable() @@ -88,153 +88,153 @@ void CUI::ClipDisable() Graphics()->ClipDisable(); } -void CUIRect::HSplitTop(float cut, CUIRect *top, CUIRect *bottom) const +void CUIRect::HSplitTop(float Cut, CUIRect *pTop, CUIRect *pBottom) const { CUIRect r = *this; - cut *= Scale(); + Cut *= Scale(); - if (top) + if (pTop) { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = cut; + pTop->x = r.x; + pTop->y = r.y; + pTop->w = r.w; + pTop->h = Cut; } - if (bottom) + if (pBottom) { - bottom->x = r.x; - bottom->y = r.y + cut; - bottom->w = r.w; - bottom->h = r.h - cut; + pBottom->x = r.x; + pBottom->y = r.y + Cut; + pBottom->w = r.w; + pBottom->h = r.h - Cut; } } -void CUIRect::HSplitBottom(float cut, CUIRect *top, CUIRect *bottom) const +void CUIRect::HSplitBottom(float Cut, CUIRect *pTop, CUIRect *pBottom) const { CUIRect r = *this; - cut *= Scale(); + Cut *= Scale(); - if (top) + if (pTop) { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = r.h - cut; + pTop->x = r.x; + pTop->y = r.y; + pTop->w = r.w; + pTop->h = r.h - Cut; } - if (bottom) + if (pBottom) { - bottom->x = r.x; - bottom->y = r.y + r.h - cut; - bottom->w = r.w; - bottom->h = cut; + pBottom->x = r.x; + pBottom->y = r.y + r.h - Cut; + pBottom->w = r.w; + pBottom->h = Cut; } } -void CUIRect::VSplitMid(CUIRect *left, CUIRect *right) const +void CUIRect::VSplitMid(CUIRect *pLeft, CUIRect *pRight) const { CUIRect r = *this; - float cut = r.w/2; + float Cut = r.w/2; - if (left) + if (pLeft) { - left->x = r.x; - left->y = r.y; - left->w = cut; - left->h = r.h; + pLeft->x = r.x; + pLeft->y = r.y; + pLeft->w = Cut; + pLeft->h = r.h; } - if (right) + if (pRight) { - right->x = r.x + cut; - right->y = r.y; - right->w = r.w - cut; - right->h = r.h; + pRight->x = r.x + Cut; + pRight->y = r.y; + pRight->w = r.w - Cut; + pRight->h = r.h; } } -void CUIRect::VSplitLeft(float cut, CUIRect *left, CUIRect *right) const +void CUIRect::VSplitLeft(float Cut, CUIRect *pLeft, CUIRect *pRight) const { CUIRect r = *this; - cut *= Scale(); + Cut *= Scale(); - if (left) + if (pLeft) { - left->x = r.x; - left->y = r.y; - left->w = cut; - left->h = r.h; + pLeft->x = r.x; + pLeft->y = r.y; + pLeft->w = Cut; + pLeft->h = r.h; } - if (right) + if (pRight) { - right->x = r.x + cut; - right->y = r.y; - right->w = r.w - cut; - right->h = r.h; + pRight->x = r.x + Cut; + pRight->y = r.y; + pRight->w = r.w - Cut; + pRight->h = r.h; } } -void CUIRect::VSplitRight(float cut, CUIRect *left, CUIRect *right) const +void CUIRect::VSplitRight(float Cut, CUIRect *pLeft, CUIRect *pRight) const { CUIRect r = *this; - cut *= Scale(); + Cut *= Scale(); - if (left) + if (pLeft) { - left->x = r.x; - left->y = r.y; - left->w = r.w - cut; - left->h = r.h; + pLeft->x = r.x; + pLeft->y = r.y; + pLeft->w = r.w - Cut; + pLeft->h = r.h; } - if (right) + if (pRight) { - right->x = r.x + r.w - cut; - right->y = r.y; - right->w = cut; - right->h = r.h; + pRight->x = r.x + r.w - Cut; + pRight->y = r.y; + pRight->w = Cut; + pRight->h = r.h; } } -void CUIRect::Margin(float cut, CUIRect *other_rect) const +void CUIRect::Margin(float Cut, CUIRect *pOtherRect) const { CUIRect r = *this; - cut *= Scale(); + Cut *= Scale(); - other_rect->x = r.x + cut; - other_rect->y = r.y + cut; - other_rect->w = r.w - 2*cut; - other_rect->h = r.h - 2*cut; + pOtherRect->x = r.x + Cut; + pOtherRect->y = r.y + Cut; + pOtherRect->w = r.w - 2*Cut; + pOtherRect->h = r.h - 2*Cut; } -void CUIRect::VMargin(float cut, CUIRect *other_rect) const +void CUIRect::VMargin(float Cut, CUIRect *pOtherRect) const { CUIRect r = *this; - cut *= Scale(); + Cut *= Scale(); - other_rect->x = r.x + cut; - other_rect->y = r.y; - other_rect->w = r.w - 2*cut; - other_rect->h = r.h; + pOtherRect->x = r.x + Cut; + pOtherRect->y = r.y; + pOtherRect->w = r.w - 2*Cut; + pOtherRect->h = r.h; } -void CUIRect::HMargin(float cut, CUIRect *other_rect) const +void CUIRect::HMargin(float Cut, CUIRect *pOtherRect) const { CUIRect r = *this; - cut *= Scale(); + Cut *= Scale(); - other_rect->x = r.x; - other_rect->y = r.y + cut; - other_rect->w = r.w; - other_rect->h = r.h - 2*cut; + pOtherRect->x = r.x; + pOtherRect->y = r.y + Cut; + pOtherRect->w = r.w; + pOtherRect->h = r.h - 2*Cut; } int CUI::DoButtonLogic(const void *pID, const char *pText, int Checked, const CUIRect *pRect) { - /* logic */ + // logic int ReturnValue = 0; int Inside = MouseInside(pRect); static int ButtonUsed = 0; @@ -308,21 +308,21 @@ int CUI::DoButton(const void *id, const char *text, int checked, const CUIRect * return ret; }*/ -void CUI::DoLabel(const CUIRect *r, const char *text, float size, int align, int max_width) +void CUI::DoLabel(const CUIRect *r, const char *pText, float size, int Align, int MaxWidth) { // TODO: FIX ME!!!! //Graphics()->BlendNormal(); size *= Scale(); - if(align == 0) + if(Align == 0) { - float tw = gfx_text_width(0, size, text, max_width); - gfx_text(0, r->x + r->w/2-tw/2, r->y, size, text, max_width); + float tw = TextRender()->TextWidth(0, size, pText, MaxWidth); + TextRender()->Text(0, r->x + r->w/2-tw/2, r->y - size/10, size, pText, MaxWidth); } - else if(align < 0) - gfx_text(0, r->x, r->y, size, text, max_width); - else if(align > 0) + else if(Align < 0) + TextRender()->Text(0, r->x, r->y - size/10, size, pText, MaxWidth); + else if(Align > 0) { - float tw = gfx_text_width(0, size, text, max_width); - gfx_text(0, r->x + r->w-tw, r->y, size, text, max_width); + float tw = TextRender()->TextWidth(0, size, pText, MaxWidth); + TextRender()->Text(0, r->x + r->w-tw, r->y - size/10, size, pText, MaxWidth); } } diff --git a/src/game/client/ui.hpp b/src/game/client/ui.h index 96f6c48b..e88b0e39 100644 --- a/src/game/client/ui.hpp +++ b/src/game/client/ui.h @@ -1,6 +1,5 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef FILE_GAME_CLIENT_UI_H -#define FILE_GAME_CLIENT_UI_H +#ifndef GAME_CLIENT_UI_H +#define GAME_CLIENT_UI_H class CUIRect { @@ -27,18 +26,20 @@ class CUI const void *m_pActiveItem; const void *m_pLastActiveItem; const void *m_pBecommingHotItem; - float m_MouseX, m_MouseY; /* in gui space */ - float m_MouseWorldX, m_MouseWorldY; /* in world space */ + float m_MouseX, m_MouseY; // in gui space + float m_MouseWorldX, m_MouseWorldY; // in world space unsigned m_MouseButtons; unsigned m_LastMouseButtons; CUIRect m_Screen; class IGraphics *m_pGraphics; + class ITextRender *m_pTextRender; public: // TODO: Refactor: Fill this in - void SetGraphics(class IGraphics *pGraphics) { m_pGraphics = pGraphics; } + void SetGraphics(class IGraphics *pGraphics, class ITextRender *pTextRender) { m_pGraphics = pGraphics; m_pTextRender = pTextRender;} class IGraphics *Graphics() { return m_pGraphics; } + class ITextRender *TextRender() { return m_pTextRender; } CUI(); @@ -57,7 +58,7 @@ public: CORNER_ALL=CORNER_T|CORNER_B }; - int Update(float mx, float my, float mwx, float mwy, int buttons); + int Update(float mx, float my, float Mwx, float Mwy, int m_Buttons); float MouseX() const { return m_MouseX; } float MouseY() const { return m_MouseY; } @@ -74,10 +75,10 @@ public: const void *ActiveItem() const { return m_pActiveItem; } const void *LastActiveItem() const { return m_pLastActiveItem; } - int MouseInside(const CUIRect *r); + int MouseInside(const CUIRect *pRect); CUIRect *Screen(); - void ClipEnable(const CUIRect *r); + void ClipEnable(const CUIRect *pRect); void ClipDisable(); // TODO: Refactor: Redo UI scaling @@ -87,7 +88,7 @@ public: int DoButtonLogic(const void *pID, const char *pText /* TODO: Refactor: Remove */, int Checked, const CUIRect *pRect); // TODO: Refactor: Remove this? - void DoLabel(const CUIRect *r, const char *text, float size, int align, int max_width = -1); + void DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1); }; diff --git a/src/game/collision.cpp b/src/game/collision.cpp index 73f4a9c5..0dee57c8 100644 --- a/src/game/collision.cpp +++ b/src/game/collision.cpp @@ -1,87 +1,202 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> -#include <base/math.hpp> -#include <base/vmath.hpp> +#include <base/math.h> +#include <base/vmath.h> #include <math.h> -#include <engine/e_common_interface.h> -#include <game/mapitems.hpp> -#include <game/layers.hpp> -#include <game/collision.hpp> +#include <engine/map.h> +#include <engine/kernel.h> -static TILE *tiles; -static int width = 0; -static int height = 0; +#include <game/mapitems.h> +#include <game/layers.h> +#include <game/collision.h> -int col_width() { return width; } -int col_height() { return height; } +CCollision::CCollision() +{ + m_pTiles = 0; + m_Width = 0; + m_Height = 0; + m_pLayers = 0; +} -int col_init() +void CCollision::Init(class CLayers *pLayers) { - width = layers_game_layer()->width; - height = layers_game_layer()->height; - tiles = (TILE *)map_get_data(layers_game_layer()->data); + m_pLayers = pLayers; + m_Width = m_pLayers->GameLayer()->m_Width; + m_Height = m_pLayers->GameLayer()->m_Height; + m_pTiles = static_cast<CTile *>(m_pLayers->Map()->GetData(m_pLayers->GameLayer()->m_Data)); - for(int i = 0; i < width*height; i++) + for(int i = 0; i < m_Width*m_Height; i++) { - int index = tiles[i].index; + int Index = m_pTiles[i].m_Index; - if(index > 128) + if(Index > 128) continue; - if(index == TILE_DEATH) - tiles[i].index = COLFLAG_DEATH; - else if(index == TILE_SOLID) - tiles[i].index = COLFLAG_SOLID; - else if(index == TILE_NOHOOK) - tiles[i].index = COLFLAG_SOLID|COLFLAG_NOHOOK; - else - tiles[i].index = 0; + switch(Index) + { + case TILE_DEATH: + m_pTiles[i].m_Index = COLFLAG_DEATH; + break; + case TILE_SOLID: + m_pTiles[i].m_Index = COLFLAG_SOLID; + break; + case TILE_NOHOOK: + m_pTiles[i].m_Index = COLFLAG_SOLID|COLFLAG_NOHOOK; + break; + default: + m_pTiles[i].m_Index = 0; + } } - - return 1; } - -int col_get(int x, int y) +int CCollision::GetTile(int x, int y) { - int nx = clamp(x/32, 0, width-1); - int ny = clamp(y/32, 0, height-1); + int nx = clamp(x/32, 0, m_Width-1); + int ny = clamp(y/32, 0, m_Height-1); - if(tiles[ny*width+nx].index > 128) - return 0; - return tiles[ny*width+nx].index; + return m_pTiles[ny*m_Width+nx].m_Index > 128 ? 0 : m_pTiles[ny*m_Width+nx].m_Index; } -int col_is_solid(int x, int y) +bool CCollision::IsTileSolid(int x, int y) { - return col_get(x,y)&COLFLAG_SOLID; + return GetTile(x,y)&COLFLAG_SOLID; } - // TODO: rewrite this smarter! -int col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out_collision, vec2 *out_before_collision) +int CCollision::IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) { - float d = distance(pos0, pos1); - vec2 last = pos0; + float d = distance(Pos0, Pos1); + vec2 Last = Pos0; for(float f = 0; f < d; f++) { float a = f/d; - vec2 pos = mix(pos0, pos1, a); - if(col_is_solid(round(pos.x), round(pos.y))) + vec2 Pos = mix(Pos0, Pos1, a); + if(CheckPoint(Pos.x, Pos.y)) { - if(out_collision) - *out_collision = pos; - if(out_before_collision) - *out_before_collision = last; - return col_get(round(pos.x), round(pos.y)); + if(pOutCollision) + *pOutCollision = Pos; + if(pOutBeforeCollision) + *pOutBeforeCollision = Last; + return GetCollisionAt(Pos.x, Pos.y); } - last = pos; + Last = Pos; } - if(out_collision) - *out_collision = pos1; - if(out_before_collision) - *out_before_collision = pos1; + if(pOutCollision) + *pOutCollision = Pos1; + if(pOutBeforeCollision) + *pOutBeforeCollision = Pos1; return 0; } + +// TODO: OPT: rewrite this smarter! +void CCollision::MovePoint(vec2 *pInoutPos, vec2 *pInoutVel, float Elasticity, int *pBounces) +{ + if(pBounces) + *pBounces = 0; + + vec2 Pos = *pInoutPos; + vec2 Vel = *pInoutVel; + if(CheckPoint(Pos + Vel)) + { + int Affected = 0; + if(CheckPoint(Pos.x + Vel.x, Pos.y)) + { + pInoutVel->x *= -Elasticity; + if(pBounces) + (*pBounces)++; + Affected++; + } + + if(CheckPoint(Pos.x, Pos.y + Vel.y)) + { + pInoutVel->y *= -Elasticity; + if(pBounces) + (*pBounces)++; + Affected++; + } + + if(Affected == 0) + { + pInoutVel->x *= -Elasticity; + pInoutVel->y *= -Elasticity; + } + } + else + { + *pInoutPos = Pos + Vel; + } +} + +bool CCollision::TestBox(vec2 Pos, vec2 Size) +{ + Size *= 0.5f; + if(CheckPoint(Pos.x-Size.x, Pos.y-Size.y)) + return true; + if(CheckPoint(Pos.x+Size.x, Pos.y-Size.y)) + return true; + if(CheckPoint(Pos.x-Size.x, Pos.y+Size.y)) + return true; + if(CheckPoint(Pos.x+Size.x, Pos.y+Size.y)) + return true; + return false; +} + +void CCollision::MoveBox(vec2 *pInoutPos, vec2 *pInoutVel, vec2 Size, float Elasticity) +{ + // do the move + vec2 Pos = *pInoutPos; + vec2 Vel = *pInoutVel; + + float Distance = length(Vel); + int Max = (int)Distance; + + if(Distance > 0.00001f) + { + //vec2 old_pos = pos; + float Fraction = 1.0f/(float)(Max+1); + for(int i = 0; i <= Max; i++) + { + //float amount = i/(float)max; + //if(max == 0) + //amount = 0; + + vec2 NewPos = Pos + Vel*Fraction; // TODO: this row is not nice + + if(TestBox(vec2(NewPos.x, NewPos.y), Size)) + { + int Hits = 0; + + if(TestBox(vec2(Pos.x, NewPos.y), Size)) + { + NewPos.y = Pos.y; + Vel.y *= -Elasticity; + Hits++; + } + + if(TestBox(vec2(NewPos.x, Pos.y), Size)) + { + NewPos.x = Pos.x; + Vel.x *= -Elasticity; + Hits++; + } + + // neither of the tests got a collision. + // this is a real _corner case_! + if(Hits == 0) + { + NewPos.y = Pos.y; + Vel.y *= -Elasticity; + NewPos.x = Pos.x; + Vel.x *= -Elasticity; + } + } + + Pos = NewPos; + } + } + + *pInoutPos = Pos; + *pInoutVel = Vel; +} diff --git a/src/game/collision.h b/src/game/collision.h new file mode 100644 index 00000000..66603890 --- /dev/null +++ b/src/game/collision.h @@ -0,0 +1,37 @@ +#ifndef GAME_COLLISION_H +#define GAME_COLLISION_H + +#include <base/vmath.h> + +class CCollision +{ + class CTile *m_pTiles; + int m_Width; + int m_Height; + class CLayers *m_pLayers; + + bool IsTileSolid(int x, int y); + int GetTile(int x, int y); + +public: + enum + { + COLFLAG_SOLID=1, + COLFLAG_DEATH=2, + COLFLAG_NOHOOK=4, + }; + + CCollision(); + void Init(class CLayers *pLayers); + bool CheckPoint(float x, float y) { return IsTileSolid(round(x), round(y)); } + bool CheckPoint(vec2 p) { return CheckPoint(p.x, p.y); } + int GetCollisionAt(float x, float y) { return GetTile(round(x), round(y)); } + int GetWidth() { return m_Width; }; + int GetHeight() { return m_Height; }; + int IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision); + void MovePoint(vec2 *pInoutPos, vec2 *pInoutVel, float Elasticity, int *Bpounces); + void MoveBox(vec2 *pInoutPos, vec2 *pInoutVel, vec2 Size, float Elasticity); + bool TestBox(vec2 Pos, vec2 Size); +}; + +#endif diff --git a/src/game/collision.hpp b/src/game/collision.hpp deleted file mode 100644 index 0f072daa..00000000 --- a/src/game/collision.hpp +++ /dev/null @@ -1,21 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef GAME_MAPRES_COL_H -#define GAME_MAPRES_COL_H - -#include <base/vmath.hpp> - -enum -{ - COLFLAG_SOLID=1, - COLFLAG_DEATH=2, - COLFLAG_NOHOOK=4, -}; - -int col_init(); -int col_is_solid(int x, int y); -int col_get(int x, int y); -int col_width(); -int col_height(); -int col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out_collision, vec2 *out_before_collision); - -#endif diff --git a/src/game/editor/array.hpp b/src/game/editor/array.hpp deleted file mode 100755 index fe9f2739..00000000 --- a/src/game/editor/array.hpp +++ /dev/null @@ -1,238 +0,0 @@ - -template <class T> -class array -{ - // - // - void init() - { - list = 0; - clear(); - } - -public: - array() - { - init(); - } - - // - array(const array &other) - { - init(); - setsize(other.len()); - for(int i = 0; i < len(); i++) - (*this)[i] = other[i]; - } - - - // - // - virtual ~array() - { - delete [] list; - list = 0; - } - - // - // - void deleteall() - { - for(int i = 0; i < len(); i++) - delete list[i]; - - clear(); - } - - - // - // - void clear() - { - delete [] list; - - list_size = 1; - list = new T[1]; - num_elements = 0; - } - - int find(T val) - { - for(int i = 0; i < len(); i++) - if((*this)[i] == val) - return i; - return -1; - } - - bool exist(T val) - { - return find(val) != -1; - } - - // - // returns the number of elements in the list - // - int len() const - { - return num_elements; - } - - // - // This doesn't conserve the order in the list. Be careful - // - void removebyindexfast(int index) - { - //ASSUME(_Pos >= 0 && _Pos < num_elements); - list[index] = list[num_elements-1]; - setsize(len()-1); - } - - void removefast(const T& _Elem) - { - for(int i = 0; i < len(); i++) - if(list[i] == _Elem) - { - removebyindexfast(i); - return; - } - } - - // - // - void removebyindex(int index) - { - //ASSUME(_Pos >= 0 && _Pos < num_elements); - - for(int i = index+1; i < num_elements; i++) - list[i-1] = list[i]; - - setsize(len()-1); - } - - void insert(int index, const T& element) - { - int some_len = len(); - if (index < some_len) - setsize(some_len+1); - else - setsize(index + 1); - - for(int i = num_elements-2; i >= index; i--) - list[i+1] = list[i]; - - list[index] = element; - } - - bool remove(const T& element) - { - for(int i = 0; i < len(); i++) - if(list[i] == element) - { - removebyindex(i); - return true; - } - return false; - } - - // - // - int add(const T& element) - { - //if(num_elements == list_size) - setsize(len()+1); - list[num_elements-1] = element; - return num_elements-1; - } - - // - // - int add(const T& elem, int index) - { - setsize(len()+1); - - for(int i = num_elements-1; i > index; i--) - list[i] = list[i-1]; - - list[index] = elem; - - //num_elements++; - return num_elements-1; - } - - // - // - T& operator[] (int index) - { - return list[index]; - } - - const T& operator[] (int index) const - { - return list[index]; - } - - // - // - T *getptr() - { - return list; - } - - const T *getptr() const - { - return list; - } - - // - // - // - void setsize(int new_len) - { - if (list_size < new_len) - allocsize(new_len); - num_elements = new_len; - } - - // removes unnessasary data, returns how many bytes was earned - int optimize() - { - int Before = memoryusage(); - setsize(num_elements); - return Before - memoryusage(); - } - - // returns how much memory this dynamic array is using - int memoryusage() - { - return sizeof(array) + sizeof(T)*list_size; - } - - // - array &operator = (const array &other) - { - setsize(other.len()); - for(int i = 0; i < len(); i++) - (*this)[i] = other[i]; - return *this; - } -private: - void allocsize(int new_len) - { - list_size = new_len; - T *new_list = new T[list_size]; - - long end = num_elements < list_size ? num_elements : list_size; - for(int i = 0; i < end; i++) - new_list[i] = list[i]; - - delete [] list; - list = 0; - num_elements = num_elements < list_size ? num_elements : list_size; - list = new_list; - } - - T *list; - long list_size; - long num_elements; -}; - diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp index 17b891fc..480a6827 100644 --- a/src/game/editor/ed_editor.cpp +++ b/src/game/editor/ed_editor.cpp @@ -1,185 +1,180 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <engine/e_common_interface.h> -#include <engine/e_datafile.h> -#include <engine/e_config.h> -#include <engine/e_engine.h> -#include <engine/client/graphics.h> +#include <engine/shared/datafile.h> +#include <engine/shared/config.h> +#include <engine/shared/engine.h> +#include <engine/client.h> +#include <engine/graphics.h> +#include <engine/textrender.h> +#include <engine/input.h> +#include <engine/keys.h> +#include <engine/storage.h> -#include <game/client/ui.hpp> -#include <game/gamecore.hpp> -#include <game/client/render.hpp> +#include <game/client/ui.h> +#include <game/gamecore.h> +#include <game/client/render.h> -#include "ed_editor.hpp" -#include <game/client/lineinput.hpp> +#include "ed_editor.h" +#include <game/client/lineinput.h> -static int checker_texture = 0; -static int background_texture = 0; -static int cursor_texture = 0; -static int entities_texture = 0; +int CEditor::ms_CheckerTexture; +int CEditor::ms_BackgroundTexture; +int CEditor::ms_CursorTexture; +int CEditor::ms_EntitiesTexture; +const void* CEditor::ms_pUiGotContext; enum { BUTTON_CONTEXT=1, }; - - -EDITOR_IMAGE::~EDITOR_IMAGE() +CEditorImage::~CEditorImage() { - editor->Graphics()->UnloadTexture(tex_id); + m_pEditor->Graphics()->UnloadTexture(m_TexId); } -static const void *ui_got_context = 0; - -LAYERGROUP::LAYERGROUP() +CLayerGroup::CLayerGroup() { - name = ""; - visible = true; - game_group = false; - offset_x = 0; - offset_y = 0; - parallax_x = 100; - parallax_y = 100; - - use_clipping = 0; - clip_x = 0; - clip_y = 0; - clip_w = 0; - clip_h = 0; + m_pName = ""; + m_Visible = true; + m_GameGroup = false; + m_OffsetX = 0; + m_OffsetY = 0; + m_ParallaxX = 100; + m_ParallaxY = 100; + + m_UseClipping = 0; + m_ClipX = 0; + m_ClipY = 0; + m_ClipW = 0; + m_ClipH = 0; } -LAYERGROUP::~LAYERGROUP() +CLayerGroup::~CLayerGroup() { - clear(); + Clear(); } -void LAYERGROUP::convert(CUIRect *rect) +void CLayerGroup::Convert(CUIRect *pRect) { - rect->x += offset_x; - rect->y += offset_y; + pRect->x += m_OffsetX; + pRect->y += m_OffsetY; } -void LAYERGROUP::mapping(float *points) +void CLayerGroup::Mapping(float *pPoints) { - m_pMap->editor->RenderTools()->mapscreen_to_world( - m_pMap->editor->world_offset_x, m_pMap->editor->world_offset_y, - parallax_x/100.0f, parallax_y/100.0f, - offset_x, offset_y, - m_pMap->editor->Graphics()->ScreenAspect(), m_pMap->editor->world_zoom, points); - - points[0] += m_pMap->editor->editor_offset_x; - points[1] += m_pMap->editor->editor_offset_y; - points[2] += m_pMap->editor->editor_offset_x; - points[3] += m_pMap->editor->editor_offset_y; + m_pMap->m_pEditor->RenderTools()->MapscreenToWorld( + m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY, + m_ParallaxX/100.0f, m_ParallaxY/100.0f, + m_OffsetX, m_OffsetY, + m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints); + + pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX; + pPoints[1] += m_pMap->m_pEditor->m_EditorOffsetY; + pPoints[2] += m_pMap->m_pEditor->m_EditorOffsetX; + pPoints[3] += m_pMap->m_pEditor->m_EditorOffsetY; } -void LAYERGROUP::mapscreen() +void CLayerGroup::MapScreen() { - float points[4]; - mapping(points); - m_pMap->editor->Graphics()->MapScreen(points[0], points[1], points[2], points[3]); + float aPoints[4]; + Mapping(aPoints); + m_pMap->m_pEditor->Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]); } -void LAYERGROUP::render() +void CLayerGroup::Render() { - mapscreen(); - IGraphics *pGraphics = m_pMap->editor->Graphics(); - - if(use_clipping) - { - float points[4]; - m_pMap->game_group->mapping(points); - float x0 = (clip_x - points[0]) / (points[2]-points[0]); - float y0 = (clip_y - points[1]) / (points[3]-points[1]); - float x1 = ((clip_x+clip_w) - points[0]) / (points[2]-points[0]); - float y1 = ((clip_y+clip_h) - points[1]) / (points[3]-points[1]); - + MapScreen(); + IGraphics *pGraphics = m_pMap->m_pEditor->Graphics(); + + if(m_UseClipping) + { + float aPoints[4]; + m_pMap->m_pGameGroup->Mapping(aPoints); + float x0 = (m_ClipX - aPoints[0]) / (aPoints[2]-aPoints[0]); + float y0 = (m_ClipY - aPoints[1]) / (aPoints[3]-aPoints[1]); + float x1 = ((m_ClipX+m_ClipW) - aPoints[0]) / (aPoints[2]-aPoints[0]); + float y1 = ((m_ClipY+m_ClipH) - aPoints[1]) / (aPoints[3]-aPoints[1]); + pGraphics->ClipEnable((int)(x0*pGraphics->ScreenWidth()), (int)(y0*pGraphics->ScreenHeight()), (int)((x1-x0)*pGraphics->ScreenWidth()), (int)((y1-y0)*pGraphics->ScreenHeight())); } - - for(int i = 0; i < layers.len(); i++) + + for(int i = 0; i < m_lLayers.size(); i++) { - if(layers[i]->visible && layers[i] != m_pMap->game_layer) + if(m_lLayers[i]->m_Visible && m_lLayers[i] != m_pMap->m_pGameLayer) { - if(m_pMap->editor->show_detail || !(layers[i]->flags&LAYERFLAG_DETAIL)) - layers[i]->render(); + if(m_pMap->m_pEditor->m_ShowDetail || !(m_lLayers[i]->m_Flags&LAYERFLAG_DETAIL)) + m_lLayers[i]->Render(); } } - + pGraphics->ClipDisable(); } -bool LAYERGROUP::is_empty() const { return layers.len() == 0; } -void LAYERGROUP::clear() { layers.deleteall(); } -void LAYERGROUP::add_layer(LAYER *l) { layers.add(l); } - -void LAYERGROUP::delete_layer(int index) +void CLayerGroup::DeleteLayer(int Index) { - if(index < 0 || index >= layers.len()) return; - delete layers[index]; - layers.removebyindex(index); -} + if(Index < 0 || Index >= m_lLayers.size()) return; + delete m_lLayers[Index]; + m_lLayers.remove_index(Index); +} -void LAYERGROUP::get_size(float *w, float *h) +void CLayerGroup::GetSize(float *w, float *h) { *w = 0; *h = 0; - for(int i = 0; i < layers.len(); i++) + for(int i = 0; i < m_lLayers.size(); i++) { float lw, lh; - layers[i]->get_size(&lw, &lh); + m_lLayers[i]->GetSize(&lw, &lh); *w = max(*w, lw); *h = max(*h, lh); } } -int LAYERGROUP::swap_layers(int index0, int index1) +int CLayerGroup::SwapLayers(int Index0, int Index1) { - if(index0 < 0 || index0 >= layers.len()) return index0; - if(index1 < 0 || index1 >= layers.len()) return index0; - if(index0 == index1) return index0; - swap(layers[index0], layers[index1]); - return index1; + if(Index0 < 0 || Index0 >= m_lLayers.size()) return Index0; + if(Index1 < 0 || Index1 >= m_lLayers.size()) return Index0; + if(Index0 == Index1) return Index0; + swap(m_lLayers[Index0], m_lLayers[Index1]); + return Index1; } -void EDITOR_IMAGE::analyse_tileflags() +void CEditorImage::AnalyseTileFlags() { - mem_zero(tileflags, sizeof(tileflags)); - - int tw = width/16; // tilesizes - int th = height/16; - if ( tw == th ) { - unsigned char *pixeldata = (unsigned char *)data; - - int tile_id = 0; + mem_zero(m_aTileFlags, sizeof(m_aTileFlags)); + + int tw = m_Width/16; // tilesizes + int th = m_Height/16; + if ( tw == th ) + { + unsigned char *pPixelData = (unsigned char *)m_pData; + + int TileId = 0; for(int ty = 0; ty < 16; ty++) - for(int tx = 0; tx < 16; tx++, tile_id++) + for(int tx = 0; tx < 16; tx++, TileId++) { - bool opaque = true; + bool Opaque = true; for(int x = 0; x < tw; x++) for(int y = 0; y < th; y++) { - int p = (ty*tw+y)*width + tx*tw+x; - if(pixeldata[p*4+3] < 250) + int p = (ty*tw+y)*m_Width + tx*tw+x; + if(pPixelData[p*4+3] < 250) { - opaque = false; + Opaque = false; break; } } - - if(opaque) - tileflags[tile_id] |= TILEFLAG_OPAQUE; + + if(Opaque) + m_aTileFlags[TileId] |= TILEFLAG_OPAQUE; } } - + } /******************************************************** @@ -189,42 +184,42 @@ void EDITOR_IMAGE::analyse_tileflags() // copied from gc_menu.cpp, should be more generalized //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false); -int EDITOR::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) +int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden) { int Inside = UI()->MouseInside(pRect); int ReturnValue = 0; - static int AtIndex = 0; + static int s_AtIndex = 0; if(UI()->LastActiveItem() == pID) { - int Len = strlen(pStr); - + int Len = str_length(pStr); + if(Inside && UI()->MouseButton(0)) { - int mx_rel = (int)(UI()->MouseX() - pRect->x); + int MxRel = (int)(UI()->MouseX() - pRect->x); for (int i = 1; i <= Len; i++) { - if (gfx_text_width(0, FontSize, pStr, i) + 10 > mx_rel) + if (TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel) { - AtIndex = i - 1; + s_AtIndex = i - 1; break; } if (i == Len) - AtIndex = Len; + s_AtIndex = Len; } } - for(int i = 0; i < inp_num_events(); i++) + for(int i = 0; i < Input()->NumEvents(); i++) { - Len = strlen(pStr); - LINEINPUT::manipulate(inp_get_event(i), pStr, StrSize, &Len, &AtIndex); + Len = str_length(pStr); + CLineInput::Manipulate(Input()->GetEvent(i), pStr, StrSize, &Len, &s_AtIndex); } } bool JustGotActive = false; - + if(UI()->ActiveItem() == pID) { if(!UI()->MouseButton(0)) @@ -239,219 +234,138 @@ int EDITOR::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS UI()->SetActiveItem(pID); } } - + if(Inside) UI()->SetHotItem(pID); CUIRect Textbox = *pRect; - RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); - Textbox.VMargin(5.0f, &Textbox); - + RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 3.0f); + Textbox.VMargin(3.0f, &Textbox); + const char *pDisplayStr = pStr; char aStars[128]; - + if(Hidden) { - unsigned s = strlen(pStr); + unsigned s = str_length(pStr); if(s >= sizeof(aStars)) s = sizeof(aStars)-1; - memset(aStars, '*', s); + for(unsigned int i = 0; i < s; ++i) + aStars[i] = '*'; aStars[s] = 0; pDisplayStr = aStars; } UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1); - if (UI()->LastActiveItem() == pID && !JustGotActive) + //TODO: make it blink + if(UI()->LastActiveItem() == pID && !JustGotActive) { - float w = gfx_text_width(0, FontSize, pDisplayStr, AtIndex); + float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex); + Textbox = *pRect; + Textbox.VSplitLeft(2.0f, 0, &Textbox); Textbox.x += w*UI()->Scale(); - UI()->DoLabel(&Textbox, "_", FontSize, -1); - } - - return ReturnValue; -} - -/* -int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false) -{ - int inside = UI()->MouseInside(rect); - int r = 0; - static int at_index = 0; - - if(UI()->LastActiveItem() == id) - { - int len = strlen(str); - - if (inside && UI()->MouseButton(0)) - { - int mx_rel = (int)(UI()->MouseX() - rect->x); - - for (int i = 1; i <= len; i++) - { - if (gfx_text_width(0, font_size, str, i) + 10 > mx_rel) - { - at_index = i - 1; - break; - } - - if (i == len) - at_index = len; - } - } - - for(int i = 0; i < inp_num_events(); i++) - { - len = strlen(str); - LINEINPUT::manipulate(inp_get_event(i), str, str_size, &len, &at_index); - } + Textbox.y -= FontSize/10.f; - r = 1; - } - - bool just_got_active = false; - - if(UI()->ActiveItem() == id) - { - if(!UI()->MouseButton(0)) - UI()->SetActiveItem(0); - } - else if(UI()->HotItem() == id) - { - if(UI()->MouseButton(0)) - { - if (UI()->LastActiveItem() != id) - just_got_active = true; - UI()->SetActiveItem(id); - } - } - - if(inside) - UI()->SetHotItem(id); - - CUIRect textbox = *rect; - RenderTools()->DrawUIRect(&textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f); - textbox.VMargin(5.0f, &textbox); - - const char *display_str = str; - char stars[128]; - - if(hidden) - { - unsigned s = strlen(str); - if(s >= sizeof(stars)) - s = sizeof(stars)-1; - memset(stars, '*', s); - stars[s] = 0; - display_str = stars; - } - - UI()->DoLabel(&textbox, display_str, font_size, -1); - - if (UI()->LastActiveItem() == id && !just_got_active) - { - float w = gfx_text_width(0, font_size, display_str, at_index); - textbox.x += w*UI()->Scale(); - UI()->DoLabel(&textbox, "_", font_size, -1); + UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1); } - return r; + return ReturnValue; } -*/ -vec4 EDITOR::button_color_mul(const void *id) +vec4 CEditor::ButtonColorMul(const void *pId) { - if(UI()->ActiveItem() == id) + if(UI()->ActiveItem() == pId) return vec4(1,1,1,0.5f); - else if(UI()->HotItem() == id) + else if(UI()->HotItem() == pId) return vec4(1,1,1,1.5f); return vec4(1,1,1,1); } -float EDITOR::ui_do_scrollbar_v(const void *id, const CUIRect *rect, float current) +float CEditor::UiDoScrollbarV(const void *pId, const CUIRect *pRect, float Current) { - CUIRect handle; - static float offset_y; - rect->HSplitTop(33, &handle, 0); + CUIRect Handle; + static float s_OffsetY; + pRect->HSplitTop(33, &Handle, 0); - handle.y += (rect->h-handle.h)*current; + Handle.y += (pRect->h-Handle.h)*Current; - /* logic */ - float ret = current; - int inside = UI()->MouseInside(&handle); + // logic + float Ret = Current; + int Inside = UI()->MouseInside(&Handle); - if(UI()->ActiveItem() == id) + if(UI()->ActiveItem() == pId) { if(!UI()->MouseButton(0)) UI()->SetActiveItem(0); - - float min = rect->y; - float max = rect->h-handle.h; - float cur = UI()->MouseY()-offset_y; - ret = (cur-min)/max; - if(ret < 0.0f) ret = 0.0f; - if(ret > 1.0f) ret = 1.0f; + + float Min = pRect->y; + float Max = pRect->h-Handle.h; + float Cur = UI()->MouseY()-s_OffsetY; + Ret = (Cur-Min)/Max; + if(Ret < 0.0f) Ret = 0.0f; + if(Ret > 1.0f) Ret = 1.0f; } - else if(UI()->HotItem() == id) + else if(UI()->HotItem() == pId) { if(UI()->MouseButton(0)) { - UI()->SetActiveItem(id); - offset_y = UI()->MouseY()-handle.y; + UI()->SetActiveItem(pId); + s_OffsetY = UI()->MouseY()-Handle.y; } } - - if(inside) - UI()->SetHotItem(id); + + if(Inside) + UI()->SetHotItem(pId); // render - CUIRect rail; - rect->VMargin(5.0f, &rail); - RenderTools()->DrawUIRect(&rail, vec4(1,1,1,0.25f), 0, 0.0f); - - CUIRect slider = handle; - slider.w = rail.x-slider.x; - RenderTools()->DrawUIRect(&slider, vec4(1,1,1,0.25f), CUI::CORNER_L, 2.5f); - slider.x = rail.x+rail.w; - RenderTools()->DrawUIRect(&slider, vec4(1,1,1,0.25f), CUI::CORNER_R, 2.5f); - - slider = handle; - slider.Margin(5.0f, &slider); - RenderTools()->DrawUIRect(&slider, vec4(1,1,1,0.25f)*button_color_mul(id), CUI::CORNER_ALL, 2.5f); - - return ret; + CUIRect Rail; + pRect->VMargin(5.0f, &Rail); + RenderTools()->DrawUIRect(&Rail, vec4(1,1,1,0.25f), 0, 0.0f); + + CUIRect Slider = Handle; + Slider.w = Rail.x-Slider.x; + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_L, 2.5f); + Slider.x = Rail.x+Rail.w; + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f), CUI::CORNER_R, 2.5f); + + Slider = Handle; + Slider.Margin(5.0f, &Slider); + RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pId), CUI::CORNER_ALL, 2.5f); + + return Ret; } -vec4 EDITOR::get_button_color(const void *id, int checked) +vec4 CEditor::GetButtonColor(const void *pId, int Checked) { - if(checked < 0) + if(Checked < 0) return vec4(0,0,0,0.5f); - - if(checked > 0) + + if(Checked > 0) { - if(UI()->HotItem() == id) + if(UI()->HotItem() == pId) return vec4(1,0,0,0.75f); return vec4(1,0,0,0.5f); } - - if(UI()->HotItem() == id) + + if(UI()->HotItem() == pId) return vec4(1,1,1,0.75f); return vec4(1,1,1,0.5f); } -int EDITOR::DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { if(UI()->MouseInside(pRect)) { if(Flags&BUTTON_CONTEXT) - ui_got_context = pID; - if(tooltip) - tooltip = pToolTip; + ms_pUiGotContext = pID; + if(m_pTooltip) + m_pTooltip = pToolTip; } - + if(UI()->HotItem() == pID && pToolTip) - tooltip = (const char *)pToolTip; - + m_pTooltip = (const char *)pToolTip; + return UI()->DoButtonLogic(pID, pText, Checked, pRect); // Draw here @@ -459,444 +373,399 @@ int EDITOR::DoButton_Editor_Common(const void *pID, const char *pText, int Check } -int EDITOR::DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_ALL, 3.0f); - UI()->DoLabel(pRect, pText, 10, 0, -1); + RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f); + CUIRect NewRect = *pRect; + NewRect.y += NewRect.h/2.0f-7.0f; + UI()->DoLabel(&NewRect, pText, 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -int EDITOR::DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { if(UI()->HotItem() == pID) - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_ALL, 3.0f); - + RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f); + CUIRect t = *pRect; t.VMargin(5.0f, &t); UI()->DoLabel(&t, pText, 10, -1, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -//static void draw_editor_button_menu(const void *id, const char *text, int checked, const CUIRect *rect, const void *extra) -int EDITOR::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - /* - if(UI()->HotItem() == id) if(extra) editor.tooltip = (const char *)extra; - if(UI()->HotItem() == id) - RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_ALL, 3.0f); - */ - CUIRect r = *pRect; - /* - if(ui_popups[id == id) - { - RenderTools()->DrawUIRect(&r, vec4(0.5f,0.5f,0.5f,0.75f), CUI::CORNER_T, 3.0f); - r.Margin(1.0f, &r); - RenderTools()->DrawUIRect(&r, vec4(0,0,0,0.75f), CUI::CORNER_T, 3.0f); - } - else*/ - RenderTools()->DrawUIRect(&r, vec4(0.5f,0.5f,0.5f, 1.0f), CUI::CORNER_T, 3.0f); - + RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f); r = *pRect; r.VMargin(5.0f, &r); UI()->DoLabel(&r, pText, 10, -1, -1); - return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); - - //CUIRect t = *r; } -int EDITOR::DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { if(UI()->HotItem() == pID || Checked) - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_ALL, 3.0f); - + RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_ALL, 3.0f); + CUIRect t = *pRect; t.VMargin(5.0f, &t); UI()->DoLabel(&t, pText, 10, -1, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, 0, 0); } -int EDITOR::DoButton_ButtonL(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) -{ - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_L, 3.0f); - UI()->DoLabel(pRect, pText, 10, 0, -1); - return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); -} - -int EDITOR::DoButton_ButtonM(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), 0, 3.0f); - UI()->DoLabel(pRect, pText, 10, 0, -1); + RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_T, 5.0f); + CUIRect NewRect = *pRect; + NewRect.y += NewRect.h/2.0f-7.0f; + UI()->DoLabel(&NewRect, pText, 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -int EDITOR::DoButton_ButtonR(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners) { - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_R, 3.0f); - UI()->DoLabel(pRect, pText, 10, 0, -1); + RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f); + CUIRect NewRect = *pRect; + NewRect.y += NewRect.h/2.0f-7.0f; + UI()->DoLabel(&NewRect, pText, 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -int EDITOR::DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_R, 3.0f); - UI()->DoLabel(pRect, pText?pText:">", 10, 0, -1); + RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_R, 3.0f); + UI()->DoLabel(pRect, pText?pText:"+", 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -int EDITOR::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) +int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip) { - RenderTools()->DrawUIRect(pRect, get_button_color(pID, Checked), CUI::CORNER_L, 3.0f); - UI()->DoLabel(pRect, pText?pText:"<", 10, 0, -1); + RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_L, 3.0f); + UI()->DoLabel(pRect, pText?pText:"-", 10, 0, -1); return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } -/* -static void draw_editor_button_l(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) -{ - RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_L, 3.0f); - UI()->DoLabel(r, text, 10, 0, -1); -} - -static void draw_editor_button_m(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) -{ - if(UI()->HotItem() == id) if(extra) editor.tooltip = (const char *)extra; - RenderTools()->DrawUIRect(r, get_button_color(id, checked), 0, 3.0f); - UI()->DoLabel(r, text, 10, 0, -1); -} - -static void draw_editor_button_r(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) -{ - if(UI()->HotItem() == id) if(extra) editor.tooltip = (const char *)extra; - RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_R, 3.0f); - UI()->DoLabel(r, text, 10, 0, -1); -} - -static void draw_inc_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) +void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness) { - if(UI()->HotItem == id) if(extra) editor.tooltip = (const char *)extra; - RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_R, 3.0f); - UI()->DoLabel(r, text?text:">", 10, 0, -1); -} - -static void draw_dec_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra) -{ - if(UI()->HotItem == id) if(extra) editor.tooltip = (const char *)extra; - RenderTools()->DrawUIRect(r, get_button_color(id, checked), CUI::CORNER_L, 3.0f); - UI()->DoLabel(r, text?text:"<", 10, 0, -1); -} - -int do_editor_button(const void *id, const char *text, int checked, const CUIRect *r, ui_draw_button_func draw_func, int flags, const char *tooltip) -{ - if(UI()->MouseInside(r)) - { - if(flags&BUTTON_CONTEXT) - ui_got_context = id; - if(tooltip) - editor.tooltip = tooltip; - } - - return UI()->DoButton(id, text, checked, r, draw_func, 0); -}*/ - - -void EDITOR::render_background(CUIRect view, int texture, float size, float brightness) -{ - Graphics()->TextureSet(texture); + Graphics()->TextureSet(Texture); Graphics()->BlendNormal(); Graphics()->QuadsBegin(); - Graphics()->SetColor(brightness,brightness,brightness,1.0f); - Graphics()->QuadsSetSubset(0,0, view.w/size, view.h/size); - Graphics()->QuadsDrawTL(view.x, view.y, view.w, view.h); + Graphics()->SetColor(Brightness, Brightness, Brightness, 1.0f); + Graphics()->QuadsSetSubset(0,0, View.w/Size, View.h/Size); + IGraphics::CQuadItem QuadItem(View.x, View.y, View.w, View.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } -static LAYERGROUP brush; -static LAYER_TILES tileset_picker(16, 16); - -int EDITOR::ui_do_value_selector(void *id, CUIRect *r, const char *label, int current, int min, int max, float scale) +int CEditor::UiDoValueSelector(void *pId, CUIRect *r, const char *pLabel, int Current, int Min, int Max, float Scale) { - /* logic */ - static float value; - int ret = 0; - int inside = UI()->MouseInside(r); + // logic + static float s_Value; + int Ret = 0; + int Inside = UI()->MouseInside(r); - if(UI()->ActiveItem() == id) + if(UI()->ActiveItem() == pId) { if(!UI()->MouseButton(0)) { - if(inside) - ret = 1; - lock_mouse = false; + if(Inside) + Ret = 1; + m_LockMouse = false; UI()->SetActiveItem(0); } else { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - value += mouse_delta_x*0.05f; + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) + s_Value += m_MouseDeltaX*0.05f; else - value += mouse_delta_x; - - if(fabs(value) > scale) + s_Value += m_MouseDeltaX; + + if(absolute(s_Value) > Scale) { - int count = (int)(value/scale); - value = fmod(value, scale); - current += count; - if(current < min) - current = min; - if(current > max) - current = max; + int Count = (int)(s_Value/Scale); + s_Value = fmod(s_Value, Scale); + Current += Count; + if(Current < Min) + Current = Min; + if(Current > Max) + Current = Max; } } } - else if(UI()->HotItem() == id) + else if(UI()->HotItem() == pId) { if(UI()->MouseButton(0)) { - lock_mouse = true; - value = 0; - UI()->SetActiveItem(id); + m_LockMouse = true; + s_Value = 0; + UI()->SetActiveItem(pId); } } - - if(inside) - UI()->SetHotItem(id); + + if(Inside) + UI()->SetHotItem(pId); // render - char buf[128]; - sprintf(buf, "%s %d", label, current); - RenderTools()->DrawUIRect(r, get_button_color(id, 0), CUI::CORNER_ALL, 5.0f); - UI()->DoLabel(r, buf, 10, 0, -1); - return current; + char aBuf[128]; + str_format(aBuf, sizeof(aBuf),"%s %d", pLabel, Current); + RenderTools()->DrawUIRect(r, GetButtonColor(pId, 0), CUI::CORNER_ALL, 5.0f); + r->y += r->h/2.0f-7.0f; + UI()->DoLabel(r, aBuf, 10, 0, -1); + + return Current; } -LAYERGROUP *EDITOR::get_selected_group() +CLayerGroup *CEditor::GetSelectedGroup() { - if(selected_group >= 0 && selected_group < map.groups.len()) - return map.groups[selected_group]; + if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size()) + return m_Map.m_lGroups[m_SelectedGroup]; return 0x0; } -LAYER *EDITOR::get_selected_layer(int index) +CLayer *CEditor::GetSelectedLayer(int Index) { - LAYERGROUP *group = get_selected_group(); - if(!group) + CLayerGroup *pGroup = GetSelectedGroup(); + if(!pGroup) return 0x0; - if(selected_layer >= 0 && selected_layer < map.groups[selected_group]->layers.len()) - return group->layers[selected_layer]; + if(m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size()) + return pGroup->m_lLayers[m_SelectedLayer]; return 0x0; } -LAYER *EDITOR::get_selected_layer_type(int index, int type) +CLayer *CEditor::GetSelectedLayerType(int Index, int Type) { - LAYER *p = get_selected_layer(index); - if(p && p->type == type) + CLayer *p = GetSelectedLayer(Index); + if(p && p->m_Type == Type) return p; return 0x0; } -QUAD *EDITOR::get_selected_quad() +CQuad *CEditor::GetSelectedQuad() { - LAYER_QUADS *ql = (LAYER_QUADS *)get_selected_layer_type(0, LAYERTYPE_QUADS); + CLayerQuads *ql = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS); if(!ql) return 0; - if(selected_quad >= 0 && selected_quad < ql->quads.len()) - return &ql->quads[selected_quad]; + if(m_SelectedQuad >= 0 && m_SelectedQuad < ql->m_lQuads.size()) + return &ql->m_lQuads[m_SelectedQuad]; return 0; } -static void callback_open_map(const char *filename, void *user) { ((EDITOR*)user)->load(filename); } -static void callback_append_map(const char *filename, void *user) { ((EDITOR*)user)->append(filename); } -static void callback_save_map(const char *filename, void *user) { ((EDITOR*)user)->save(filename); } +static void CallbackOpenMap(const char *pFileName, void *pUser) { if(((CEditor*)pUser)->Load(pFileName)) str_copy(((CEditor*)pUser)->m_aFileName, pFileName, 512); } +static void CallbackAppendMap(const char *pFileName, void *pUser) { if(((CEditor*)pUser)->Append(pFileName)) ((CEditor*)pUser)->m_aFileName[0] = 0; } +static void CallbackSaveMap(const char *pFileName, void *pUser){ if(((CEditor*)pUser)->Save(pFileName)) str_copy(((CEditor*)pUser)->m_aFileName, pFileName, 512); } -void EDITOR::do_toolbar(CUIRect toolbar) +void CEditor::DoToolbar(CUIRect ToolBar) { - CUIRect button; + CUIRect TB_Top, TB_Bottom; + CUIRect Button; - // ctrl+o to open - if(inp_key_down('o') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))) - invoke_file_dialog(LISTDIRTYPE_ALL, "Open Map", "Open", "maps/", "", callback_open_map, this); + ToolBar.HSplitTop(ToolBar.h/2.0f, &TB_Top, &TB_Bottom); + TB_Top.HSplitBottom(2.5f, &TB_Top, 0); + TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom); + + // ctrl+o to open + if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) + InvokeFileDialog(IStorage::TYPE_ALL, "Open Map", "Open", "maps/", "", CallbackOpenMap, this); + // ctrl+s to save - if(inp_key_down('s') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL))) - invoke_file_dialog(LISTDIRTYPE_SAVE, "Save Map", "Save", "maps/", "", callback_save_map, this); + if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))) + { + if(m_aFileName[0]) + Save(m_aFileName); + else + InvokeFileDialog(IStorage::TYPE_SAVE, "Save Map", "Save", "maps/", "", CallbackSaveMap, this); + } // detail button - toolbar.VSplitLeft(30.0f, &button, &toolbar); - static int hq_button = 0; - if(DoButton_Editor(&hq_button, "Detail", show_detail, &button, 0, "[ctrl+h] Toggle High Detail") || - (inp_key_down('h') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_HqButton = 0; + if(DoButton_Editor(&s_HqButton, "HD", m_ShowDetail, &Button, 0, "[ctrl+h] Toggle High Detail") || + (Input()->KeyDown('h') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))) { - show_detail = !show_detail; + m_ShowDetail = !m_ShowDetail; } - toolbar.VSplitLeft(5.0f, 0, &toolbar); - + TB_Top.VSplitLeft(5.0f, 0, &TB_Top); + // animation button - toolbar.VSplitLeft(30.0f, &button, &toolbar); - static int animate_button = 0; - if(DoButton_Editor(&animate_button, "Anim", animate, &button, 0, "[ctrl+m] Toggle animation") || - (inp_key_down('m') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) + TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); + static int s_AnimateButton = 0; + if(DoButton_Editor(&s_AnimateButton, "Anim", m_Animate, &Button, 0, "[ctrl+m] Toggle animation") || + (Input()->KeyDown('m') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))) { - animate_start = time_get(); - animate = !animate; + m_AnimateStart = time_get(); + m_Animate = !m_Animate; } - toolbar.VSplitLeft(5.0f, 0, &toolbar); + TB_Top.VSplitLeft(5.0f, 0, &TB_Top); // proof button - toolbar.VSplitLeft(30.0f, &button, &toolbar); - static int proof_button = 0; - if(DoButton_Editor(&proof_button, "Proof", proof_borders, &button, 0, "[ctrl-p] Toggles proof borders. These borders represent what a player maximum can see.") || - (inp_key_down('p') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) + TB_Top.VSplitLeft(40.0f, &Button, &TB_Top); + static int s_ProofButton = 0; + if(DoButton_Editor(&s_ProofButton, "Proof", m_ProofBorders, &Button, 0, "[ctrl-p] Toggles proof borders. These borders represent what a player maximum can see.") || + (Input()->KeyDown('p') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))) { - proof_borders = !proof_borders; + m_ProofBorders = !m_ProofBorders; } - toolbar.VSplitLeft(15.0f, 0, &toolbar); - + TB_Top.VSplitLeft(15.0f, 0, &TB_Top); + // zoom group - toolbar.VSplitLeft(16.0f, &button, &toolbar); - static int zoom_out_button = 0; - if(DoButton_ButtonL(&zoom_out_button, "ZO", 0, &button, 0, "[NumPad-] Zoom out") || inp_key_down(KEY_KP_MINUS)) - zoom_level += 50; - - toolbar.VSplitLeft(16.0f, &button, &toolbar); - static int zoom_normal_button = 0; - if(DoButton_ButtonM(&zoom_normal_button, "1:1", 0, &button, 0, "[NumPad*] Zoom to normal and remove editor offset") || inp_key_down(KEY_KP_MULTIPLY)) + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_ZoomOutButton = 0; + if(DoButton_Ex(&s_ZoomOutButton, "ZO", 0, &Button, 0, "[NumPad-] Zoom out", CUI::CORNER_L) || Input()->KeyDown(KEY_KP_MINUS)) + m_ZoomLevel += 50; + + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_ZoomNormalButton = 0; + if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0) || Input()->KeyDown(KEY_KP_MULTIPLY)) { - editor_offset_x = 0; - editor_offset_y = 0; - zoom_level = 100; + m_EditorOffsetX = 0; + m_EditorOffsetY = 0; + m_ZoomLevel = 100; } - - toolbar.VSplitLeft(16.0f, &button, &toolbar); - static int zoom_in_button = 0; - if(DoButton_ButtonR(&zoom_in_button, "ZI", 0, &button, 0, "[NumPad+] Zoom in") || inp_key_down(KEY_KP_PLUS)) - zoom_level -= 50; - - toolbar.VSplitLeft(15.0f, 0, &toolbar); - + + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_ZoomInButton = 0; + if(DoButton_Ex(&s_ZoomInButton, "ZI", 0, &Button, 0, "[NumPad+] Zoom in", CUI::CORNER_R) || Input()->KeyDown(KEY_KP_PLUS)) + m_ZoomLevel -= 50; + + TB_Top.VSplitLeft(10.0f, 0, &TB_Top); + // animation speed - toolbar.VSplitLeft(16.0f, &button, &toolbar); - static int anim_faster_button = 0; - if(DoButton_ButtonL(&anim_faster_button, "A+", 0, &button, 0, "Increase animation speed")) - animate_speed += 0.5f; - - toolbar.VSplitLeft(16.0f, &button, &toolbar); - static int anim_normal_button = 0; - if(DoButton_ButtonM(&anim_normal_button, "1", 0, &button, 0, "Normal animation speed")) - animate_speed = 1.0f; - - toolbar.VSplitLeft(16.0f, &button, &toolbar); - static int anim_slower_button = 0; - if(DoButton_ButtonR(&anim_slower_button, "A-", 0, &button, 0, "Decrease animation speed")) + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_AnimFasterButton = 0; + if(DoButton_Ex(&s_AnimFasterButton, "A+", 0, &Button, 0, "Increase animation speed", CUI::CORNER_L)) + m_AnimateSpeed += 0.5f; + + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_AnimNormalButton = 0; + if(DoButton_Ex(&s_AnimNormalButton, "1", 0, &Button, 0, "Normal animation speed", 0)) + m_AnimateSpeed = 1.0f; + + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_AnimSlowerButton = 0; + if(DoButton_Ex(&s_AnimSlowerButton, "A-", 0, &Button, 0, "Decrease animation speed", CUI::CORNER_R)) { - if(animate_speed > 0.5f) - animate_speed -= 0.5f; + if(m_AnimateSpeed > 0.5f) + m_AnimateSpeed -= 0.5f; } - - if(inp_key_presses(KEY_MOUSE_WHEEL_UP) && dialog == DIALOG_NONE) - zoom_level -= 20; - - if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN) && dialog == DIALOG_NONE) - zoom_level += 20; - - if(zoom_level < 50) - zoom_level = 50; - world_zoom = zoom_level/100.0f; - toolbar.VSplitLeft(10.0f, &button, &toolbar); + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && m_Dialog == DIALOG_NONE) + m_ZoomLevel -= 20; + + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && m_Dialog == DIALOG_NONE) + m_ZoomLevel += 20; + + if(m_ZoomLevel < 50) + m_ZoomLevel = 50; + m_WorldZoom = m_ZoomLevel/100.0f; + + TB_Top.VSplitLeft(10.0f, &Button, &TB_Top); // brush manipulation - { - int enabled = brush.is_empty()?-1:0; - + { + int Enabled = m_Brush.IsEmpty()?-1:0; + // flip buttons - toolbar.VSplitLeft(20.0f, &button, &toolbar); - static int flipx_button = 0; - if(DoButton_ButtonL(&flipx_button, "^X", enabled, &button, 0, "[N] Flip brush horizontal") || inp_key_down('n')) + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_FlipXButton = 0; + if(DoButton_Ex(&s_FlipXButton, "X/X", Enabled, &Button, 0, "[N] Flip brush horizontal", CUI::CORNER_L) || Input()->KeyDown('n')) { - for(int i = 0; i < brush.layers.len(); i++) - brush.layers[i]->brush_flip_x(); + for(int i = 0; i < m_Brush.m_lLayers.size(); i++) + m_Brush.m_lLayers[i]->BrushFlipX(); } - - toolbar.VSplitLeft(20.0f, &button, &toolbar); - static int flipy_button = 0; - if(DoButton_ButtonR(&flipy_button, "^Y", enabled, &button, 0, "[M] Flip brush vertical") || inp_key_down('m')) + + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_FlipyButton = 0; + if(DoButton_Ex(&s_FlipyButton, "Y/Y", Enabled, &Button, 0, "[M] Flip brush vertical", CUI::CORNER_R) || Input()->KeyDown('m')) { - for(int i = 0; i < brush.layers.len(); i++) - brush.layers[i]->brush_flip_y(); + for(int i = 0; i < m_Brush.m_lLayers.size(); i++) + m_Brush.m_lLayers[i]->BrushFlipY(); } // rotate buttons - toolbar.VSplitLeft(20.0f, &button, &toolbar); - - toolbar.VSplitLeft(30.0f, &button, &toolbar); - static int rotation_amount = 90; - rotation_amount = ui_do_value_selector(&rotation_amount, &button, "", rotation_amount, 1, 360, 2.0f); - - toolbar.VSplitLeft(5.0f, &button, &toolbar); - toolbar.VSplitLeft(30.0f, &button, &toolbar); - static int ccw_button = 0; - if(DoButton_ButtonL(&ccw_button, "CCW", enabled, &button, 0, "[R] Rotates the brush counter clockwise") || inp_key_down('r')) + TB_Top.VSplitLeft(15.0f, &Button, &TB_Top); + + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_RotationAmount = 90; + s_RotationAmount = UiDoValueSelector(&s_RotationAmount, &Button, "", s_RotationAmount, 1, 360, 2.0f); + + TB_Top.VSplitLeft(5.0f, &Button, &TB_Top); + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_CcwButton = 0; + if(DoButton_Ex(&s_CcwButton, "CCW", Enabled, &Button, 0, "[R] Rotates the brush counter clockwise", CUI::CORNER_L) || Input()->KeyDown('r')) { - for(int i = 0; i < brush.layers.len(); i++) - brush.layers[i]->brush_rotate(-rotation_amount/360.0f*pi*2); + for(int i = 0; i < m_Brush.m_lLayers.size(); i++) + m_Brush.m_lLayers[i]->BrushRotate(-s_RotationAmount/360.0f*pi*2); } - - toolbar.VSplitLeft(30.0f, &button, &toolbar); - static int cw_button = 0; - if(DoButton_ButtonR(&cw_button, "CW", enabled, &button, 0, "[T] Rotates the brush clockwise") || inp_key_down('t')) + + TB_Top.VSplitLeft(30.0f, &Button, &TB_Top); + static int s_CwButton = 0; + if(DoButton_Ex(&s_CwButton, "CW", Enabled, &Button, 0, "[T] Rotates the brush clockwise", CUI::CORNER_R) || Input()->KeyDown('t')) { - for(int i = 0; i < brush.layers.len(); i++) - brush.layers[i]->brush_rotate(rotation_amount/360.0f*pi*2); + for(int i = 0; i < m_Brush.m_lLayers.size(); i++) + m_Brush.m_lLayers[i]->BrushRotate(s_RotationAmount/360.0f*pi*2); } } // quad manipulation { // do add button - toolbar.VSplitLeft(10.0f, &button, &toolbar); - toolbar.VSplitLeft(60.0f, &button, &toolbar); - static int new_button = 0; - - LAYER_QUADS *qlayer = (LAYER_QUADS *)get_selected_layer_type(0, LAYERTYPE_QUADS); - //LAYER_TILES *tlayer = (LAYER_TILES *)get_selected_layer_type(0, LAYERTYPE_TILES); - if(DoButton_Editor(&new_button, "Add Quad", qlayer?0:-1, &button, 0, "Adds a new quad")) + TB_Top.VSplitLeft(10.0f, &Button, &TB_Top); + TB_Top.VSplitLeft(60.0f, &Button, &TB_Top); + static int s_NewButton = 0; + + CLayerQuads *pQLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS); + //CLayerTiles *tlayer = (CLayerTiles *)get_selected_layer_type(0, LAYERTYPE_TILES); + if(DoButton_Editor(&s_NewButton, "Add Quad", pQLayer?0:-1, &Button, 0, "Adds a new quad")) { - if(qlayer) + if(pQLayer) { - float mapping[4]; - LAYERGROUP *g = get_selected_group(); - g->mapping(mapping); - int add_x = f2fx(mapping[0] + (mapping[2]-mapping[0])/2); - int add_y = f2fx(mapping[1] + (mapping[3]-mapping[1])/2); - - QUAD *q = qlayer->new_quad(); + float Mapping[4]; + CLayerGroup *g = GetSelectedGroup(); + g->Mapping(Mapping); + int AddX = f2fx(Mapping[0] + (Mapping[2]-Mapping[0])/2); + int AddY = f2fx(Mapping[1] + (Mapping[3]-Mapping[1])/2); + + CQuad *q = pQLayer->NewQuad(); for(int i = 0; i < 5; i++) { - q->points[i].x += add_x; - q->points[i].y += add_y; + q->m_aPoints[i].x += AddX; + q->m_aPoints[i].y += AddY; } } } } + + // tile manipulation + { + TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom); + static int s_BorderBut = 0; + CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); + + if(DoButton_Editor(&s_BorderBut, "Border", pT?0:-1, &Button, 0, "Border")) + { + if(pT) + DoMapBorder(); + } + } } -static void rotate(POINT *center, POINT *point, float rotation) +static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) { - int x = point->x - center->x; - int y = point->y - center->y; - point->x = (int)(x * cosf(rotation) - y * sinf(rotation) + center->x); - point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y); + int x = pPoint->x - pCenter->x; + int y = pPoint->y - pCenter->y; + pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x); + pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y); } -void EDITOR::do_quad(QUAD *q, int index) +void CEditor::DoQuad(CQuad *q, int Index) { enum { @@ -906,71 +775,72 @@ void EDITOR::do_quad(QUAD *q, int index) OP_ROTATE, OP_CONTEXT_MENU, }; - + // some basic values - void *id = &q->points[4]; // use pivot addr as id - static POINT rotate_points[4]; - static float last_wx; - static float last_wy; - static int operation = OP_NONE; - static float rotate_angle = 0; + void *pId = &q->m_aPoints[4]; // use pivot addr as id + static CPoint s_RotatePoints[4]; + static float s_LastWx; + static float s_LastWy; + static int s_Operation = OP_NONE; + static float s_RotateAngle = 0; float wx = UI()->MouseWorldX(); float wy = UI()->MouseWorldY(); - + // get pivot - float center_x = fx2f(q->points[4].x); - float center_y = fx2f(q->points[4].y); + float CenterX = fx2f(q->m_aPoints[4].x); + float CenterY = fx2f(q->m_aPoints[4].y); - float dx = (center_x - wx); - float dy = (center_y - wy); + float dx = (CenterX - wx); + float dy = (CenterY - wy); if(dx*dx+dy*dy < 10*10) - UI()->SetHotItem(id); + UI()->SetHotItem(pId); - // draw selection background - if(selected_quad == index) + // draw selection background + if(m_SelectedQuad == Index) { Graphics()->SetColor(0,0,0,1); - Graphics()->QuadsDraw(center_x, center_y, 7.0f, 7.0f); + IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f, 7.0f); + Graphics()->QuadsDraw(&QuadItem, 1); } - - if(UI()->ActiveItem() == id) + + if(UI()->ActiveItem() == pId) { // check if we only should move pivot - if(operation == OP_MOVE_PIVOT) + if(s_Operation == OP_MOVE_PIVOT) { - q->points[4].x += f2fx(wx-last_wx); - q->points[4].y += f2fx(wy-last_wy); + q->m_aPoints[4].x += f2fx(wx-s_LastWx); + q->m_aPoints[4].y += f2fx(wy-s_LastWy); } - else if(operation == OP_MOVE_ALL) + else if(s_Operation == OP_MOVE_ALL) { // move all points including pivot for(int v = 0; v < 5; v++) { - q->points[v].x += f2fx(wx-last_wx); - q->points[v].y += f2fx(wy-last_wy); + q->m_aPoints[v].x += f2fx(wx-s_LastWx); + q->m_aPoints[v].y += f2fx(wy-s_LastWy); } } - else if(operation == OP_ROTATE) + else if(s_Operation == OP_ROTATE) { for(int v = 0; v < 4; v++) { - q->points[v] = rotate_points[v]; - rotate(&q->points[4], &q->points[v], rotate_angle); + q->m_aPoints[v] = s_RotatePoints[v]; + Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle); } } - - rotate_angle += (mouse_delta_x) * 0.002f; - last_wx = wx; - last_wy = wy; - - if(operation == OP_CONTEXT_MENU) + + s_RotateAngle += (m_MouseDeltaX) * 0.002f; + s_LastWx = wx; + s_LastWy = wy; + + if(s_Operation == OP_CONTEXT_MENU) { if(!UI()->MouseButton(1)) { - static int quad_popup_id = 0; - ui_invoke_popup_menu(&quad_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, popup_quad); - lock_mouse = false; - operation = OP_NONE; + static int s_QuadPopupId = 0; + UiInvokePopupMenu(&s_QuadPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupQuad); + m_LockMouse = false; + s_Operation = OP_NONE; UI()->SetActiveItem(0); } } @@ -978,79 +848,81 @@ void EDITOR::do_quad(QUAD *q, int index) { if(!UI()->MouseButton(0)) { - lock_mouse = false; - operation = OP_NONE; + m_LockMouse = false; + s_Operation = OP_NONE; UI()->SetActiveItem(0); } - } + } Graphics()->SetColor(1,1,1,1); } - else if(UI()->HotItem() == id) + else if(UI()->HotItem() == pId) { - ui_got_context = id; - + ms_pUiGotContext = pId; + Graphics()->SetColor(1,1,1,1); - tooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate"; - + m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate"; + if(UI()->MouseButton(0)) { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - operation = OP_MOVE_PIVOT; - else if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)) + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) + s_Operation = OP_MOVE_PIVOT; + else if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) { - lock_mouse = true; - operation = OP_ROTATE; - rotate_angle = 0; - rotate_points[0] = q->points[0]; - rotate_points[1] = q->points[1]; - rotate_points[2] = q->points[2]; - rotate_points[3] = q->points[3]; + m_LockMouse = true; + s_Operation = OP_ROTATE; + s_RotateAngle = 0; + s_RotatePoints[0] = q->m_aPoints[0]; + s_RotatePoints[1] = q->m_aPoints[1]; + s_RotatePoints[2] = q->m_aPoints[2]; + s_RotatePoints[3] = q->m_aPoints[3]; } else - operation = OP_MOVE_ALL; - - UI()->SetActiveItem(id); - selected_quad = index; - last_wx = wx; - last_wy = wy; + s_Operation = OP_MOVE_ALL; + + UI()->SetActiveItem(pId); + m_SelectedQuad = Index; + s_LastWx = wx; + s_LastWy = wy; } - + if(UI()->MouseButton(1)) { - selected_quad = index; - operation = OP_CONTEXT_MENU; - UI()->SetActiveItem(id); + m_SelectedQuad = Index; + s_Operation = OP_CONTEXT_MENU; + UI()->SetActiveItem(pId); } } else Graphics()->SetColor(0,1,0,1); - Graphics()->QuadsDraw(center_x, center_y, 5.0f, 5.0f); + IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f, 5.0f); + Graphics()->QuadsDraw(&QuadItem, 1); } -void EDITOR::do_quad_point(QUAD *q, int quad_index, int v) +void CEditor::DoQuadPoint(CQuad *q, int QuadIndex, int v) { - void *id = &q->points[v]; + void *pId = &q->m_aPoints[v]; float wx = UI()->MouseWorldX(); float wy = UI()->MouseWorldY(); - - float px = fx2f(q->points[v].x); - float py = fx2f(q->points[v].y); - + + float px = fx2f(q->m_aPoints[v].x); + float py = fx2f(q->m_aPoints[v].y); + float dx = (px - wx); float dy = (py - wy); if(dx*dx+dy*dy < 10*10) - UI()->SetHotItem(id); + UI()->SetHotItem(pId); - // draw selection background - if(selected_quad == quad_index && selected_points&(1<<v)) + // draw selection background + if(m_SelectedQuad == QuadIndex && m_SelectedPoints&(1<<v)) { Graphics()->SetColor(0,0,0,1); - Graphics()->QuadsDraw(px, py, 7.0f, 7.0f); + IGraphics::CQuadItem QuadItem(px, py, 7.0f, 7.0f); + Graphics()->QuadsDraw(&QuadItem, 1); } - + enum { OP_NONE=0, @@ -1058,48 +930,48 @@ void EDITOR::do_quad_point(QUAD *q, int quad_index, int v) OP_MOVEUV, OP_CONTEXT_MENU }; - - static bool moved; - static int operation = OP_NONE; - if(UI()->ActiveItem() == id) + static bool s_Moved; + static int s_Operation = OP_NONE; + + if(UI()->ActiveItem() == pId) { - float dx = mouse_delta_wx; - float dy = mouse_delta_wy; - if(!moved) + float dx = m_MouseDeltaWx; + float dy = m_MouseDeltaWy; + if(!s_Moved) { if(dx*dx+dy*dy > 0.5f) - moved = true; + s_Moved = true; } - - if(moved) + + if(s_Moved) { - if(operation == OP_MOVEPOINT) + if(s_Operation == OP_MOVEPOINT) { for(int m = 0; m < 4; m++) - if(selected_points&(1<<m)) + if(m_SelectedPoints&(1<<m)) { - q->points[m].x += f2fx(dx); - q->points[m].y += f2fx(dy); + q->m_aPoints[m].x += f2fx(dx); + q->m_aPoints[m].y += f2fx(dy); } } - else if(operation == OP_MOVEUV) + else if(s_Operation == OP_MOVEUV) { for(int m = 0; m < 4; m++) - if(selected_points&(1<<m)) + if(m_SelectedPoints&(1<<m)) { - q->texcoords[m].x += f2fx(dx*0.001f); - q->texcoords[m].y += f2fx(dy*0.001f); + q->m_aTexcoords[m].x += f2fx(dx*0.001f); + q->m_aTexcoords[m].y += f2fx(dy*0.001f); } } } - - if(operation == OP_CONTEXT_MENU) + + if(s_Operation == OP_CONTEXT_MENU) { if(!UI()->MouseButton(1)) { - static int point_popup_id = 0; - ui_invoke_popup_menu(&point_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, popup_point); + static int s_PointPopupId = 0; + UiInvokePopupMenu(&s_PointPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupPoint); UI()->SetActiveItem(0); } } @@ -1107,210 +979,214 @@ void EDITOR::do_quad_point(QUAD *q, int quad_index, int v) { if(!UI()->MouseButton(0)) { - if(!moved) + if(!s_Moved) { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - selected_points ^= 1<<v; + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) + m_SelectedPoints ^= 1<<v; else - selected_points = 1<<v; + m_SelectedPoints = 1<<v; } - lock_mouse = false; + m_LockMouse = false; UI()->SetActiveItem(0); } } Graphics()->SetColor(1,1,1,1); } - else if(UI()->HotItem() == id) + else if(UI()->HotItem() == pId) { - ui_got_context = id; - + ms_pUiGotContext = pId; + Graphics()->SetColor(1,1,1,1); - tooltip = "Left mouse button to move. Hold shift to move the texture."; - + m_pTooltip = "Left mouse button to move. Hold shift to move the texture."; + if(UI()->MouseButton(0)) { - UI()->SetActiveItem(id); - moved = false; - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + UI()->SetActiveItem(pId); + s_Moved = false; + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) { - operation = OP_MOVEUV; - lock_mouse = true; + s_Operation = OP_MOVEUV; + m_LockMouse = true; } else - operation = OP_MOVEPOINT; - - if(!(selected_points&(1<<v))) + s_Operation = OP_MOVEPOINT; + + if(!(m_SelectedPoints&(1<<v))) { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - selected_points |= 1<<v; + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) + m_SelectedPoints |= 1<<v; else - selected_points = 1<<v; - moved = true; + m_SelectedPoints = 1<<v; + s_Moved = true; } - - selected_quad = quad_index; + + m_SelectedQuad = QuadIndex; } else if(UI()->MouseButton(1)) { - operation = OP_CONTEXT_MENU; - selected_quad = quad_index; - UI()->SetActiveItem(id); + s_Operation = OP_CONTEXT_MENU; + m_SelectedQuad = QuadIndex; + UI()->SetActiveItem(pId); } } else Graphics()->SetColor(1,0,0,1); - - Graphics()->QuadsDraw(px, py, 5.0f, 5.0f); + + IGraphics::CQuadItem QuadItem(px, py, 5.0f, 5.0f); + Graphics()->QuadsDraw(&QuadItem, 1); } -void EDITOR::do_map_editor(CUIRect view, CUIRect toolbar) +void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar) { //UI()->ClipEnable(&view); - - bool show_picker = inp_key_pressed(KEY_SPACE) != 0 && dialog == DIALOG_NONE; + + bool ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE; // render all good stuff - if(!show_picker) + if(!ShowPicker) { - for(int g = 0; g < map.groups.len(); g++) + for(int g = 0; g < m_Map.m_lGroups.size(); g++) { - if(map.groups[g]->visible) - map.groups[g]->render(); + if(m_Map.m_lGroups[g]->m_Visible) + m_Map.m_lGroups[g]->Render(); //UI()->ClipEnable(&view); } - + // render the game above everything else - if(map.game_group->visible && map.game_layer->visible) + if(m_Map.m_pGameGroup->m_Visible && m_Map.m_pGameLayer->m_Visible) { - map.game_group->mapscreen(); - map.game_layer->render(); + m_Map.m_pGameGroup->MapScreen(); + m_Map.m_pGameLayer->Render(); } } - static void *editor_id = (void *)&editor_id; - int inside = UI()->MouseInside(&view); + static void *s_pEditorId = (void *)&s_pEditorId; + int Inside = UI()->MouseInside(&View); // fetch mouse position float wx = UI()->MouseWorldX(); float wy = UI()->MouseWorldY(); float mx = UI()->MouseX(); float my = UI()->MouseY(); - - static float start_wx = 0; - static float start_wy = 0; - static float start_mx = 0; - static float start_my = 0; - + + static float s_StartWx = 0; + static float s_StartWy = 0; + static float s_StartMx = 0; + static float s_StartMy = 0; + enum { OP_NONE=0, OP_BRUSH_GRAB, OP_BRUSH_DRAW, + OP_BRUSH_PAINT, OP_PAN_WORLD, OP_PAN_EDITOR, }; // remap the screen so it can display the whole tileset - if(show_picker) - { - CUIRect screen = *UI()->Screen(); - float size = 32.0*16.0f; - float w = size*(screen.w/view.w); - float h = size*(screen.h/view.h); - float x = -(view.x/screen.w)*w; - float y = -(view.y/screen.h)*h; - wx = x+w*mx/screen.w; - wy = y+h*my/screen.h; + if(ShowPicker) + { + CUIRect Screen = *UI()->Screen(); + float Size = 32.0*16.0f; + float w = Size*(Screen.w/View.w); + float h = Size*(Screen.h/View.h); + float x = -(View.x/Screen.w)*w; + float y = -(View.y/Screen.h)*h; + wx = x+w*mx/Screen.w; + wy = y+h*my/Screen.h; Graphics()->MapScreen(x, y, x+w, y+h); - LAYER_TILES *t = (LAYER_TILES *)get_selected_layer_type(0, LAYERTYPE_TILES); + CLayerTiles *t = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); if(t) { - tileset_picker.image = t->image; - tileset_picker.tex_id = t->tex_id; - tileset_picker.render(); + m_TilesetPicker.m_Image = t->m_Image; + m_TilesetPicker.m_TexId = t->m_TexId; + m_TilesetPicker.Render(); } } - - static int operation = OP_NONE; - + + static int s_Operation = OP_NONE; + // draw layer borders - LAYER *edit_layers[16]; - int num_edit_layers = 0; - num_edit_layers = 0; - - if(show_picker) + CLayer *pEditLayers[16]; + int NumEditLayers = 0; + NumEditLayers = 0; + + if(ShowPicker) { - edit_layers[0] = &tileset_picker; - num_edit_layers++; + pEditLayers[0] = &m_TilesetPicker; + NumEditLayers++; } else { - edit_layers[0] = get_selected_layer(0); - if(edit_layers[0]) - num_edit_layers++; + pEditLayers[0] = GetSelectedLayer(0); + if(pEditLayers[0]) + NumEditLayers++; - LAYERGROUP *g = get_selected_group(); + CLayerGroup *g = GetSelectedGroup(); if(g) { - g->mapscreen(); - - for(int i = 0; i < num_edit_layers; i++) + g->MapScreen(); + + for(int i = 0; i < NumEditLayers; i++) { - if(edit_layers[i]->type != LAYERTYPE_TILES) + if(pEditLayers[i]->m_Type != LAYERTYPE_TILES) continue; - + float w, h; - edit_layers[i]->get_size(&w, &h); + pEditLayers[i]->GetSize(&w, &h); + IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem(0, 0, w, 0), + IGraphics::CLineItem(w, 0, w, h), + IGraphics::CLineItem(w, h, 0, h), + IGraphics::CLineItem(0, h, 0, 0)}; Graphics()->TextureSet(-1); Graphics()->LinesBegin(); - Graphics()->LinesDraw(0,0, w,0); - Graphics()->LinesDraw(w,0, w,h); - Graphics()->LinesDraw(w,h, 0,h); - Graphics()->LinesDraw(0,h, 0,0); + Graphics()->LinesDraw(Array, 4); Graphics()->LinesEnd(); } } } - - if(inside) + + if(Inside) { - UI()->SetHotItem(editor_id); - + UI()->SetHotItem(s_pEditorId); + // do global operations like pan and zoom if(UI()->ActiveItem() == 0 && (UI()->MouseButton(0) || UI()->MouseButton(2))) { - start_wx = wx; - start_wy = wy; - start_mx = mx; - start_my = my; - - if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL) || UI()->MouseButton(2)) + s_StartWx = wx; + s_StartWy = wy; + s_StartMx = mx; + s_StartMy = my; + + if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL) || UI()->MouseButton(2)) { - if(inp_key_pressed(KEY_LSHIFT)) - operation = OP_PAN_EDITOR; + if(Input()->KeyPressed(KEY_LSHIFT)) + s_Operation = OP_PAN_EDITOR; else - operation = OP_PAN_WORLD; - UI()->SetActiveItem(editor_id); + s_Operation = OP_PAN_WORLD; + UI()->SetActiveItem(s_pEditorId); } } // brush editing - if(UI()->HotItem() == editor_id) + if(UI()->HotItem() == s_pEditorId) { - if(brush.is_empty()) - tooltip = "Use left mouse button to drag and create a brush."; + if(m_Brush.IsEmpty()) + m_pTooltip = "Use left mouse button to drag and create a brush."; else - tooltip = "Use left mouse button to paint with the brush. Right button clears the brush."; + m_pTooltip = "Use left mouse button to paint with the brush. Right button clears the brush."; - if(UI()->ActiveItem() == editor_id) + if(UI()->ActiveItem() == s_pEditorId) { CUIRect r; - r.x = start_wx; - r.y = start_wy; - r.w = wx-start_wx; - r.h = wy-start_wy; + r.x = s_StartWx; + r.y = s_StartWy; + r.w = wx-s_StartWx; + r.h = wy-s_StartWy; if(r.w < 0) { r.x += r.w; @@ -1322,38 +1198,53 @@ void EDITOR::do_map_editor(CUIRect view, CUIRect toolbar) r.y += r.h; r.h = -r.h; } - - if(operation == OP_BRUSH_DRAW) - { - if(!brush.is_empty()) + + if(s_Operation == OP_BRUSH_DRAW) + { + if(!m_Brush.IsEmpty()) { // draw with brush - for(int k = 0; k < num_edit_layers; k++) + for(int k = 0; k < NumEditLayers; k++) { - if(edit_layers[k]->type == brush.layers[0]->type) - edit_layers[k]->brush_draw(brush.layers[0], wx, wy); + if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type) + pEditLayers[k]->BrushDraw(m_Brush.m_lLayers[0], wx, wy); } } } - else if(operation == OP_BRUSH_GRAB) + else if(s_Operation == OP_BRUSH_GRAB) { if(!UI()->MouseButton(0)) { // grab brush dbg_msg("editor", "grabbing %f %f %f %f", r.x, r.y, r.w, r.h); - + // TODO: do all layers - int grabs = 0; - for(int k = 0; k < num_edit_layers; k++) - grabs += edit_layers[k]->brush_grab(&brush, r); - if(grabs == 0) - brush.clear(); + int Grabs = 0; + for(int k = 0; k < NumEditLayers; k++) + Grabs += pEditLayers[k]->BrushGrab(&m_Brush, r); + if(Grabs == 0) + m_Brush.Clear(); } else { //editor.map.groups[selected_group]->mapscreen(); - for(int k = 0; k < num_edit_layers; k++) - edit_layers[k]->brush_selecting(r); + for(int k = 0; k < NumEditLayers; k++) + pEditLayers[k]->BrushSelecting(r); + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); + } + } + else if(s_Operation == OP_BRUSH_PAINT) + { + if(!UI()->MouseButton(0)) + { + for(int k = 0; k < NumEditLayers; k++) + pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r); + } + else + { + //editor.map.groups[selected_group]->mapscreen(); + for(int k = 0; k < NumEditLayers; k++) + pEditLayers[k]->BrushSelecting(r); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); } } @@ -1361,182 +1252,196 @@ void EDITOR::do_map_editor(CUIRect view, CUIRect toolbar) else { if(UI()->MouseButton(1)) - brush.clear(); - - if(UI()->MouseButton(0) && operation == OP_NONE) + m_Brush.Clear(); + + if(UI()->MouseButton(0) && s_Operation == OP_NONE) { - UI()->SetActiveItem(editor_id); - - if(brush.is_empty()) - operation = OP_BRUSH_GRAB; + UI()->SetActiveItem(s_pEditorId); + + if(m_Brush.IsEmpty()) + s_Operation = OP_BRUSH_GRAB; else { - operation = OP_BRUSH_DRAW; - for(int k = 0; k < num_edit_layers; k++) + s_Operation = OP_BRUSH_DRAW; + for(int k = 0; k < NumEditLayers; k++) { - if(edit_layers[k]->type == brush.layers[0]->type) - edit_layers[k]->brush_place(brush.layers[0], wx, wy); + if(pEditLayers[k]->m_Type == m_Brush.m_lLayers[0]->m_Type) + pEditLayers[k]->BrushPlace(m_Brush.m_lLayers[0], wx, wy); } - + } + + CLayerTiles *pLayer = (CLayerTiles*)GetSelectedLayerType(0, LAYERTYPE_TILES); + if((Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) && pLayer) + s_Operation = OP_BRUSH_PAINT; } - - if(!brush.is_empty()) + + if(!m_Brush.IsEmpty()) { - brush.offset_x = -(int)wx; - brush.offset_y = -(int)wy; - for(int i = 0; i < brush.layers.len(); i++) + m_Brush.m_OffsetX = -(int)wx; + m_Brush.m_OffsetY = -(int)wy; + for(int i = 0; i < m_Brush.m_lLayers.size(); i++) { - if(brush.layers[i]->type == LAYERTYPE_TILES) + if(m_Brush.m_lLayers[i]->m_Type == LAYERTYPE_TILES) { - brush.offset_x = -(int)(wx/32.0f)*32; - brush.offset_y = -(int)(wy/32.0f)*32; + m_Brush.m_OffsetX = -(int)(wx/32.0f)*32; + m_Brush.m_OffsetY = -(int)(wy/32.0f)*32; break; } } - - LAYERGROUP *g = get_selected_group(); - brush.offset_x += g->offset_x; - brush.offset_y += g->offset_y; - brush.parallax_x = g->parallax_x; - brush.parallax_y = g->parallax_y; - brush.render(); + + CLayerGroup *g = GetSelectedGroup(); + m_Brush.m_OffsetX += g->m_OffsetX; + m_Brush.m_OffsetY += g->m_OffsetY; + m_Brush.m_ParallaxX = g->m_ParallaxX; + m_Brush.m_ParallaxY = g->m_ParallaxY; + m_Brush.Render(); float w, h; - brush.get_size(&w, &h); - + m_Brush.GetSize(&w, &h); + + IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem(0, 0, w, 0), + IGraphics::CLineItem(w, 0, w, h), + IGraphics::CLineItem(w, h, 0, h), + IGraphics::CLineItem(0, h, 0, 0)}; Graphics()->TextureSet(-1); Graphics()->LinesBegin(); - Graphics()->LinesDraw(0,0, w,0); - Graphics()->LinesDraw(w,0, w,h); - Graphics()->LinesDraw(w,h, 0,h); - Graphics()->LinesDraw(0,h, 0,0); + Graphics()->LinesDraw(Array, 4); Graphics()->LinesEnd(); - + } } } - + // quad editing { - if(!show_picker && brush.is_empty()) + if(!ShowPicker && m_Brush.IsEmpty()) { // fetch layers - LAYERGROUP *g = get_selected_group(); + CLayerGroup *g = GetSelectedGroup(); if(g) - g->mapscreen(); - - for(int k = 0; k < num_edit_layers; k++) + g->MapScreen(); + + for(int k = 0; k < NumEditLayers; k++) { - if(edit_layers[k]->type == LAYERTYPE_QUADS) + if(pEditLayers[k]->m_Type == LAYERTYPE_QUADS) { - LAYER_QUADS *layer = (LAYER_QUADS *)edit_layers[k]; - + CLayerQuads *pLayer = (CLayerQuads *)pEditLayers[k]; + Graphics()->TextureSet(-1); - Graphics()->QuadsBegin(); - for(int i = 0; i < layer->quads.len(); i++) + Graphics()->QuadsBegin(); + for(int i = 0; i < pLayer->m_lQuads.size(); i++) { for(int v = 0; v < 4; v++) - do_quad_point(&layer->quads[i], i, v); - - do_quad(&layer->quads[i], i); + DoQuadPoint(&pLayer->m_lQuads[i], i, v); + + DoQuad(&pLayer->m_lQuads[i], i); } Graphics()->QuadsEnd(); } } - + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); - } - + } + // do panning - if(UI()->ActiveItem() == editor_id) + if(UI()->ActiveItem() == s_pEditorId) { - if(operation == OP_PAN_WORLD) + if(s_Operation == OP_PAN_WORLD) { - world_offset_x -= mouse_delta_x*world_zoom; - world_offset_y -= mouse_delta_y*world_zoom; + m_WorldOffsetX -= m_MouseDeltaX*m_WorldZoom; + m_WorldOffsetY -= m_MouseDeltaY*m_WorldZoom; } - else if(operation == OP_PAN_EDITOR) + else if(s_Operation == OP_PAN_EDITOR) { - editor_offset_x -= mouse_delta_x*world_zoom; - editor_offset_y -= mouse_delta_y*world_zoom; + m_EditorOffsetX -= m_MouseDeltaX*m_WorldZoom; + m_EditorOffsetY -= m_MouseDeltaY*m_WorldZoom; } // release mouse if(!UI()->MouseButton(0)) { - operation = OP_NONE; + s_Operation = OP_NONE; UI()->SetActiveItem(0); } } } } - - if(get_selected_group() && get_selected_group()->use_clipping) + + if(GetSelectedGroup() && GetSelectedGroup()->m_UseClipping) { - LAYERGROUP *g = map.game_group; - g->mapscreen(); - + CLayerGroup *g = m_Map.m_pGameGroup; + g->MapScreen(); + Graphics()->TextureSet(-1); Graphics()->LinesBegin(); CUIRect r; - r.x = get_selected_group()->clip_x; - r.y = get_selected_group()->clip_y; - r.w = get_selected_group()->clip_w; - r.h = get_selected_group()->clip_h; - + r.x = GetSelectedGroup()->m_ClipX; + r.y = GetSelectedGroup()->m_ClipY; + r.w = GetSelectedGroup()->m_ClipW; + r.h = GetSelectedGroup()->m_ClipH; + + IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y), + IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h), + IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h), + IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)}; Graphics()->SetColor(1,0,0,1); - Graphics()->LinesDraw(r.x, r.y, r.x+r.w, r.y); - Graphics()->LinesDraw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); - Graphics()->LinesDraw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); - Graphics()->LinesDraw(r.x, r.y+r.h, r.x, r.y); - + Graphics()->LinesDraw(Array, 4); + Graphics()->LinesEnd(); } - // render screen sizes - if(proof_borders) + // render screen sizes + if(m_ProofBorders) { - LAYERGROUP *g = map.game_group; - g->mapscreen(); - + CLayerGroup *g = m_Map.m_pGameGroup; + g->MapScreen(); + Graphics()->TextureSet(-1); Graphics()->LinesBegin(); - - float last_points[4]; - float start = 1.0f; //9.0f/16.0f; - float end = 16.0f/9.0f; - const int num_steps = 20; - for(int i = 0; i <= num_steps; i++) + + float aLastPoints[4]; + float Start = 1.0f; //9.0f/16.0f; + float End = 16.0f/9.0f; + const int NumSteps = 20; + for(int i = 0; i <= NumSteps; i++) { - float points[4]; - float aspect = start + (end-start)*(i/(float)num_steps); - - RenderTools()->mapscreen_to_world( - world_offset_x, world_offset_y, - 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); - + float aPoints[4]; + float Aspect = Start + (End-Start)*(i/(float)NumSteps); + + RenderTools()->MapscreenToWorld( + m_WorldOffsetX, m_WorldOffsetY, + 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); + if(i == 0) { - Graphics()->LinesDraw(points[0], points[1], points[2], points[1]); - Graphics()->LinesDraw(points[0], points[3], points[2], points[3]); + IGraphics::CLineItem Array[2] = { + IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[2], aPoints[1]), + IGraphics::CLineItem(aPoints[0], aPoints[3], aPoints[2], aPoints[3])}; + Graphics()->LinesDraw(Array, 2); } if(i != 0) { - Graphics()->LinesDraw(points[0], points[1], last_points[0], last_points[1]); - Graphics()->LinesDraw(points[2], points[1], last_points[2], last_points[1]); - Graphics()->LinesDraw(points[0], points[3], last_points[0], last_points[3]); - Graphics()->LinesDraw(points[2], points[3], last_points[2], last_points[3]); + IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem(aPoints[0], aPoints[1], aLastPoints[0], aLastPoints[1]), + IGraphics::CLineItem(aPoints[2], aPoints[1], aLastPoints[2], aLastPoints[1]), + IGraphics::CLineItem(aPoints[0], aPoints[3], aLastPoints[0], aLastPoints[3]), + IGraphics::CLineItem(aPoints[2], aPoints[3], aLastPoints[2], aLastPoints[3])}; + Graphics()->LinesDraw(Array, 4); } - if(i == num_steps) + if(i == NumSteps) { - Graphics()->LinesDraw(points[0], points[1], points[0], points[3]); - Graphics()->LinesDraw(points[2], points[1], points[2], points[3]); + IGraphics::CLineItem Array[2] = { + IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[0], aPoints[3]), + IGraphics::CLineItem(aPoints[2], aPoints[1], aPoints[2], aPoints[3])}; + Graphics()->LinesDraw(Array, 2); } - - mem_copy(last_points, points, sizeof(points)); + + mem_copy(aLastPoints, aPoints, sizeof(aPoints)); } if(1) @@ -1544,351 +1449,359 @@ void EDITOR::do_map_editor(CUIRect view, CUIRect toolbar) Graphics()->SetColor(1,0,0,1); for(int i = 0; i < 2; i++) { - float points[4]; - float aspects[] = {4.0f/3.0f, 16.0f/10.0f, 5.0f/4.0f, 16.0f/9.0f}; - float aspect = aspects[i]; - - RenderTools()->mapscreen_to_world( - world_offset_x, world_offset_y, - 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); - + float aPoints[4]; + float aAspects[] = {4.0f/3.0f, 16.0f/10.0f, 5.0f/4.0f, 16.0f/9.0f}; + float Aspect = aAspects[i]; + + RenderTools()->MapscreenToWorld( + m_WorldOffsetX, m_WorldOffsetY, + 1.0f, 1.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints); + CUIRect r; - r.x = points[0]; - r.y = points[1]; - r.w = points[2]-points[0]; - r.h = points[3]-points[1]; - - Graphics()->LinesDraw(r.x, r.y, r.x+r.w, r.y); - Graphics()->LinesDraw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); - Graphics()->LinesDraw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); - Graphics()->LinesDraw(r.x, r.y+r.h, r.x, r.y); + r.x = aPoints[0]; + r.y = aPoints[1]; + r.w = aPoints[2]-aPoints[0]; + r.h = aPoints[3]-aPoints[1]; + + IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem(r.x, r.y, r.x+r.w, r.y), + IGraphics::CLineItem(r.x+r.w, r.y, r.x+r.w, r.y+r.h), + IGraphics::CLineItem(r.x+r.w, r.y+r.h, r.x, r.y+r.h), + IGraphics::CLineItem(r.x, r.y+r.h, r.x, r.y)}; + Graphics()->LinesDraw(Array, 4); Graphics()->SetColor(0,1,0,1); } } - + Graphics()->LinesEnd(); } - + Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); //UI()->ClipDisable(); } -int EDITOR::do_properties(CUIRect *toolbox, PROPERTY *props, int *ids, int *new_val) +int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIds, int *pNewVal) { - int change = -1; + int Change = -1; - for(int i = 0; props[i].name; i++) + for(int i = 0; pProps[i].m_pName; i++) { - CUIRect slot; - toolbox->HSplitTop(13.0f, &slot, toolbox); - CUIRect label, shifter; - slot.VSplitMid(&label, &shifter); - shifter.HMargin(1.0f, &shifter); - UI()->DoLabel(&label, props[i].name, 10.0f, -1, -1); - - if(props[i].type == PROPTYPE_INT_STEP) + CUIRect Slot; + pToolBox->HSplitTop(13.0f, &Slot, pToolBox); + CUIRect Label, Shifter; + Slot.VSplitMid(&Label, &Shifter); + Shifter.HMargin(1.0f, &Shifter); + UI()->DoLabel(&Label, pProps[i].m_pName, 10.0f, -1, -1); + + if(pProps[i].m_Type == PROPTYPE_INT_STEP) { - CUIRect inc, dec; - char buf[64]; - - shifter.VSplitRight(10.0f, &shifter, &inc); - shifter.VSplitLeft(10.0f, &dec, &shifter); - sprintf(buf, "%d", props[i].value); - RenderTools()->DrawUIRect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); - UI()->DoLabel(&shifter, buf, 10.0f, 0, -1); - - if(DoButton_ButtonDec(&ids[i], 0, 0, &dec, 0, "Decrease")) + CUIRect Inc, Dec; + char aBuf[64]; + + Shifter.VSplitRight(10.0f, &Shifter, &Inc); + Shifter.VSplitLeft(10.0f, &Dec, &Shifter); + str_format(aBuf, sizeof(aBuf),"%d", pProps[i].m_Value); + RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f); + UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1); + + if(DoButton_ButtonDec(&pIds[i], 0, 0, &Dec, 0, "Decrease")) { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - *new_val = props[i].value-5; + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) + *pNewVal = pProps[i].m_Value-5; else - *new_val = props[i].value-1; - change = i; + *pNewVal = pProps[i].m_Value-1; + Change = i; } - if(DoButton_ButtonInc(((char *)&ids[i])+1, 0, 0, &inc, 0, "Increase")) + if(DoButton_ButtonInc(((char *)&pIds[i])+1, 0, 0, &Inc, 0, "Increase")) { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - *new_val = props[i].value+5; + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) + *pNewVal = pProps[i].m_Value+5; else - *new_val = props[i].value+1; - change = i; + *pNewVal = pProps[i].m_Value+1; + Change = i; } } - else if(props[i].type == PROPTYPE_BOOL) + else if(pProps[i].m_Type == PROPTYPE_BOOL) { - CUIRect no, yes; - shifter.VSplitMid(&no, &yes); - if(DoButton_ButtonDec(&ids[i], "No", !props[i].value, &no, 0, "")) + CUIRect No, Yes; + Shifter.VSplitMid(&No, &Yes); + if(DoButton_ButtonDec(&pIds[i], "No", !pProps[i].m_Value, &No, 0, "")) { - *new_val = 0; - change = i; + *pNewVal = 0; + Change = i; } - if(DoButton_ButtonInc(((char *)&ids[i])+1, "Yes", props[i].value, &yes, 0, "")) + if(DoButton_ButtonInc(((char *)&pIds[i])+1, "Yes", pProps[i].m_Value, &Yes, 0, "")) { - *new_val = 1; - change = i; + *pNewVal = 1; + Change = i; } - } - else if(props[i].type == PROPTYPE_INT_SCROLL) + } + else if(pProps[i].m_Type == PROPTYPE_INT_SCROLL) { - int new_value = ui_do_value_selector(&ids[i], &shifter, "", props[i].value, props[i].min, props[i].max, 1.0f); - if(new_value != props[i].value) + int NewValue = UiDoValueSelector(&pIds[i], &Shifter, "", pProps[i].m_Value, pProps[i].m_Min, pProps[i].m_Max, 1.0f); + if(NewValue != pProps[i].m_Value) { - *new_val = new_value; - change = i; + *pNewVal = NewValue; + Change = i; } } - else if(props[i].type == PROPTYPE_COLOR) + else if(pProps[i].m_Type == PROPTYPE_COLOR) { - static const char *texts[4] = {"R", "G", "B", "A"}; - static int shift[] = {24, 16, 8, 0}; - int new_color = 0; - + static const char *s_paTexts[4] = {"R", "G", "B", "A"}; + static int s_aShift[] = {24, 16, 8, 0}; + int NewColor = 0; + for(int c = 0; c < 4; c++) { - int v = (props[i].value >> shift[c])&0xff; - new_color |= ui_do_value_selector(((char *)&ids[i])+c, &shifter, texts[c], v, 0, 255, 1.0f)<<shift[c]; + int v = (pProps[i].m_Value >> s_aShift[c])&0xff; + NewColor |= UiDoValueSelector(((char *)&pIds[i])+c, &Shifter, s_paTexts[c], v, 0, 255, 1.0f)<<s_aShift[c]; if(c != 3) { - toolbox->HSplitTop(13.0f, &slot, toolbox); - slot.VSplitMid(0, &shifter); - shifter.HMargin(1.0f, &shifter); + pToolBox->HSplitTop(13.0f, &Slot, pToolBox); + Slot.VSplitMid(0, &Shifter); + Shifter.HMargin(1.0f, &Shifter); } } - - if(new_color != props[i].value) + + if(NewColor != pProps[i].m_Value) { - *new_val = new_color; - change = i; + *pNewVal = NewColor; + Change = i; } } - else if(props[i].type == PROPTYPE_IMAGE) + else if(pProps[i].m_Type == PROPTYPE_IMAGE) { - char buf[64]; - if(props[i].value < 0) - strcpy(buf, "None"); + char aBuf[64]; + if(pProps[i].m_Value < 0) + str_copy(aBuf, "None", sizeof(aBuf)); else - sprintf(buf, "%s", map.images[props[i].value]->name); - - if(DoButton_Editor(&ids[i], buf, 0, &shifter, 0, 0)) - popup_select_image_invoke(props[i].value, UI()->MouseX(), UI()->MouseY()); - - int r = popup_select_image_result(); + str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lImages[pProps[i].m_Value]->m_aName); + + if(DoButton_Editor(&pIds[i], aBuf, 0, &Shifter, 0, 0)) + PopupSelectImageInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY()); + + int r = PopupSelectImageResult(); if(r >= -1) { - *new_val = r; - change = i; + *pNewVal = r; + Change = i; } } } - return change; + return Change; } -void EDITOR::render_layers(CUIRect toolbox, CUIRect toolbar, CUIRect view) +void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) { - CUIRect layersbox = toolbox; + CUIRect LayersBox = ToolBox; - if(!gui_active) + if(!m_GuiActive) return; - - CUIRect slot, button; - char buf[64]; - int valid_group = 0; - int valid_layer = 0; - if(selected_group >= 0 && selected_group < map.groups.len()) - valid_group = 1; + CUIRect Slot, Button; + char aBuf[64]; - if(valid_group && selected_layer >= 0 && selected_layer < map.groups[selected_group]->layers.len()) - valid_layer = 1; - - // render layers + int ValidGroup = 0; + int ValidLayer = 0; + if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size()) + ValidGroup = 1; + + if(ValidGroup && m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size()) + ValidLayer = 1; + + // render layers { - for(int g = 0; g < map.groups.len(); g++) + for(int g = 0; g < m_Map.m_lGroups.size(); g++) { - CUIRect visible_toggle; - layersbox.HSplitTop(12.0f, &slot, &layersbox); - slot.VSplitLeft(12, &visible_toggle, &slot); - if(DoButton_ButtonL(&map.groups[g]->visible, map.groups[g]->visible?"V":"H", 0, &visible_toggle, 0, "Toggle group visibility")) - map.groups[g]->visible = !map.groups[g]->visible; - - sprintf(buf, "#%d %s", g, map.groups[g]->name); - if(int result = DoButton_ButtonR(&map.groups[g], buf, g==selected_group, &slot, - BUTTON_CONTEXT, "Select group. Right click for properties.")) + CUIRect VisibleToggle; + LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); + Slot.VSplitLeft(12, &VisibleToggle, &Slot); + if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L)) + m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible; + + str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_pName); + if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot, + BUTTON_CONTEXT, "Select group. Right click for properties.", CUI::CORNER_R)) { - selected_group = g; - selected_layer = 0; - - static int group_popup_id = 0; - if(result == 2) - ui_invoke_popup_menu(&group_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, popup_group); + m_SelectedGroup = g; + m_SelectedLayer = 0; + + static int s_GroupPopupId = 0; + if(Result == 2) + UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupGroup); } - - - layersbox.HSplitTop(2.0f, &slot, &layersbox); - - for(int i = 0; i < map.groups[g]->layers.len(); i++) + + LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); + + for(int i = 0; i < m_Map.m_lGroups[g]->m_lLayers.size(); i++) { //visible - layersbox.HSplitTop(12.0f, &slot, &layersbox); - slot.VSplitLeft(12.0f, 0, &button); - button.VSplitLeft(15, &visible_toggle, &button); + LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); + Slot.VSplitLeft(12.0f, 0, &Button); + Button.VSplitLeft(15, &VisibleToggle, &Button); - if(DoButton_ButtonL(&map.groups[g]->layers[i]->visible, map.groups[g]->layers[i]->visible?"V":"H", 0, &visible_toggle, 0, "Toggle layer visibility")) - map.groups[g]->layers[i]->visible = !map.groups[g]->layers[i]->visible; + if(DoButton_Ex(&m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible, m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible?"V":"H", 0, &VisibleToggle, 0, "Toggle layer visibility", CUI::CORNER_L)) + m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible = !m_Map.m_lGroups[g]->m_lLayers[i]->m_Visible; - sprintf(buf, "#%d %s ", i, map.groups[g]->layers[i]->type_name); - if(int result = DoButton_ButtonR(map.groups[g]->layers[i], buf, g==selected_group&&i==selected_layer, &button, - BUTTON_CONTEXT, "Select layer. Right click for properties.")) + str_format(aBuf, sizeof(aBuf),"#%d %s ", i, m_Map.m_lGroups[g]->m_lLayers[i]->m_pTypeName); + if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button, + BUTTON_CONTEXT, "Select layer. Right click for properties.", CUI::CORNER_R)) { - selected_layer = i; - selected_group = g; - static int layer_popup_id = 0; - if(result == 2) - ui_invoke_popup_menu(&layer_popup_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, popup_layer); + m_SelectedLayer = i; + m_SelectedGroup = g; + static int s_LayerPopupId = 0; + if(Result == 2) + UiInvokePopupMenu(&s_LayerPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupLayer); } - - - layersbox.HSplitTop(2.0f, &slot, &layersbox); + + + LayersBox.HSplitTop(2.0f, &Slot, &LayersBox); } - layersbox.HSplitTop(5.0f, &slot, &layersbox); + LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); } } - + { - layersbox.HSplitTop(12.0f, &slot, &layersbox); + LayersBox.HSplitTop(12.0f, &Slot, &LayersBox); - static int new_group_button = 0; - if(DoButton_Editor(&new_group_button, "Add Group", 0, &slot, 0, "Adds a new group")) + static int s_NewGroupButton = 0; + if(DoButton_Editor(&s_NewGroupButton, "Add Group", 0, &Slot, 0, "Adds a new group")) { - map.new_group(); - selected_group = map.groups.len()-1; + m_Map.NewGroup(); + m_SelectedGroup = m_Map.m_lGroups.size()-1; } } - layersbox.HSplitTop(5.0f, &slot, &layersbox); - + LayersBox.HSplitTop(5.0f, &Slot, &LayersBox); + } -static void extract_name(const char *filename, char *name) +static void ExtractName(const char *pFileName, char *pName) { - int len = strlen(filename); - int start = len; - int end = len; - - while(start > 0) + int Len = str_length(pFileName); + int Start = Len; + int End = Len; + + while(Start > 0) { - start--; - if(filename[start] == '/' || filename[start] == '\\') + Start--; + if(pFileName[Start] == '/' || pFileName[Start] == '\\') { - start++; + Start++; break; } } - - end = start; - for(int i = start; i < len; i++) + + End = Start; + for(int i = Start; i < Len; i++) { - if(filename[i] == '.') - end = i; + if(pFileName[i] == '.') + End = i; } - - if(end == start) - end = len; - - int final_len = end-start; - mem_copy(name, &filename[start], final_len); - name[final_len] = 0; - dbg_msg("", "%s %s %d %d", filename, name, start, end); + + if(End == Start) + End = Len; + + int FinalLen = End-Start; + mem_copy(pName, &pFileName[Start], FinalLen); + pName[FinalLen] = 0; + dbg_msg("", "%s %s %d %d", pFileName, pName, Start, End); } -void EDITOR::replace_image(const char *filename, void *user) +void CEditor::ReplaceImage(const char *pFileName, void *pUser) { - EDITOR *editor = (EDITOR *)user; - EDITOR_IMAGE imginfo(editor); - if(!editor->Graphics()->LoadPNG(&imginfo, filename)) + CEditor *pEditor = (CEditor *)pUser; + CEditorImage ImgInfo(pEditor); + if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName)) return; - - EDITOR_IMAGE *img = editor->map.images[editor->selected_image]; - editor->Graphics()->UnloadTexture(img->tex_id); - *img = imginfo; - extract_name(filename, img->name); - img->tex_id = editor->Graphics()->LoadTextureRaw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); + + CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage]; + pEditor->Graphics()->UnloadTexture(pImg->m_TexId); + *pImg = ImgInfo; + ExtractName(pFileName, pImg->m_aName); + pImg->m_TexId = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); } -void EDITOR::add_image(const char *filename, void *user) +void CEditor::AddImage(const char *pFileName, void *pUser) { - EDITOR *editor = (EDITOR *)user; - EDITOR_IMAGE imginfo(editor); - if(!editor->Graphics()->LoadPNG(&imginfo, filename)) + CEditor *pEditor = (CEditor *)pUser; + CEditorImage ImgInfo(pEditor); + if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName)) return; - EDITOR_IMAGE *img = new EDITOR_IMAGE(editor); - *img = imginfo; - img->tex_id = editor->Graphics()->LoadTextureRaw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); - img->external = 1; // external by default - extract_name(filename, img->name); - editor->map.images.add(img); + CEditorImage *pImg = new CEditorImage(pEditor); + *pImg = ImgInfo; + pImg->m_TexId = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); + pImg->m_External = 1; // external by default + ExtractName(pFileName, pImg->m_aName); + + for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i) + { + if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName)) + return; + } + + pEditor->m_Map.m_lImages.add(pImg); } -static int modify_index_deleted_index; -static void modify_index_deleted(int *index) +static int gs_ModifyIndexDeletedIndex; +static void ModifyIndexDeleted(int *pIndex) { - if(*index == modify_index_deleted_index) - *index = -1; - else if(*index > modify_index_deleted_index) - *index = *index - 1; + if(*pIndex == gs_ModifyIndexDeletedIndex) + *pIndex = -1; + else if(*pIndex > gs_ModifyIndexDeletedIndex) + *pIndex = *pIndex - 1; } -int EDITOR::popup_image(EDITOR *pEditor, CUIRect view) +int CEditor::PopupImage(CEditor *pEditor, CUIRect View) { - static int replace_button = 0; - static int remove_button = 0; + static int s_ReplaceButton = 0; + static int s_RemoveButton = 0; - CUIRect slot; - view.HSplitTop(2.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - EDITOR_IMAGE *img = pEditor->map.images[pEditor->selected_image]; - - static int external_button = 0; - if(img->external) + CUIRect Slot; + View.HSplitTop(2.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + CEditorImage *pImg = pEditor->m_Map.m_lImages[pEditor->m_SelectedImage]; + + static int s_ExternalButton = 0; + if(pImg->m_External) { - if(pEditor->DoButton_MenuItem(&external_button, "Embedd", 0, &slot, 0, "Embedds the image into the map file.")) + if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embedd", 0, &Slot, 0, "Embedds the image into the map file.")) { - img->external = 0; + pImg->m_External = 0; return 1; } } else - { - if(pEditor->DoButton_MenuItem(&external_button, "Make external", 0, &slot, 0, "Removes the image from the map file.")) + { + if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file.")) { - img->external = 1; + pImg->m_External = 1; return 1; } } - view.HSplitTop(10.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&replace_button, "Replace", 0, &slot, 0, "Replaces the image with a new one")) + View.HSplitTop(10.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the image with a new one")) { - pEditor->invoke_file_dialog(LISTDIRTYPE_ALL, "Replace Image", "Replace", "mapres/", "", replace_image, pEditor); + pEditor->InvokeFileDialog(IStorage::TYPE_ALL, "Replace Image", "Replace", "mapres/", "", ReplaceImage, pEditor); return 1; } - view.HSplitTop(10.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&remove_button, "Remove", 0, &slot, 0, "Removes the image from the map")) + View.HSplitTop(10.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the image from the map")) { - delete img; - pEditor->map.images.removebyindex(pEditor->selected_image); - modify_index_deleted_index = pEditor->selected_image; - pEditor->map.modify_image_index(modify_index_deleted); + delete pImg; + pEditor->m_Map.m_lImages.remove_index(pEditor->m_SelectedImage); + gs_ModifyIndexDeletedIndex = pEditor->m_SelectedImage; + pEditor->m_Map.ModifyImageIndex(ModifyIndexDeleted); return 1; } @@ -1896,712 +1809,722 @@ int EDITOR::popup_image(EDITOR *pEditor, CUIRect view) } -void EDITOR::render_images(CUIRect toolbox, CUIRect toolbar, CUIRect view) +void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View) { for(int e = 0; e < 2; e++) // two passes, first embedded, then external { - CUIRect slot; - toolbox.HSplitTop(15.0f, &slot, &toolbox); + CUIRect Slot; + ToolBox.HSplitTop(15.0f, &Slot, &ToolBox); if(e == 0) - UI()->DoLabel(&slot, "Embedded", 12.0f, 0); + UI()->DoLabel(&Slot, "Embedded", 12.0f, 0); else - UI()->DoLabel(&slot, "External", 12.0f, 0); - - for(int i = 0; i < map.images.len(); i++) + UI()->DoLabel(&Slot, "External", 12.0f, 0); + + for(int i = 0; i < m_Map.m_lImages.size(); i++) { - if((e && !map.images[i]->external) || - (!e && map.images[i]->external)) + if((e && !m_Map.m_lImages[i]->m_External) || + (!e && m_Map.m_lImages[i]->m_External)) { continue; } - - char buf[128]; - sprintf(buf, "%s", map.images[i]->name); - toolbox.HSplitTop(12.0f, &slot, &toolbox); - - if(int result = DoButton_Editor(&map.images[i], buf, selected_image == i, &slot, + + char aBuf[128]; + str_copy(aBuf, m_Map.m_lImages[i]->m_aName, sizeof(aBuf)); + ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); + + if(int Result = DoButton_Editor(&m_Map.m_lImages[i], aBuf, m_SelectedImage == i, &Slot, BUTTON_CONTEXT, "Select image")) { - selected_image = i; - - static int popup_image_id = 0; - if(result == 2) - ui_invoke_popup_menu(&popup_image_id, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, popup_image); + m_SelectedImage = i; + + static int s_PopupImageId = 0; + if(Result == 2) + UiInvokePopupMenu(&s_PopupImageId, 0, UI()->MouseX(), UI()->MouseY(), 120, 80, PopupImage); } - - toolbox.HSplitTop(2.0f, 0, &toolbox); - + + ToolBox.HSplitTop(2.0f, 0, &ToolBox); + // render image - if(selected_image == i) + if(m_SelectedImage == i) { CUIRect r; - view.Margin(10.0f, &r); + View.Margin(10.0f, &r); if(r.h < r.w) r.w = r.h; else r.h = r.w; - Graphics()->TextureSet(map.images[i]->tex_id); + Graphics()->TextureSet(m_Map.m_lImages[i]->m_TexId); Graphics()->BlendNormal(); Graphics()->QuadsBegin(); - Graphics()->QuadsDrawTL(r.x, r.y, r.w, r.h); + IGraphics::CQuadItem QuadItem(r.x, r.y, r.w, r.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); - + } } } - - CUIRect slot; - toolbox.HSplitTop(5.0f, &slot, &toolbox); - - // new image - static int new_image_button = 0; - toolbox.HSplitTop(10.0f, &slot, &toolbox); - toolbox.HSplitTop(12.0f, &slot, &toolbox); - if(DoButton_Editor(&new_image_button, "Add", 0, &slot, 0, "Load a new image to use in the map")) - invoke_file_dialog(LISTDIRTYPE_ALL, "Add Image", "Add", "mapres/", "", add_image, this); -} + CUIRect Slot; + ToolBox.HSplitTop(5.0f, &Slot, &ToolBox); -static int file_dialog_dirtypes = 0; -static const char *file_dialog_title = 0; -static const char *file_dialog_button_text = 0; -static void (*file_dialog_func)(const char *filename, void *user); -static void *file_dialog_user = 0; -static char file_dialog_filename[512] = {0}; -static char file_dialog_path[512] = {0}; -static char file_dialog_complete_filename[512] = {0}; -static int files_num = 0; -int files_startat = 0; -int files_cur = 0; -int files_stopat = 999; - -struct LISTDIRINFO -{ - CUIRect *rect; - EDITOR *editor; + // new image + static int s_NewImageButton = 0; + ToolBox.HSplitTop(10.0f, &Slot, &ToolBox); + ToolBox.HSplitTop(12.0f, &Slot, &ToolBox); + if(DoButton_Editor(&s_NewImageButton, "Add", 0, &Slot, 0, "Load a new image to use in the map")) + InvokeFileDialog(IStorage::TYPE_ALL, "Add Image", "Add", "mapres/", "", AddImage, this); +} + + +static int gs_FileDialogDirTypes = 0; +static const char *gs_pFileDialogTitle = 0; +static const char *gs_pFileDialogButtonText = 0; +static void (*gs_pfnFileDialogFunc)(const char *pFileName, void *pUser); +static void *gs_pFileDialogUser = 0; +static char gs_FileDialogFileName[512] = {0}; +static char gs_aFileDialogPath[512] = {0}; +static char gs_aFileDialogCompleteFilename[512] = {0}; +static int gs_FilesNum = 0; +int g_FilesStartAt = 0; +int g_FilesCur = 0; +int g_FilesStopAt = 999; + +struct CListDirInfo +{ + CUIRect *m_pRect; + CEditor *m_pEditor; }; -static void editor_listdir_callback(const char *name, int is_dir, void *user) +static void EditorListdirCallback(const char *pName, int IsDir, void *pUser) { - if(name[0] == '.' || is_dir) // skip this shit! + if(pName[0] == '.' || IsDir) // skip this shit! return; - - if(files_cur > files_num) - files_num = files_cur; - - files_cur++; - if(files_cur-1 < files_startat || files_cur > files_stopat) + + if(g_FilesCur > gs_FilesNum) + gs_FilesNum = g_FilesCur; + + g_FilesCur++; + if(g_FilesCur-1 < g_FilesStartAt || g_FilesCur > g_FilesStopAt) return; - - LISTDIRINFO *info = (LISTDIRINFO *)user; - CUIRect *view = info->rect; - CUIRect button; - view->HSplitTop(15.0f, &button, view); - view->HSplitTop(2.0f, 0, view); + + CListDirInfo *pInfo = (CListDirInfo *)pUser; + CUIRect *pView = pInfo->m_pRect; + CUIRect Button; + pView->HSplitTop(15.0f, &Button, pView); + pView->HSplitTop(2.0f, 0, pView); //char buf[512]; - - if(info->editor->DoButton_File((void*)(10+(int)button.y), name, 0, &button, 0, 0)) + + if(pInfo->m_pEditor->DoButton_File((void*)(10+(int)Button.y), pName, 0, &Button, 0, 0)) { - strncpy(file_dialog_filename, name, sizeof(file_dialog_filename)); - - file_dialog_complete_filename[0] = 0; - strcat(file_dialog_complete_filename, file_dialog_path); - strcat(file_dialog_complete_filename, file_dialog_filename); + str_copy(gs_FileDialogFileName, pName, sizeof(gs_FileDialogFileName)); + + gs_aFileDialogCompleteFilename[0] = 0; + str_append(gs_aFileDialogCompleteFilename, gs_aFileDialogPath, sizeof(gs_aFileDialogCompleteFilename)); + str_append(gs_aFileDialogCompleteFilename, gs_FileDialogFileName, sizeof(gs_aFileDialogCompleteFilename)); - if(inp_mouse_doubleclick()) + if(pInfo->m_pEditor->Input()->MouseDoubleClick()) { - if(file_dialog_func) - file_dialog_func(file_dialog_complete_filename, user); - info->editor->dialog = DIALOG_NONE; + if(gs_pfnFileDialogFunc) + gs_pfnFileDialogFunc(gs_aFileDialogCompleteFilename, pInfo->m_pEditor); + pInfo->m_pEditor->m_Dialog = DIALOG_NONE; } } } -void EDITOR::render_file_dialog() +void CEditor::RenderFileDialog() { // GUI coordsys Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); - - CUIRect view = *UI()->Screen(); - RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.25f), 0, 0); - view.VMargin(150.0f, &view); - view.HMargin(50.0f, &view); - RenderTools()->DrawUIRect(&view, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f); - view.Margin(10.0f, &view); - - CUIRect title, filebox, filebox_label, buttonbar, scroll; - view.HSplitTop(18.0f, &title, &view); - view.HSplitTop(5.0f, 0, &view); // some spacing - view.HSplitBottom(14.0f, &view, &buttonbar); - view.HSplitBottom(10.0f, &view, 0); // some spacing - view.HSplitBottom(14.0f, &view, &filebox); - filebox.VSplitLeft(50.0f, &filebox_label, &filebox); - view.VSplitRight(15.0f, &view, &scroll); - + + CUIRect View = *UI()->Screen(); + RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.25f), 0, 0); + View.VMargin(150.0f, &View); + View.HMargin(50.0f, &View); + RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f); + View.Margin(10.0f, &View); + + CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll; + View.HSplitTop(18.0f, &Title, &View); + View.HSplitTop(5.0f, 0, &View); // some spacing + View.HSplitBottom(14.0f, &View, &ButtonBar); + View.HSplitBottom(10.0f, &View, 0); // some spacing + View.HSplitBottom(14.0f, &View, &FileBox); + FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox); + View.VSplitRight(15.0f, &View, &Scroll); + // title - RenderTools()->DrawUIRect(&title, vec4(1,1,1,0.25f), CUI::CORNER_ALL, 5.0f); - title.VMargin(10.0f, &title); - UI()->DoLabel(&title, file_dialog_title, 14.0f, -1, -1); - + RenderTools()->DrawUIRect(&Title, vec4(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f); + Title.VMargin(10.0f, &Title); + UI()->DoLabel(&Title, gs_pFileDialogTitle, 12.0f, -1, -1); + // filebox - UI()->DoLabel(&filebox_label, "Filename:", 10.0f, -1, -1); - - static int filebox_id = 0; - DoEditBox(&filebox_id, &filebox, file_dialog_filename, sizeof(file_dialog_filename), 10.0f); + static int s_FileBoxId = 0; + UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, -1, -1); + DoEditBox(&s_FileBoxId, &FileBox, gs_FileDialogFileName, sizeof(gs_FileDialogFileName), 10.0f); - file_dialog_complete_filename[0] = 0; - strcat(file_dialog_complete_filename, file_dialog_path); - strcat(file_dialog_complete_filename, file_dialog_filename); - - int num = (int)(view.h/17.0); - static float scrollvalue = 0; - static int scrollbar = 0; - scroll.HMargin(5.0f, &scroll); - scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue); - - int scrollnum = files_num-num+10; - if(scrollnum > 0) + gs_aFileDialogCompleteFilename[0] = 0; + str_append(gs_aFileDialogCompleteFilename, gs_aFileDialogPath, sizeof(gs_aFileDialogCompleteFilename)); + str_append(gs_aFileDialogCompleteFilename, gs_FileDialogFileName, sizeof(gs_aFileDialogCompleteFilename)); + + int Num = (int)(View.h/17.0); + static float s_ScrollValue = 0; + static int ScrollBar = 0; + Scroll.HMargin(5.0f, &Scroll); + s_ScrollValue = UiDoScrollbarV(&ScrollBar, &Scroll, s_ScrollValue); + + int ScrollNum = gs_FilesNum-Num+10; + if(ScrollNum > 0) { - if(inp_key_presses(KEY_MOUSE_WHEEL_UP)) - scrollvalue -= 3.0f/scrollnum; - if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN)) - scrollvalue += 3.0f/scrollnum; - - if(scrollvalue < 0) scrollvalue = 0; - if(scrollvalue > 1) scrollvalue = 1; + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP)) + s_ScrollValue -= 3.0f/ScrollNum; + if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN)) + s_ScrollValue += 3.0f/ScrollNum; + + if(s_ScrollValue < 0) s_ScrollValue = 0; + if(s_ScrollValue > 1) s_ScrollValue = 1; } else - scrollnum = 0; - - files_startat = (int)(scrollnum*scrollvalue); - if(files_startat < 0) - files_startat = 0; - - files_stopat = files_startat+num; - - files_cur = 0; - + ScrollNum = 0; + + g_FilesStartAt = (int)(ScrollNum*s_ScrollValue); + if(g_FilesStartAt < 0) + g_FilesStartAt = 0; + + g_FilesStopAt = g_FilesStartAt+Num; + + g_FilesCur = 0; + // set clipping - UI()->ClipEnable(&view); - + UI()->ClipEnable(&View); + // the list - LISTDIRINFO info; - info.rect = &view; - info.editor = this; - engine_listdir(file_dialog_dirtypes, file_dialog_path, editor_listdir_callback, &info); - + CListDirInfo Info; + Info.m_pRect = &View; + Info.m_pEditor = this; + + // TODO: lazy ass coding, should store the interface pointer somewere + Kernel()->RequestInterface<IStorage>()->ListDirectory(gs_FileDialogDirTypes, gs_aFileDialogPath, EditorListdirCallback, &Info); + // disable clipping again UI()->ClipDisable(); - + // the buttons - static int ok_button = 0; - static int cancel_button = 0; + static int s_OkButton = 0; + static int s_CancelButton = 0; - CUIRect button; - buttonbar.VSplitRight(50.0f, &buttonbar, &button); - if(DoButton_Editor(&ok_button, file_dialog_button_text, 0, &button, 0, 0) || inp_key_pressed(KEY_RETURN)) + CUIRect Button; + ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button); + if(DoButton_Editor(&s_OkButton, gs_pFileDialogButtonText, 0, &Button, 0, 0) || Input()->KeyPressed(KEY_RETURN)) { - if(file_dialog_func) - file_dialog_func(file_dialog_complete_filename, file_dialog_user); - dialog = DIALOG_NONE; + if(gs_pfnFileDialogFunc) + gs_pfnFileDialogFunc(gs_aFileDialogCompleteFilename, gs_pFileDialogUser); + m_Dialog = DIALOG_NONE; } - buttonbar.VSplitRight(40.0f, &buttonbar, &button); - buttonbar.VSplitRight(50.0f, &buttonbar, &button); - if(DoButton_Editor(&cancel_button, "Cancel", 0, &button, 0, 0) || inp_key_pressed(KEY_ESCAPE)) - dialog = DIALOG_NONE; + ButtonBar.VSplitRight(40.0f, &ButtonBar, &Button); + ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button); + if(DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, 0) || Input()->KeyPressed(KEY_ESCAPE)) + m_Dialog = DIALOG_NONE; } -void EDITOR::invoke_file_dialog(int listdirtypes, const char *title, const char *button_text, - const char *basepath, const char *default_name, - void (*func)(const char *filename, void *user), void *user) +void CEditor::InvokeFileDialog(int ListDirTypes, const char *pTitle, const char *pButtonText, + const char *pBasePath, const char *pDefaultName, + void (*pfnFunc)(const char *pFileName, void *pUser), void *pUser) { - file_dialog_dirtypes = listdirtypes; - file_dialog_title = title; - file_dialog_button_text = button_text; - file_dialog_func = func; - file_dialog_user = user; - file_dialog_filename[0] = 0; - file_dialog_path[0] = 0; - - if(default_name) - strncpy(file_dialog_filename, default_name, sizeof(file_dialog_filename)); - if(basepath) - strncpy(file_dialog_path, basepath, sizeof(file_dialog_path)); - - dialog = DIALOG_FILE; + gs_FileDialogDirTypes = ListDirTypes; + gs_pFileDialogTitle = pTitle; + gs_pFileDialogButtonText = pButtonText; + gs_pfnFileDialogFunc = pfnFunc; + gs_pFileDialogUser = pUser; + gs_FileDialogFileName[0] = 0; + gs_aFileDialogPath[0] = 0; + + if(pDefaultName) + str_copy(gs_FileDialogFileName, pDefaultName, sizeof(gs_FileDialogFileName)); + if(pBasePath) + str_copy(gs_aFileDialogPath, pBasePath, sizeof(gs_aFileDialogPath)); + + m_Dialog = DIALOG_FILE; } -void EDITOR::render_modebar(CUIRect view) +void CEditor::RenderModebar(CUIRect View) { - CUIRect button; + CUIRect Button; // mode buttons { - view.VSplitLeft(40.0f, &button, &view); - static int tile_button = 0; - if(DoButton_ButtonM(&tile_button, "Layers", mode == MODE_LAYERS, &button, 0, "Switch to edit layers.")) - mode = MODE_LAYERS; - - view.VSplitLeft(40.0f, &button, &view); - static int img_button = 0; - if(DoButton_ButtonR(&img_button, "Images", mode == MODE_IMAGES, &button, 0, "Switch to manage images.")) - mode = MODE_IMAGES; + View.VSplitLeft(65.0f, &Button, &View); + Button.HSplitTop(30.0f, 0, &Button); + static int s_Button = 0; + const char *pButName = m_Mode == MODE_LAYERS ? "Layers" : "Images"; + if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images and layers managment.")) + { + if(m_Mode == MODE_LAYERS) + m_Mode = MODE_IMAGES; + else + m_Mode = MODE_LAYERS; + } } - view.VSplitLeft(5.0f, 0, &view); - - // spacing - //view.VSplitLeft(10.0f, 0, &view); + View.VSplitLeft(5.0f, 0, &View); } -void EDITOR::render_statusbar(CUIRect view) +void CEditor::RenderStatusbar(CUIRect View) { - CUIRect button; - view.VSplitRight(60.0f, &view, &button); - static int envelope_button = 0; - if(DoButton_Editor(&envelope_button, "Envelopes", show_envelope_editor, &button, 0, "Toggles the envelope editor.")) - show_envelope_editor = (show_envelope_editor+1)%4; - - if(tooltip) + CUIRect Button; + View.VSplitRight(60.0f, &View, &Button); + static int s_EnvelopeButton = 0; + if(DoButton_Editor(&s_EnvelopeButton, "Envelopes", m_ShowEnvelopeEditor, &Button, 0, "Toggles the envelope editor.")) + m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor+1)%4; + + if(m_pTooltip) { - if(ui_got_context && ui_got_context == UI()->HotItem()) + if(ms_pUiGotContext && ms_pUiGotContext == UI()->HotItem()) { - char buf[512]; - sprintf(buf, "%s Right click for context menu.", tooltip); - UI()->DoLabel(&view, buf, 10.0f, -1, -1); + char aBuf[512]; + str_format(aBuf, sizeof(aBuf),"%s Right click for context menu.", m_pTooltip); + UI()->DoLabel(&View, aBuf, 10.0f, -1, -1); } else - UI()->DoLabel(&view, tooltip, 10.0f, -1, -1); + UI()->DoLabel(&View, m_pTooltip, 10.0f, -1, -1); } } -void EDITOR::render_envelopeeditor(CUIRect view) +void CEditor::RenderEnvelopeEditor(CUIRect View) { - if(selected_envelope < 0) selected_envelope = 0; - if(selected_envelope >= map.envelopes.len()) selected_envelope--; + if(m_SelectedEnvelope < 0) m_SelectedEnvelope = 0; + if(m_SelectedEnvelope >= m_Map.m_lEnvelopes.size()) m_SelectedEnvelope--; - ENVELOPE *envelope = 0; - if(selected_envelope >= 0 && selected_envelope < map.envelopes.len()) - envelope = map.envelopes[selected_envelope]; + CEnvelope *pEnvelope = 0; + if(m_SelectedEnvelope >= 0 && m_SelectedEnvelope < m_Map.m_lEnvelopes.size()) + pEnvelope = m_Map.m_lEnvelopes[m_SelectedEnvelope]; - bool show_colorbar = false; - if(envelope && envelope->channels == 4) - show_colorbar = true; + bool ShowColorBar = false; + if(pEnvelope && pEnvelope->m_Channels == 4) + ShowColorBar = true; - CUIRect toolbar, curvebar, colorbar; - view.HSplitTop(15.0f, &toolbar, &view); - view.HSplitTop(15.0f, &curvebar, &view); - toolbar.Margin(2.0f, &toolbar); - curvebar.Margin(2.0f, &curvebar); + CUIRect ToolBar, CurveBar, ColorBar; + View.HSplitTop(15.0f, &ToolBar, &View); + View.HSplitTop(15.0f, &CurveBar, &View); + ToolBar.Margin(2.0f, &ToolBar); + CurveBar.Margin(2.0f, &CurveBar); - if(show_colorbar) + if(ShowColorBar) { - view.HSplitTop(20.0f, &colorbar, &view); - colorbar.Margin(2.0f, &colorbar); - render_background(colorbar, checker_texture, 16.0f, 1.0f); + View.HSplitTop(20.0f, &ColorBar, &View); + ColorBar.Margin(2.0f, &ColorBar); + RenderBackground(ColorBar, ms_CheckerTexture, 16.0f, 1.0f); } - render_background(view, checker_texture, 32.0f, 0.1f); + RenderBackground(View, ms_CheckerTexture, 32.0f, 0.1f); // do the toolbar { - CUIRect button; - ENVELOPE *new_env = 0; - - toolbar.VSplitRight(50.0f, &toolbar, &button); - static int new_4d_button = 0; - if(DoButton_Editor(&new_4d_button, "Color+", 0, &button, 0, "Creates a new color envelope")) - new_env = map.new_envelope(4); - - toolbar.VSplitRight(5.0f, &toolbar, &button); - toolbar.VSplitRight(50.0f, &toolbar, &button); - static int new_2d_button = 0; - if(DoButton_Editor(&new_2d_button, "Pos.+", 0, &button, 0, "Creates a new pos envelope")) - new_env = map.new_envelope(3); - - if(new_env) // add the default points + CUIRect Button; + CEnvelope *pNewEnv = 0; + + ToolBar.VSplitRight(50.0f, &ToolBar, &Button); + static int s_New4dButton = 0; + if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope")) + pNewEnv = m_Map.NewEnvelope(4); + + ToolBar.VSplitRight(5.0f, &ToolBar, &Button); + ToolBar.VSplitRight(50.0f, &ToolBar, &Button); + static int s_New2dButton = 0; + if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new pos envelope")) + pNewEnv = m_Map.NewEnvelope(3); + + if(pNewEnv) // add the default points { - if(new_env->channels == 4) + if(pNewEnv->m_Channels == 4) { - new_env->add_point(0, 1,1,1,1); - new_env->add_point(1000, 1,1,1,1); + pNewEnv->AddPoint(0, 1,1,1,1); + pNewEnv->AddPoint(1000, 1,1,1,1); } else { - new_env->add_point(0, 0); - new_env->add_point(1000, 0); + pNewEnv->AddPoint(0, 0); + pNewEnv->AddPoint(1000, 0); } } - - CUIRect shifter, inc, dec; - toolbar.VSplitLeft(60.0f, &shifter, &toolbar); - shifter.VSplitRight(15.0f, &shifter, &inc); - shifter.VSplitLeft(15.0f, &dec, &shifter); - char buf[512]; - sprintf(buf, "%d/%d", selected_envelope+1, map.envelopes.len()); - RenderTools()->DrawUIRect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); - UI()->DoLabel(&shifter, buf, 10.0f, 0, -1); - - static int prev_button = 0; - if(DoButton_ButtonDec(&prev_button, 0, 0, &dec, 0, "Previous Envelope")) - selected_envelope--; - - static int next_button = 0; - if(DoButton_ButtonInc(&next_button, 0, 0, &inc, 0, "Next Envelope")) - selected_envelope++; - - if(envelope) + + CUIRect Shifter, Inc, Dec; + ToolBar.VSplitLeft(60.0f, &Shifter, &ToolBar); + Shifter.VSplitRight(15.0f, &Shifter, &Inc); + Shifter.VSplitLeft(15.0f, &Dec, &Shifter); + char aBuf[512]; + str_format(aBuf, sizeof(aBuf),"%d/%d", m_SelectedEnvelope+1, m_Map.m_lEnvelopes.size()); + RenderTools()->DrawUIRect(&Shifter, vec4(1,1,1,0.5f), 0, 0.0f); + UI()->DoLabel(&Shifter, aBuf, 10.0f, 0, -1); + + static int s_PrevButton = 0; + if(DoButton_ButtonDec(&s_PrevButton, 0, 0, &Dec, 0, "Previous Envelope")) + m_SelectedEnvelope--; + + static int s_NextButton = 0; + if(DoButton_ButtonInc(&s_NextButton, 0, 0, &Inc, 0, "Next Envelope")) + m_SelectedEnvelope++; + + if(pEnvelope) { - toolbar.VSplitLeft(15.0f, &button, &toolbar); - toolbar.VSplitLeft(35.0f, &button, &toolbar); - UI()->DoLabel(&button, "Name:", 10.0f, -1, -1); - - toolbar.VSplitLeft(80.0f, &button, &toolbar); - - static int name_box = 0; - DoEditBox(&name_box, &button, envelope->name, sizeof(envelope->name), 10.0f); + ToolBar.VSplitLeft(15.0f, &Button, &ToolBar); + ToolBar.VSplitLeft(35.0f, &Button, &ToolBar); + UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1); + + ToolBar.VSplitLeft(80.0f, &Button, &ToolBar); + + static int s_NameBox = 0; + DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f); } } - - if(envelope) + + if(pEnvelope) { - static array<int> selection; - static int envelope_editor_id = 0; - static int active_channels = 0xf; - - if(envelope) + static array<int> Selection; + static int sEnvelopeEditorId = 0; + static int s_ActiveChannels = 0xf; + + if(pEnvelope) { - CUIRect button; - - toolbar.VSplitLeft(15.0f, &button, &toolbar); + CUIRect Button; + + ToolBar.VSplitLeft(15.0f, &Button, &ToolBar); - static const char *names[4][4] = { + static const char *s_paNames[4][4] = { {"X", "", "", ""}, {"X", "Y", "", ""}, {"X", "Y", "R", ""}, {"R", "G", "B", "A"}, }; - - static int channel_buttons[4] = {0}; - int bit = 1; - /*ui_draw_button_func draw_func;*/ - - for(int i = 0; i < envelope->channels; i++, bit<<=1) + + static int s_aChannelButtons[4] = {0}; + int Bit = 1; + //ui_draw_button_func draw_func; + + for(int i = 0; i < pEnvelope->m_Channels; i++, Bit<<=1) { - toolbar.VSplitLeft(15.0f, &button, &toolbar); - + ToolBar.VSplitLeft(15.0f, &Button, &ToolBar); + /*if(i == 0) draw_func = draw_editor_button_l; else if(i == envelope->channels-1) draw_func = draw_editor_button_r; else draw_func = draw_editor_button_m;*/ - - if(DoButton_Editor(&channel_buttons[i], names[envelope->channels-1][i], active_channels&bit, &button, 0, 0)) - active_channels ^= bit; + + if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-1][i], s_ActiveChannels&Bit, &Button, 0, 0)) + s_ActiveChannels ^= Bit; } - } - - float end_time = envelope->end_time(); - if(end_time < 1) - end_time = 1; - - envelope->find_top_bottom(active_channels); - float top = envelope->top; - float bottom = envelope->bottom; - - if(top < 1) - top = 1; - if(bottom >= 0) - bottom = 0; - - float timescale = end_time/view.w; - float valuescale = (top-bottom)/view.h; - - if(UI()->MouseInside(&view)) - UI()->SetHotItem(&envelope_editor_id); - - if(UI()->HotItem() == &envelope_editor_id) + } + + float EndTime = pEnvelope->EndTime(); + if(EndTime < 1) + EndTime = 1; + + pEnvelope->FindTopBottom(s_ActiveChannels); + float Top = pEnvelope->m_Top; + float Bottom = pEnvelope->m_Bottom; + + if(Top < 1) + Top = 1; + if(Bottom >= 0) + Bottom = 0; + + float TimeScale = EndTime/View.w; + float ValueScale = (Top-Bottom)/View.h; + + if(UI()->MouseInside(&View)) + UI()->SetHotItem(&sEnvelopeEditorId); + + if(UI()->HotItem() == &sEnvelopeEditorId) { // do stuff - if(envelope) + if(pEnvelope) { if(UI()->MouseButtonClicked(1)) { // add point - int time = (int)(((UI()->MouseX()-view.x)*timescale)*1000.0f); - //float env_y = (UI()->MouseY()-view.y)/timescale; - float channels[4]; - envelope->eval(time, channels); - envelope->add_point(time, - f2fx(channels[0]), f2fx(channels[1]), - f2fx(channels[2]), f2fx(channels[3])); + int Time = (int)(((UI()->MouseX()-View.x)*TimeScale)*1000.0f); + //float env_y = (UI()->MouseY()-view.y)/TimeScale; + float aChannels[4]; + pEnvelope->Eval(Time, aChannels); + pEnvelope->AddPoint(Time, + f2fx(aChannels[0]), f2fx(aChannels[1]), + f2fx(aChannels[2]), f2fx(aChannels[3])); } - - tooltip = "Press right mouse button to create a new point"; + + m_pTooltip = "Press right mouse button to create a new point"; } } - vec3 colors[] = {vec3(1,0.2f,0.2f), vec3(0.2f,1,0.2f), vec3(0.2f,0.2f,1), vec3(1,1,0.2f)}; + vec3 aColors[] = {vec3(1,0.2f,0.2f), vec3(0.2f,1,0.2f), vec3(0.2f,0.2f,1), vec3(1,1,0.2f)}; // render lines { - UI()->ClipEnable(&view); + UI()->ClipEnable(&View); Graphics()->TextureSet(-1); Graphics()->LinesBegin(); - for(int c = 0; c < envelope->channels; c++) + for(int c = 0; c < pEnvelope->m_Channels; c++) { - if(active_channels&(1<<c)) - Graphics()->SetColor(colors[c].r,colors[c].g,colors[c].b,1); + if(s_ActiveChannels&(1<<c)) + Graphics()->SetColor(aColors[c].r,aColors[c].g,aColors[c].b,1); else - Graphics()->SetColor(colors[c].r*0.5f,colors[c].g*0.5f,colors[c].b*0.5f,1); - - float prev_x = 0; - float results[4]; - envelope->eval(0.000001f, results); - float prev_value = results[c]; - - int steps = (int)((view.w/UI()->Screen()->w) * Graphics()->ScreenWidth()); - for(int i = 1; i <= steps; i++) + Graphics()->SetColor(aColors[c].r*0.5f,aColors[c].g*0.5f,aColors[c].b*0.5f,1); + + float PrevX = 0; + float aResults[4]; + pEnvelope->Eval(0.000001f, aResults); + float PrevValue = aResults[c]; + + int Steps = (int)((View.w/UI()->Screen()->w) * Graphics()->ScreenWidth()); + for(int i = 1; i <= Steps; i++) { - float a = i/(float)steps; - envelope->eval(a*end_time, results); - float v = results[c]; - v = (v-bottom)/(top-bottom); - - Graphics()->LinesDraw(view.x + prev_x*view.w, view.y+view.h - prev_value*view.h, view.x + a*view.w, view.y+view.h - v*view.h); - prev_x = a; - prev_value = v; + float a = i/(float)Steps; + pEnvelope->Eval(a*EndTime, aResults); + float v = aResults[c]; + v = (v-Bottom)/(Top-Bottom); + + IGraphics::CLineItem LineItem(View.x + PrevX*View.w, View.y+View.h - PrevValue*View.h, View.x + a*View.w, View.y+View.h - v*View.h); + Graphics()->LinesDraw(&LineItem, 1); + PrevX = a; + PrevValue = v; } } Graphics()->LinesEnd(); UI()->ClipDisable(); } - + // render curve options { - for(int i = 0; i < envelope->points.len()-1; i++) + for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++) { - float t0 = envelope->points[i].time/1000.0f/end_time; - float t1 = envelope->points[i+1].time/1000.0f/end_time; + float t0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime; + float t1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime; //dbg_msg("", "%f", end_time); - + CUIRect v; - v.x = curvebar.x + (t0+(t1-t0)*0.5f) * curvebar.w; - v.y = curvebar.y; - v.h = curvebar.h; - v.w = curvebar.h; + v.x = CurveBar.x + (t0+(t1-t0)*0.5f) * CurveBar.w; + v.y = CurveBar.y; + v.h = CurveBar.h; + v.w = CurveBar.h; v.x -= v.w/2; - void *id = &envelope->points[i].curvetype; - const char *type_name[] = { + void *pId = &pEnvelope->m_lPoints[i].m_Curvetype; + const char *paTypeName[] = { "N", "L", "S", "F", "M" }; - - if(DoButton_Editor(id, type_name[envelope->points[i].curvetype], 0, &v, 0, "Switch curve type")) - envelope->points[i].curvetype = (envelope->points[i].curvetype+1)%NUM_CURVETYPES; + + if(DoButton_Editor(pId, paTypeName[pEnvelope->m_lPoints[i].m_Curvetype], 0, &v, 0, "Switch curve type")) + pEnvelope->m_lPoints[i].m_Curvetype = (pEnvelope->m_lPoints[i].m_Curvetype+1)%NUM_CURVETYPES; } } - + // render colorbar - if(show_colorbar) + if(ShowColorBar) { Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); - for(int i = 0; i < envelope->points.len()-1; i++) + for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++) { - float r0 = fx2f(envelope->points[i].values[0]); - float g0 = fx2f(envelope->points[i].values[1]); - float b0 = fx2f(envelope->points[i].values[2]); - float a0 = fx2f(envelope->points[i].values[3]); - float r1 = fx2f(envelope->points[i+1].values[0]); - float g1 = fx2f(envelope->points[i+1].values[1]); - float b1 = fx2f(envelope->points[i+1].values[2]); - float a1 = fx2f(envelope->points[i+1].values[3]); - - Graphics()->SetColorVertex(0, r0, g0, b0, a0); - Graphics()->SetColorVertex(1, r1, g1, b1, a1); - Graphics()->SetColorVertex(2, r1, g1, b1, a1); - Graphics()->SetColorVertex(3, r0, g0, b0, a0); - - float x0 = envelope->points[i].time/1000.0f/end_time; + float r0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[0]); + float g0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]); + float b0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[2]); + float a0 = fx2f(pEnvelope->m_lPoints[i].m_aValues[3]); + float r1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]); + float g1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]); + float b1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[2]); + float a1 = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[3]); + + IGraphics::CColorVertex Array[4] = {IGraphics::CColorVertex(0, r0, g0, b0, a0), + IGraphics::CColorVertex(1, r1, g1, b1, a1), + IGraphics::CColorVertex(2, r1, g1, b1, a1), + IGraphics::CColorVertex(3, r0, g0, b0, a0)}; + Graphics()->SetColorVertex(Array, 4); + + float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime; // float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); - float x1 = envelope->points[i+1].time/1000.0f/end_time; + float x1 = pEnvelope->m_lPoints[i+1].m_Time/1000.0f/EndTime; //float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom); CUIRect v; - v.x = colorbar.x + x0*colorbar.w; - v.y = colorbar.y; - v.w = (x1-x0)*colorbar.w; - v.h = colorbar.h; - - Graphics()->QuadsDrawTL(v.x, v.y, v.w, v.h); + v.x = ColorBar.x + x0*ColorBar.w; + v.y = ColorBar.y; + v.w = (x1-x0)*ColorBar.w; + v.h = ColorBar.h; + + IGraphics::CQuadItem QuadItem(v.x, v.y, v.w, v.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); } Graphics()->QuadsEnd(); } - + // render handles { - static bool move = false; - - int current_value = 0, current_time = 0; - + static bool s_Move = false; + + int CurrentValue = 0, CurrentTime = 0; + Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); - for(int c = 0; c < envelope->channels; c++) + for(int c = 0; c < pEnvelope->m_Channels; c++) { - if(!(active_channels&(1<<c))) + if(!(s_ActiveChannels&(1<<c))) continue; - - for(int i = 0; i < envelope->points.len(); i++) + + for(int i = 0; i < pEnvelope->m_lPoints.size(); i++) { - float x0 = envelope->points[i].time/1000.0f/end_time; - float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); - CUIRect final; - final.x = view.x + x0*view.w; - final.y = view.y+view.h - y0*view.h; - final.x -= 2.0f; - final.y -= 2.0f; - final.w = 4.0f; - final.h = 4.0f; - - void *id = &envelope->points[i].values[c]; - - if(UI()->MouseInside(&final)) - UI()->SetHotItem(id); - - float colormod = 1.0f; + float x0 = pEnvelope->m_lPoints[i].m_Time/1000.0f/EndTime; + float y0 = (fx2f(pEnvelope->m_lPoints[i].m_aValues[c])-Bottom)/(Top-Bottom); + CUIRect Final; + Final.x = View.x + x0*View.w; + Final.y = View.y+View.h - y0*View.h; + Final.x -= 2.0f; + Final.y -= 2.0f; + Final.w = 4.0f; + Final.h = 4.0f; - if(UI()->ActiveItem() == id) + void *pId = &pEnvelope->m_lPoints[i].m_aValues[c]; + + if(UI()->MouseInside(&Final)) + UI()->SetHotItem(pId); + + float ColorMod = 1.0f; + + if(UI()->ActiveItem() == pId) { if(!UI()->MouseButton(0)) { UI()->SetActiveItem(0); - move = false; + s_Move = false; } else { - envelope->points[i].values[c] -= f2fx(mouse_delta_y*valuescale); - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*ValueScale); + if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) { if(i != 0) { - envelope->points[i].time += (int)((mouse_delta_x*timescale)*1000.0f); - if(envelope->points[i].time < envelope->points[i-1].time) - envelope->points[i].time = envelope->points[i-1].time + 1; - if(i+1 != envelope->points.len() && envelope->points[i].time > envelope->points[i+1].time) - envelope->points[i].time = envelope->points[i+1].time - 1; + pEnvelope->m_lPoints[i].m_Time += (int)((m_MouseDeltaX*TimeScale)*1000.0f); + if(pEnvelope->m_lPoints[i].m_Time < pEnvelope->m_lPoints[i-1].m_Time) + pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i-1].m_Time + 1; + if(i+1 != pEnvelope->m_lPoints.size() && pEnvelope->m_lPoints[i].m_Time > pEnvelope->m_lPoints[i+1].m_Time) + pEnvelope->m_lPoints[i].m_Time = pEnvelope->m_lPoints[i+1].m_Time - 1; } } } - - colormod = 100.0f; + + ColorMod = 100.0f; Graphics()->SetColor(1,1,1,1); } - else if(UI()->HotItem() == id) + else if(UI()->HotItem() == pId) { if(UI()->MouseButton(0)) { - selection.clear(); - selection.add(i); - UI()->SetActiveItem(id); + Selection.clear(); + Selection.add(i); + UI()->SetActiveItem(pId); } // remove point if(UI()->MouseButtonClicked(1)) - envelope->points.removebyindex(i); - - colormod = 100.0f; + pEnvelope->m_lPoints.remove_index(i); + + ColorMod = 100.0f; Graphics()->SetColor(1,0.75f,0.75f,1); - tooltip = "Left mouse to drag. Hold shift to alter time point aswell. Right click to delete."; + m_pTooltip = "Left mouse to drag. Hold shift to alter time point aswell. Right click to delete."; } - if(UI()->ActiveItem() == id || UI()->HotItem() == id) + if(UI()->ActiveItem() == pId || UI()->HotItem() == pId) { - current_time = envelope->points[i].time; - current_value = envelope->points[i].values[c]; + CurrentTime = pEnvelope->m_lPoints[i].m_Time; + CurrentValue = pEnvelope->m_lPoints[i].m_aValues[c]; } - - Graphics()->SetColor(colors[c].r*colormod, colors[c].g*colormod, colors[c].b*colormod, 1.0f); - Graphics()->QuadsDrawTL(final.x, final.y, final.w, final.h); + + Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f); + IGraphics::CQuadItem QuadItem(Final.x, Final.y, Final.w, Final.h); + Graphics()->QuadsDrawTL(&QuadItem, 1); } } Graphics()->QuadsEnd(); - char buf[512]; - sprintf(buf, "%.3f %.3f", current_time/1000.0f, fx2f(current_value)); - UI()->DoLabel(&toolbar, buf, 10.0f, 0, -1); + char aBuf[512]; + str_format(aBuf, sizeof(aBuf),"%.3f %.3f", CurrentTime/1000.0f, fx2f(CurrentValue)); + UI()->DoLabel(&ToolBar, aBuf, 10.0f, 0, -1); } } } -int EDITOR::popup_menu_file(EDITOR *pEditor, CUIRect view) +int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View) { - static int new_map_button = 0; - static int save_button = 0; - static int save_as_button = 0; - static int open_button = 0; - static int append_button = 0; - static int exit_button = 0; - - CUIRect slot; - view.HSplitTop(2.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&new_map_button, "New", 0, &slot, 0, "Creates a new map")) - { - pEditor->reset(); + static int s_NewMapButton = 0; + static int s_SaveButton = 0; + static int s_SaveAsButton = 0; + static int s_OpenButton = 0; + static int s_AppendButton = 0; + static int s_ExitButton = 0; + + CUIRect Slot; + View.HSplitTop(2.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_NewMapButton, "New", 0, &Slot, 0, "Creates a new map")) + { + pEditor->Reset(); + pEditor->m_aFileName[0] = 0; return 1; } - view.HSplitTop(10.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&open_button, "Open", 0, &slot, 0, "Opens a map for editing")) + View.HSplitTop(10.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_OpenButton, "Open", 0, &Slot, 0, "Opens a map for editing")) { - pEditor->invoke_file_dialog(LISTDIRTYPE_ALL, "Open Map", "Open", "maps/", "", callback_open_map, pEditor); + pEditor->InvokeFileDialog(IStorage::TYPE_ALL, "Open Map", "Open", "maps/", "", CallbackOpenMap, pEditor); return 1; } - view.HSplitTop(10.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&append_button, "Append", 0, &slot, 0, "Opens a map and adds everything from that map to the current one")) + View.HSplitTop(10.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_AppendButton, "Append", 0, &Slot, 0, "Opens a map and adds everything from that map to the current one")) { - pEditor->invoke_file_dialog(LISTDIRTYPE_ALL, "Append Map", "Append", "maps/", "", callback_append_map, pEditor); + pEditor->InvokeFileDialog(IStorage::TYPE_ALL, "Append Map", "Append", "maps/", "", CallbackAppendMap, pEditor); return 1; } - view.HSplitTop(10.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&save_button, "Save (NOT IMPL)", 0, &slot, 0, "Saves the current map")) + View.HSplitTop(10.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_SaveButton, "Save", 0, &Slot, 0, "Saves the current map")) { + if(pEditor->m_aFileName[0]) + pEditor->Save(pEditor->m_aFileName); + else + pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, "Save Map", "Save", "maps/", "", CallbackSaveMap, pEditor); return 1; } - view.HSplitTop(2.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&save_as_button, "Save As", 0, &slot, 0, "Saves the current map under a new name")) + View.HSplitTop(2.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name")) { - pEditor->invoke_file_dialog(LISTDIRTYPE_SAVE, "Save Map", "Save", "maps/", "", callback_save_map, pEditor); + pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, "Save Map", "Save", "maps/", "", CallbackSaveMap, pEditor); return 1; } - - view.HSplitTop(10.0f, &slot, &view); - view.HSplitTop(12.0f, &slot, &view); - if(pEditor->DoButton_MenuItem(&exit_button, "Exit", 0, &slot, 0, "Exits from the editor")) + + View.HSplitTop(10.0f, &Slot, &View); + View.HSplitTop(12.0f, &Slot, &View); + if(pEditor->DoButton_MenuItem(&s_ExitButton, "Exit", 0, &Slot, 0, "Exits from the editor")) { - config.cl_editor = 0; + g_Config.m_ClEditor = 0; return 1; - } - + } + return 0; } -void EDITOR::render_menubar(CUIRect menubar) +void CEditor::RenderMenubar(CUIRect MenuBar) { - static CUIRect file /*, view, help*/; + static CUIRect s_File /*, view, help*/; + + MenuBar.VSplitLeft(60.0f, &s_File, &MenuBar); + if(DoButton_Menu(&s_File, "File", 0, &s_File, 0, 0)) + UiInvokePopupMenu(&s_File, 1, s_File.x, s_File.y+s_File.h-1.0f, 120, 150, PopupMenuFile, this); - menubar.VSplitLeft(60.0f, &file, &menubar); - if(DoButton_Menu(&file, "File", 0, &file, 0, 0)) - ui_invoke_popup_menu(&file, 1, file.x, file.y+file.h-1.0f, 120, 150, popup_menu_file, this); - /* menubar.VSplitLeft(5.0f, 0, &menubar); menubar.VSplitLeft(60.0f, &view, &menubar); @@ -2615,288 +2538,320 @@ void EDITOR::render_menubar(CUIRect menubar) */ } -void EDITOR::render() +void CEditor::Render() { // basic start - Graphics()->Clear(1.0f,0.0f,1.0f); - CUIRect view = *UI()->Screen(); + Graphics()->Clear(1.0f, 0.0f, 1.0f); + CUIRect View = *UI()->Screen(); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); - + // reset tip - tooltip = 0; - + m_pTooltip = 0; + // render checker - render_background(view, checker_texture, 32.0f, 1.0f); - - CUIRect menubar, modebar, toolbar, statusbar, envelope_editor, toolbox; - - if(gui_active) + RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f); + + CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox; + + if(m_GuiActive) { - - view.HSplitTop(16.0f, &menubar, &view); - view.VSplitLeft(80.0f, &toolbox, &view); - view.HSplitTop(16.0f, &toolbar, &view); - view.HSplitBottom(16.0f, &view, &statusbar); - if(show_envelope_editor) + View.HSplitTop(16.0f, &MenuBar, &View); + View.HSplitTop(53.0f, &ToolBar, &View); + View.VSplitLeft(80.0f, &ToolBox, &View); + View.HSplitBottom(16.0f, &View, &StatusBar); + + if(m_ShowEnvelopeEditor) { float size = 125.0f; - if(show_envelope_editor == 2) + if(m_ShowEnvelopeEditor == 2) size *= 2.0f; - else if(show_envelope_editor == 3) + else if(m_ShowEnvelopeEditor == 3) size *= 3.0f; - view.HSplitBottom(size, &view, &envelope_editor); + View.HSplitBottom(size, &View, &EnvelopeEditor); } } - + // a little hack for now - if(mode == MODE_LAYERS) - do_map_editor(view, toolbar); - - if(gui_active) + if(m_Mode == MODE_LAYERS) + DoMapEditor(View, ToolBar); + + if(m_GuiActive) { - float brightness = 0.25f; - render_background(menubar, background_texture, 128.0f, brightness*0); - menubar.Margin(2.0f, &menubar); + float Brightness = 0.25f; + RenderBackground(MenuBar, ms_BackgroundTexture, 128.0f, Brightness*0); + MenuBar.Margin(2.0f, &MenuBar); - render_background(toolbox, background_texture, 128.0f, brightness); - toolbox.Margin(2.0f, &toolbox); - - render_background(toolbar, background_texture, 128.0f, brightness); - toolbar.Margin(2.0f, &toolbar); - toolbar.VSplitLeft(150.0f, &modebar, &toolbar); + RenderBackground(ToolBox, ms_BackgroundTexture, 128.0f, Brightness); + ToolBox.Margin(2.0f, &ToolBox); + + RenderBackground(ToolBar, ms_BackgroundTexture, 128.0f, Brightness); + ToolBar.Margin(2.0f, &ToolBar); + ToolBar.VSplitLeft(100.0f, &CModeBar, &ToolBar); + + RenderBackground(StatusBar, ms_BackgroundTexture, 128.0f, Brightness); + StatusBar.Margin(2.0f, &StatusBar); - render_background(statusbar, background_texture, 128.0f, brightness); - statusbar.Margin(2.0f, &statusbar); - // do the toolbar - if(mode == MODE_LAYERS) - do_toolbar(toolbar); - - if(show_envelope_editor) + if(m_Mode == MODE_LAYERS) + DoToolbar(ToolBar); + + if(m_ShowEnvelopeEditor) { - render_background(envelope_editor, background_texture, 128.0f, brightness); - envelope_editor.Margin(2.0f, &envelope_editor); + RenderBackground(EnvelopeEditor, ms_BackgroundTexture, 128.0f, Brightness); + EnvelopeEditor.Margin(2.0f, &EnvelopeEditor); } } - - - if(mode == MODE_LAYERS) - render_layers(toolbox, toolbar, view); - else if(mode == MODE_IMAGES) - render_images(toolbox, toolbar, view); + + + if(m_Mode == MODE_LAYERS) + RenderLayers(ToolBox, ToolBar, View); + else if(m_Mode == MODE_IMAGES) + RenderImages(ToolBox, ToolBar, View); Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); - if(gui_active) + if(m_GuiActive) { - render_menubar(menubar); - - render_modebar(modebar); - if(show_envelope_editor) - render_envelopeeditor(envelope_editor); + RenderMenubar(MenuBar); + + RenderModebar(CModeBar); + if(m_ShowEnvelopeEditor) + RenderEnvelopeEditor(EnvelopeEditor); } - if(dialog == DIALOG_FILE) + if(m_Dialog == DIALOG_FILE) { - static int null_ui_target = 0; - UI()->SetHotItem(&null_ui_target); - render_file_dialog(); + static int s_NullUiTarget = 0; + UI()->SetHotItem(&s_NullUiTarget); + RenderFileDialog(); } - - - ui_do_popup_menu(); - if(gui_active) - render_statusbar(statusbar); + + UiDoPopupMenu(); + + if(m_GuiActive) + RenderStatusbar(StatusBar); // - if(config.ed_showkeys) + if(g_Config.m_EdShowkeys) { Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h); - TEXT_CURSOR cursor; - gfx_text_set_cursor(&cursor, view.x+10, view.y+view.h-24-10, 24.0f, TEXTFLAG_RENDER); - - int nkeys = 0; + CTextCursor Cursor; + TextRender()->SetCursor(&Cursor, View.x+10, View.y+View.h-24-10, 24.0f, TEXTFLAG_RENDER); + + int NKeys = 0; for(int i = 0; i < KEY_LAST; i++) { - if(inp_key_pressed(i)) + if(Input()->KeyPressed(i)) { - if(nkeys) - gfx_text_ex(&cursor, " + ", -1); - gfx_text_ex(&cursor, inp_key_name(i), -1); - nkeys++; + if(NKeys) + TextRender()->TextEx(&Cursor, " + ", -1); + TextRender()->TextEx(&Cursor, Input()->KeyName(i), -1); + NKeys++; } } } - - if(show_mouse_pointer) + + if(m_ShowMousePointer) { // render butt ugly mouse cursor float mx = UI()->MouseX(); float my = UI()->MouseY(); - Graphics()->TextureSet(cursor_texture); + Graphics()->TextureSet(ms_CursorTexture); Graphics()->QuadsBegin(); - if(ui_got_context == UI()->HotItem()) + if(ms_pUiGotContext == UI()->HotItem()) Graphics()->SetColor(1,0,0,1); - Graphics()->QuadsDrawTL(mx,my, 16.0f, 16.0f); + IGraphics::CQuadItem QuadItem(mx,my, 16.0f, 16.0f); + Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } - + } -void EDITOR::reset(bool create_default) +void CEditor::Reset(bool CreateDefault) { - map.clean(); + m_Map.Clean(); // create default layers - if(create_default) - map.create_default(entities_texture); - + if(CreateDefault) + m_Map.CreateDefault(ms_EntitiesTexture); + /* { }*/ - - selected_layer = 0; - selected_group = 0; - selected_quad = -1; - selected_points = 0; - selected_envelope = 0; - selected_image = 0; + + m_SelectedLayer = 0; + m_SelectedGroup = 0; + m_SelectedQuad = -1; + m_SelectedPoints = 0; + m_SelectedEnvelope = 0; + m_SelectedImage = 0; } -void MAP::make_game_layer(LAYER *layer) +void CEditorMap::MakeGameLayer(CLayer *pLayer) { - game_layer = (LAYER_GAME *)layer; - game_layer->tex_id = entities_texture; + m_pGameLayer = (CLayerGame *)pLayer; + m_pGameLayer->m_pEditor = m_pEditor; + m_pGameLayer->m_TexId = m_pEditor->ms_EntitiesTexture; } -void MAP::make_game_group(LAYERGROUP *group) +void CEditorMap::MakeGameGroup(CLayerGroup *pGroup) { - game_group = group; - game_group->game_group = true; - game_group->name = "Game"; + m_pGameGroup = pGroup; + m_pGameGroup->m_GameGroup = true; + m_pGameGroup->m_pName = "Game"; } -void MAP::clean() +void CEditorMap::Clean() { - groups.deleteall(); - envelopes.deleteall(); - images.deleteall(); - - game_layer = 0x0; - game_group = 0x0; + m_lGroups.delete_all(); + m_lEnvelopes.delete_all(); + m_lImages.delete_all(); + + m_pGameLayer = 0x0; + m_pGameGroup = 0x0; } -void MAP::create_default(int entities_texture) +void CEditorMap::CreateDefault(int EntitiesTexture) { - make_game_group(new_group()); - make_game_layer(new LAYER_GAME(50, 50)); - game_group->add_layer(game_layer); + MakeGameGroup(NewGroup()); + MakeGameLayer(new CLayerGame(50, 50)); + m_pGameGroup->AddLayer(m_pGameLayer); } -void EDITOR::Init(class IGraphics *pGraphics) +void CEditor::Init() { - m_pGraphics = pGraphics; - - checker_texture = Graphics()->LoadTexture("editor/checker.png", IMG_AUTO, 0); - background_texture = Graphics()->LoadTexture("editor/background.png", IMG_AUTO, 0); - cursor_texture = Graphics()->LoadTexture("editor/cursor.png", IMG_AUTO, 0); - entities_texture = Graphics()->LoadTexture("editor/entities.png", IMG_AUTO, 0); - - tileset_picker.make_palette(); - tileset_picker.readonly = true; - - reset(); + m_pInput = Kernel()->RequestInterface<IInput>(); + m_pClient = Kernel()->RequestInterface<IClient>(); + m_pGraphics = Kernel()->RequestInterface<IGraphics>(); + m_pTextRender = Kernel()->RequestInterface<ITextRender>(); + m_RenderTools.m_pGraphics = m_pGraphics; + m_RenderTools.m_pUI = &m_UI; + m_UI.SetGraphics(m_pGraphics, m_pTextRender); + m_Map.m_pEditor = this; + + ms_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", CImageInfo::FORMAT_AUTO, 0); + ms_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", CImageInfo::FORMAT_AUTO, 0); + ms_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", CImageInfo::FORMAT_AUTO, 0); + ms_EntitiesTexture = Graphics()->LoadTexture("editor/entities.png", CImageInfo::FORMAT_AUTO, 0); + + m_TilesetPicker.m_pEditor = this; + m_TilesetPicker.MakePalette(); + m_TilesetPicker.m_Readonly = true; + + m_Brush.m_pMap = &m_Map; + + Reset(); } -void EDITOR::UpdateAndRender() +void CEditor::DoMapBorder() { - static int mouse_x = 0; - static int mouse_y = 0; - - if(animate) - animate_time = (time_get()-animate_start)/(float)time_freq(); + CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES); + + for(int i = 0; i < pT->m_Width*2; ++i) + pT->m_pTiles[i].m_Index = 1; + + for(int i = 0; i < pT->m_Width*pT->m_Height; ++i) + { + if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3) + pT->m_pTiles[i].m_Index = 1; + } + + for(int i = ((pT->m_Width-2)*pT->m_Height); i < pT->m_Width*pT->m_Height; ++i) + pT->m_pTiles[i].m_Index = 1; +} + +void CEditor::UpdateAndRender() +{ + static int s_MouseX = 0; + static int s_MouseY = 0; + + if(m_Animate) + m_AnimateTime = (time_get()-m_AnimateStart)/(float)time_freq(); else - animate_time = 0; - ui_got_context = 0; + m_AnimateTime = 0; + ms_pUiGotContext = 0; // handle mouse movement - float mx, my, mwx, mwy; + float mx, my, Mwx, Mwy; int rx, ry; { - inp_mouse_relative(&rx, &ry); - mouse_delta_x = rx; - mouse_delta_y = ry; - - if(!lock_mouse) + Input()->MouseRelative(&rx, &ry); + m_MouseDeltaX = rx; + m_MouseDeltaY = ry; + + if(!m_LockMouse) { - mouse_x += rx; - mouse_y += ry; + s_MouseX += rx; + s_MouseY += ry; } - - if(mouse_x < 0) mouse_x = 0; - if(mouse_y < 0) mouse_y = 0; - if(mouse_x > UI()->Screen()->w) mouse_x = (int)UI()->Screen()->w; - if(mouse_y > UI()->Screen()->h) mouse_y = (int)UI()->Screen()->h; + + if(s_MouseX < 0) s_MouseX = 0; + if(s_MouseY < 0) s_MouseY = 0; + if(s_MouseX > UI()->Screen()->w) s_MouseX = (int)UI()->Screen()->w; + if(s_MouseY > UI()->Screen()->h) s_MouseY = (int)UI()->Screen()->h; // update the ui - mx = mouse_x; - my = mouse_y; - mwx = 0; - mwy = 0; - + mx = s_MouseX; + my = s_MouseY; + Mwx = 0; + Mwy = 0; + // fix correct world x and y - LAYERGROUP *g = get_selected_group(); + CLayerGroup *g = GetSelectedGroup(); if(g) { - float points[4]; - g->mapping(points); - - float world_width = points[2]-points[0]; - float world_height = points[3]-points[1]; - - mwx = points[0] + world_width * (mouse_x/UI()->Screen()->w); - mwy = points[1] + world_height * (mouse_y/UI()->Screen()->h); - mouse_delta_wx = mouse_delta_x*(world_width / UI()->Screen()->w); - mouse_delta_wy = mouse_delta_y*(world_height / UI()->Screen()->h); + float aPoints[4]; + g->Mapping(aPoints); + + float WorldWidth = aPoints[2]-aPoints[0]; + float WorldHeight = aPoints[3]-aPoints[1]; + + Mwx = aPoints[0] + WorldWidth * (s_MouseX/UI()->Screen()->w); + Mwy = aPoints[1] + WorldHeight * (s_MouseY/UI()->Screen()->h); + m_MouseDeltaWx = m_MouseDeltaX*(WorldWidth / UI()->Screen()->w); + m_MouseDeltaWy = m_MouseDeltaY*(WorldHeight / UI()->Screen()->h); } - - int buttons = 0; - if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; - if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; - if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; - - UI()->Update(mx,my,mwx,mwy,buttons); + + int Buttons = 0; + if(Input()->KeyPressed(KEY_MOUSE_1)) Buttons |= 1; + if(Input()->KeyPressed(KEY_MOUSE_2)) Buttons |= 2; + if(Input()->KeyPressed(KEY_MOUSE_3)) Buttons |= 4; + + UI()->Update(mx,my,Mwx,Mwy,Buttons); } - + // toggle gui - if(inp_key_down(KEY_TAB)) - gui_active = !gui_active; + if(Input()->KeyDown(KEY_TAB)) + m_GuiActive = !m_GuiActive; - if(inp_key_down(KEY_F5)) - save("maps/debug_test2.map"); + if(Input()->KeyDown(KEY_F5)) + Save("maps/debug_test2.map"); - if(inp_key_down(KEY_F6)) - load("maps/debug_test2.map"); - - if(inp_key_down(KEY_F8)) - load("maps/debug_test.map"); + if(Input()->KeyDown(KEY_F6)) + Load("maps/debug_test2.map"); - if(inp_key_down(KEY_F10)) - show_mouse_pointer = false; + if(Input()->KeyDown(KEY_F8)) + Load("maps/debug_test.map"); - render(); - - if(inp_key_down(KEY_F10)) + if(Input()->KeyDown(KEY_F7)) + Save("maps/quicksave.map"); + + if(Input()->KeyDown(KEY_F10)) + m_ShowMousePointer = false; + + Render(); + + if(Input()->KeyDown(KEY_F10)) { Graphics()->TakeScreenshot(); - show_mouse_pointer = true; + m_ShowMousePointer = true; } - - inp_clear_events(); + + Input()->ClearEvents(); } -IEditor *CreateEditor() { return new EDITOR; } +IEditor *CreateEditor() { return new CEditor; } diff --git a/src/game/editor/ed_editor.h b/src/game/editor/ed_editor.h new file mode 100644 index 00000000..1730fb0a --- /dev/null +++ b/src/game/editor/ed_editor.h @@ -0,0 +1,615 @@ +#ifndef GAME_EDITOR_ED_EDITOR_H +#define GAME_EDITOR_ED_EDITOR_H + +#include <base/system.h> +#include <base/math.h> +#include <base/tl/array.h> +#include <base/tl/algorithm.h> + +#include <math.h> +#include <game/mapitems.h> +#include <game/client/render.h> + +#include <engine/shared/datafile.h> +#include <engine/shared/config.h> +#include <engine/editor.h> +#include <engine/graphics.h> + +#include <game/client/ui.h> + +typedef void (*INDEX_MODIFY_FUNC)(int *pIndex); + +//CRenderTools m_RenderTools; + +// CEditor SPECIFIC +enum +{ + MODE_LAYERS=0, + MODE_IMAGES, + + DIALOG_NONE=0, + DIALOG_FILE, +}; + +struct CEntity +{ + CPoint m_Position; + int m_Type; +}; + +class CEnvelope +{ +public: + int m_Channels; + array<CEnvPoint> m_lPoints; + char m_aName[32]; + float m_Bottom, m_Top; + + CEnvelope(int Chan) + { + m_Channels = Chan; + m_aName[0] = 0; + m_Bottom = 0; + m_Top = 0; + } + + void Resort() + { + sort(m_lPoints.all()); + FindTopBottom(0xf); + } + + void FindTopBottom(int ChannelMask) + { + m_Top = -1000000000.0f; + m_Bottom = 1000000000.0f; + for(int i = 0; i < m_lPoints.size(); i++) + { + for(int c = 0; c < m_Channels; c++) + { + if(ChannelMask&(1<<c)) + { + float v = fx2f(m_lPoints[i].m_aValues[c]); + if(v > m_Top) m_Top = v; + if(v < m_Bottom) m_Bottom = v; + } + } + } + } + + int Eval(float Time, float *pResult) + { + CRenderTools::RenderEvalEnvelope(m_lPoints.base_ptr(), m_lPoints.size(), m_Channels, Time, pResult); + return m_Channels; + } + + void AddPoint(int Time, int v0, int v1=0, int v2=0, int v3=0) + { + CEnvPoint p; + p.m_Time = Time; + p.m_aValues[0] = v0; + p.m_aValues[1] = v1; + p.m_aValues[2] = v2; + p.m_aValues[3] = v3; + p.m_Curvetype = CURVETYPE_LINEAR; + m_lPoints.add(p); + Resort(); + } + + float EndTime() + { + if(m_lPoints.size()) + return m_lPoints[m_lPoints.size()-1].m_Time*(1.0f/1000.0f); + return 0; + } +}; + + +class CLayer; +class CLayerGroup; +class CEditorMap; + +class CLayer +{ +public: + class CEditor *m_pEditor; + class IGraphics *Graphics(); + class ITextRender *TextRender(); + + CLayer() + { + m_Type = LAYERTYPE_INVALID; + m_pTypeName = "(invalid)"; + m_Visible = true; + m_Readonly = false; + m_Flags = 0; + m_pEditor = 0; + } + + virtual ~CLayer() + { + } + + + virtual void BrushSelecting(CUIRect Rect) {} + virtual int BrushGrab(CLayerGroup *pBrush, CUIRect Rect) { return 0; } + virtual void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) {} + virtual void BrushDraw(CLayer *pBrush, float x, float y) {} + virtual void BrushPlace(CLayer *pBrush, float x, float y) {} + virtual void BrushFlipX() {} + virtual void BrushFlipY() {} + virtual void BrushRotate(float Amount) {} + + virtual void Render() {} + virtual int RenderProperties(CUIRect *pToolbox) { return 0; } + + virtual void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc) {} + virtual void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) {} + + virtual void GetSize(float *w, float *h) { *w = 0; *h = 0;} + + const char *m_pTypeName; + int m_Type; + int m_Flags; + + bool m_Readonly; + bool m_Visible; +}; + +class CLayerGroup +{ +public: + class CEditorMap *m_pMap; + + array<CLayer*> m_lLayers; + + int m_OffsetX; + int m_OffsetY; + + int m_ParallaxX; + int m_ParallaxY; + + int m_UseClipping; + int m_ClipX; + int m_ClipY; + int m_ClipW; + int m_ClipH; + + const char *m_pName; + bool m_GameGroup; + bool m_Visible; + + CLayerGroup(); + ~CLayerGroup(); + + void Convert(CUIRect *pRect); + void Render(); + void MapScreen(); + void Mapping(float *pPoints); + + void GetSize(float *w, float *h); + + void DeleteLayer(int Index); + int SwapLayers(int Index0, int Index1); + + bool IsEmpty() const + { + return m_lLayers.size() == 0; + } + + void Clear() + { + m_lLayers.delete_all(); + } + + void AddLayer(CLayer *l) + { + m_lLayers.add(l); + } + + void ModifyImageIndex(INDEX_MODIFY_FUNC Func) + { + for(int i = 0; i < m_lLayers.size(); i++) + m_lLayers[i]->ModifyImageIndex(Func); + } + + void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func) + { + for(int i = 0; i < m_lLayers.size(); i++) + m_lLayers[i]->ModifyEnvelopeIndex(Func); + } +}; + +class CEditorImage : public CImageInfo +{ +public: + CEditor *m_pEditor; + + CEditorImage(CEditor *pEditor) + { + m_pEditor = pEditor; + m_TexId = -1; + m_aName[0] = 0; + m_External = 0; + m_Width = 0; + m_Height = 0; + m_pData = 0; + m_Format = 0; + } + + ~CEditorImage(); + + void AnalyseTileFlags(); + + int m_TexId; + int m_External; + char m_aName[128]; + unsigned char m_aTileFlags[256]; +}; + +class CEditorMap +{ + void MakeGameGroup(CLayerGroup *pGroup); + void MakeGameLayer(CLayer *pLayer); +public: + CEditor *m_pEditor; + + CEditorMap() + { + Clean(); + } + + array<CLayerGroup*> m_lGroups; + array<CEditorImage*> m_lImages; + array<CEnvelope*> m_lEnvelopes; + + class CLayerGame *m_pGameLayer; + CLayerGroup *m_pGameGroup; + + CEnvelope *NewEnvelope(int Channels) + { + CEnvelope *e = new CEnvelope(Channels); + m_lEnvelopes.add(e); + return e; + } + + CLayerGroup *NewGroup() + { + CLayerGroup *g = new CLayerGroup; + g->m_pMap = this; + m_lGroups.add(g); + return g; + } + + int SwapGroups(int Index0, int Index1) + { + if(Index0 < 0 || Index0 >= m_lGroups.size()) return Index0; + if(Index1 < 0 || Index1 >= m_lGroups.size()) return Index0; + if(Index0 == Index1) return Index0; + swap(m_lGroups[Index0], m_lGroups[Index1]); + return Index1; + } + + void DeleteGroup(int Index) + { + if(Index < 0 || Index >= m_lGroups.size()) return; + delete m_lGroups[Index]; + m_lGroups.remove_index(Index); + } + + void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc) + { + for(int i = 0; i < m_lGroups.size(); i++) + m_lGroups[i]->ModifyImageIndex(pfnFunc); + } + + void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) + { + for(int i = 0; i < m_lGroups.size(); i++) + m_lGroups[i]->ModifyEnvelopeIndex(pfnFunc); + } + + void Clean(); + void CreateDefault(int EntitiesTexture); + + // io + int Save(class IStorage *pStorage, const char *pFilename); + int Load(class IStorage *pStorage, const char *pFilename); +}; + + +struct CProperty +{ + const char *m_pName; + int m_Value; + int m_Type; + int m_Min; + int m_Max; +}; + +enum +{ + PROPTYPE_NULL=0, + PROPTYPE_BOOL, + PROPTYPE_INT_STEP, + PROPTYPE_INT_SCROLL, + PROPTYPE_COLOR, + PROPTYPE_IMAGE, + PROPTYPE_ENVELOPE, +}; + +typedef struct +{ + int x, y; + int w, h; +} RECTi; + +class CLayerTiles : public CLayer +{ +public: + CLayerTiles(int w, int h); + ~CLayerTiles(); + + void Resize(int NewW, int NewH); + + void MakePalette(); + virtual void Render(); + + int ConvertX(float x) const; + int ConvertY(float y) const; + void Convert(CUIRect Rect, RECTi *pOut); + void Snap(CUIRect *pRect); + void Clamp(RECTi *pRect); + + virtual void BrushSelecting(CUIRect Rect); + virtual int BrushGrab(CLayerGroup *pBrush, CUIRect Rect); + virtual void FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect); + virtual void BrushDraw(CLayer *pBrush, float wx, float wy); + virtual void BrushFlipX(); + virtual void BrushFlipY(); + + virtual int RenderProperties(CUIRect *pToolbox); + + virtual void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc); + virtual void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc); + + void PrepareForSave(); + + void GetSize(float *w, float *h) { *w = m_Width*32.0f; *h = m_Height*32.0f; } + + int m_TexId; + int m_Game; + int m_Image; + int m_Width; + int m_Height; + CTile *m_pTiles; +}; + +class CLayerQuads : public CLayer +{ +public: + CLayerQuads(); + ~CLayerQuads(); + + virtual void Render(); + CQuad *NewQuad(); + + virtual void BrushSelecting(CUIRect Rect); + virtual int BrushGrab(CLayerGroup *pBrush, CUIRect Rect); + virtual void BrushPlace(CLayer *pBrush, float wx, float wy); + virtual void BrushFlipX(); + virtual void BrushFlipY(); + virtual void BrushRotate(float Amount); + + virtual int RenderProperties(CUIRect *pToolbox); + + virtual void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc); + virtual void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc); + + void GetSize(float *w, float *h); + + int m_Image; + array<CQuad> m_lQuads; +}; + +class CLayerGame : public CLayerTiles +{ +public: + CLayerGame(int w, int h); + ~CLayerGame(); + + virtual int RenderProperties(CUIRect *pToolbox); +}; + +class CEditor : public IEditor +{ + class IInput *m_pInput; + class IClient *m_pClient; + class IGraphics *m_pGraphics; + class ITextRender *m_pTextRender; + CRenderTools m_RenderTools; + CUI m_UI; +public: + class IInput *Input() { return m_pInput; }; + class IClient *Client() { return m_pClient; }; + class IGraphics *Graphics() { return m_pGraphics; }; + class ITextRender *TextRender() { return m_pTextRender; }; + CUI *UI() { return &m_UI; } + CRenderTools *RenderTools() { return &m_RenderTools; } + + CEditor() : m_TilesetPicker(16, 16) + { + m_pInput = 0; + m_pClient = 0; + m_pGraphics = 0; + m_pTextRender = 0; + + m_Mode = MODE_LAYERS; + m_Dialog = 0; + m_pTooltip = 0; + + m_aFileName[0] = 0; + + m_WorldOffsetX = 0; + m_WorldOffsetY = 0; + m_EditorOffsetX = 0.0f; + m_EditorOffsetY = 0.0f; + + m_WorldZoom = 1.0f; + m_ZoomLevel = 200; + m_LockMouse = false; + m_ShowMousePointer = true; + m_MouseDeltaX = 0; + m_MouseDeltaY = 0; + m_MouseDeltaWx = 0; + m_MouseDeltaWy = 0; + + m_GuiActive = true; + m_ProofBorders = false; + + m_ShowDetail = true; + m_Animate = false; + m_AnimateStart = 0; + m_AnimateTime = 0; + m_AnimateSpeed = 1; + + m_ShowEnvelopeEditor = 0; + + ms_CheckerTexture = 0; + ms_BackgroundTexture = 0; + ms_CursorTexture = 0; + ms_EntitiesTexture = 0; + + ms_pUiGotContext = 0; + } + + virtual void Init(); + virtual void UpdateAndRender(); + + void InvokeFileDialog(int ListdirType, const char *pTitle, const char *pButtonText, + const char *pBasepath, const char *pDefaultName, + void (*pfnFunc)(const char *pFilename, void *pUser), void *pUser); + + void Reset(bool CreateDefault=true); + int Save(const char *pFilename); + int Load(const char *pFilename); + int Append(const char *pFilename); + void Render(); + + CQuad *GetSelectedQuad(); + CLayer *GetSelectedLayerType(int Index, int Type); + CLayer *GetSelectedLayer(int Index); + CLayerGroup *GetSelectedGroup(); + + int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal); + + int m_Mode; + int m_Dialog; + const char *m_pTooltip; + + char m_aFileName[512]; + + float m_WorldOffsetX; + float m_WorldOffsetY; + float m_EditorOffsetX; + float m_EditorOffsetY; + float m_WorldZoom; + int m_ZoomLevel; + bool m_LockMouse; + bool m_ShowMousePointer; + bool m_GuiActive; + bool m_ProofBorders; + float m_MouseDeltaX; + float m_MouseDeltaY; + float m_MouseDeltaWx; + float m_MouseDeltaWy; + + bool m_ShowDetail; + bool m_Animate; + int64 m_AnimateStart; + float m_AnimateTime; + float m_AnimateSpeed; + + int m_ShowEnvelopeEditor; + + int m_SelectedLayer; + int m_SelectedGroup; + int m_SelectedQuad; + int m_SelectedPoints; + int m_SelectedEnvelope; + int m_SelectedImage; + + static int ms_CheckerTexture; + static int ms_BackgroundTexture; + static int ms_CursorTexture; + static int ms_EntitiesTexture; + + CLayerGroup m_Brush; + CLayerTiles m_TilesetPicker; + + static const void *ms_pUiGotContext; + + CEditorMap m_Map; + + void DoMapBorder(); + int DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + + int DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners); + int DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + + int DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + + int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); + int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0); + + int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false); + + void RenderBackground(CUIRect View, int Texture, float Size, float Brightness); + + void UiInvokePopupMenu(void *pId, int Flags, float x, float y, float w, float h, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra=0); + void UiDoPopupMenu(); + + int UiDoValueSelector(void *pId, CUIRect *r, const char *pLabel, int Current, int Min, int Max, float Scale); + + static int PopupGroup(CEditor *pEditor, CUIRect View); + static int PopupLayer(CEditor *pEditor, CUIRect View); + static int PopupQuad(CEditor *pEditor, CUIRect View); + static int PopupPoint(CEditor *pEditor, CUIRect View); + static int PopupSelectImage(CEditor *pEditor, CUIRect View); + static int PopupImage(CEditor *pEditor, CUIRect View); + static int PopupMenuFile(CEditor *pEditor, CUIRect View); + + + void PopupSelectImageInvoke(int Current, float x, float y); + int PopupSelectImageResult(); + + vec4 ButtonColorMul(const void *pId); + + void DoQuadPoint(CQuad *pQuad, int QuadIndex, int v); + void DoMapEditor(CUIRect View, CUIRect Toolbar); + void DoToolbar(CUIRect Toolbar); + void DoQuad(CQuad *pQuad, int Index); + float UiDoScrollbarV(const void *id, const CUIRect *pRect, float Current); + vec4 GetButtonColor(const void *id, int Checked); + + static void ReplaceImage(const char *pFilename, void *pUser); + static void AddImage(const char *pFilename, void *pUser); + + void RenderImages(CUIRect Toolbox, CUIRect Toolbar, CUIRect View); + void RenderLayers(CUIRect Toolbox, CUIRect Toolbar, CUIRect View); + void RenderModebar(CUIRect View); + void RenderStatusbar(CUIRect View); + void RenderEnvelopeEditor(CUIRect View); + + void RenderMenubar(CUIRect Menubar); + void RenderFileDialog(); +}; + +// make sure to inline this function +inline class IGraphics *CLayer::Graphics() { return m_pEditor->Graphics(); } +inline class ITextRender *CLayer::TextRender() { return m_pEditor->TextRender(); } + +#endif diff --git a/src/game/editor/ed_editor.hpp b/src/game/editor/ed_editor.hpp deleted file mode 100644 index 98d1d960..00000000 --- a/src/game/editor/ed_editor.hpp +++ /dev/null @@ -1,589 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <base/system.h> -#include <base/math.hpp> - -#include <stdlib.h> -#include <math.h> -#include "array.hpp" -#include "../mapitems.hpp" -#include "../client/render.hpp" - -#include <engine/e_client_interface.h> -#include <engine/e_datafile.h> -#include <engine/e_config.h> -#include <engine/client/editor.h> - -#include <game/client/ui.hpp> - -typedef void (*INDEX_MODIFY_FUNC)(int *index); - -//CRenderTools m_RenderTools; - -// EDITOR SPECIFIC -template<typename T> -void swap(T &a, T &b) -{ - T tmp = a; - a = b; - b = tmp; -} - -enum -{ - MODE_LAYERS=0, - MODE_IMAGES, - - DIALOG_NONE=0, - DIALOG_FILE, -}; - -typedef struct -{ - POINT position; - int type; -} ENTITY; - -class ENVELOPE -{ -public: - int channels; - array<ENVPOINT> points; - char name[32]; - float bottom, top; - - ENVELOPE(int chan) - { - channels = chan; - name[0] = 0; - bottom = 0; - top = 0; - } - - static int sort_comp(const void *v0, const void *v1) - { - const ENVPOINT *p0 = (const ENVPOINT *)v0; - const ENVPOINT *p1 = (const ENVPOINT *)v1; - if(p0->time < p1->time) - return -1; - if(p0->time > p1->time) - return 1; - return 0; - } - - void resort() - { - qsort(points.getptr(), points.len(), sizeof(ENVPOINT), sort_comp); - find_top_bottom(0xf); - } - - void find_top_bottom(int channelmask) - { - top = -1000000000.0f; - bottom = 1000000000.0f; - for(int i = 0; i < points.len(); i++) - { - for(int c = 0; c < channels; c++) - { - if(channelmask&(1<<c)) - { - float v = fx2f(points[i].values[c]); - if(v > top) top = v; - if(v < bottom) bottom = v; - } - } - } - } - - int eval(float time, float *result) - { - CRenderTools::render_eval_envelope(points.getptr(), points.len(), channels, time, result); - return channels; - } - - void add_point(int time, int v0, int v1=0, int v2=0, int v3=0) - { - ENVPOINT p; - p.time = time; - p.values[0] = v0; - p.values[1] = v1; - p.values[2] = v2; - p.values[3] = v3; - p.curvetype = CURVETYPE_LINEAR; - points.add(p); - resort(); - } - - float end_time() - { - if(points.len()) - return points[points.len()-1].time*(1.0f/1000.0f); - return 0; - } -}; - - -class LAYER; -class LAYERGROUP; -class MAP; - -class LAYER -{ -public: - class EDITOR *editor; - class IGraphics *Graphics(); - - LAYER() - { - type = LAYERTYPE_INVALID; - type_name = "(invalid)"; - visible = true; - readonly = false; - flags = 0; - editor = 0; - } - - virtual ~LAYER() - { - } - - - virtual void brush_selecting(CUIRect rect) {} - virtual int brush_grab(LAYERGROUP *brush, CUIRect rect) { return 0; } - virtual void brush_draw(LAYER *brush, float x, float y) {} - virtual void brush_place(LAYER *brush, float x, float y) {} - virtual void brush_flip_x() {} - virtual void brush_flip_y() {} - virtual void brush_rotate(float amount) {} - - virtual void render() {} - virtual int render_properties(CUIRect *toolbox) { return 0; } - - virtual void modify_image_index(INDEX_MODIFY_FUNC func) {} - virtual void modify_envelope_index(INDEX_MODIFY_FUNC func) {} - - virtual void get_size(float *w, float *h) { *w = 0; *h = 0;} - - const char *type_name; - int type; - int flags; - - bool readonly; - bool visible; -}; - -class LAYERGROUP -{ -public: - class MAP *m_pMap; - - array<LAYER*> layers; - - int offset_x; - int offset_y; - - int parallax_x; - int parallax_y; - - int use_clipping; - int clip_x; - int clip_y; - int clip_w; - int clip_h; - - const char *name; - bool game_group; - bool visible; - - LAYERGROUP(); - ~LAYERGROUP(); - - void convert(CUIRect *rect); - void render(); - void mapscreen(); - void mapping(float *points); - - bool is_empty() const; - void clear(); - void add_layer(LAYER *l); - - void get_size(float *w, float *h); - - void delete_layer(int index); - int swap_layers(int index0, int index1); - - void modify_image_index(INDEX_MODIFY_FUNC func) - { - for(int i = 0; i < layers.len(); i++) - layers[i]->modify_image_index(func); - } - - void modify_envelope_index(INDEX_MODIFY_FUNC func) - { - for(int i = 0; i < layers.len(); i++) - layers[i]->modify_envelope_index(func); - } -}; - -class EDITOR_IMAGE : public IMAGE_INFO -{ -public: - EDITOR *editor; - - EDITOR_IMAGE(EDITOR *ed) - { - editor = editor; - tex_id = -1; - name[0] = 0; - external = 0; - width = 0; - height = 0; - data = 0; - format = 0; - } - - ~EDITOR_IMAGE(); - - void analyse_tileflags(); - - int tex_id; - int external; - char name[128]; - unsigned char tileflags[256]; -}; - -class MAP -{ - void make_game_group(LAYERGROUP *group); - void make_game_layer(LAYER *layer); -public: - EDITOR *editor; - - MAP() - { - clean(); - } - - array<LAYERGROUP*> groups; - array<EDITOR_IMAGE*> images; - array<ENVELOPE*> envelopes; - - class LAYER_GAME *game_layer; - LAYERGROUP *game_group; - - ENVELOPE *new_envelope(int channels) - { - ENVELOPE *e = new ENVELOPE(channels); - envelopes.add(e); - return e; - } - - LAYERGROUP *new_group() - { - LAYERGROUP *g = new LAYERGROUP; - g->m_pMap = this; - groups.add(g); - return g; - } - - int swap_groups(int index0, int index1) - { - if(index0 < 0 || index0 >= groups.len()) return index0; - if(index1 < 0 || index1 >= groups.len()) return index0; - if(index0 == index1) return index0; - swap(groups[index0], groups[index1]); - return index1; - } - - void delete_group(int index) - { - if(index < 0 || index >= groups.len()) return; - delete groups[index]; - groups.removebyindex(index); - } - - void modify_image_index(INDEX_MODIFY_FUNC func) - { - for(int i = 0; i < groups.len(); i++) - groups[i]->modify_image_index(func); - } - - void modify_envelope_index(INDEX_MODIFY_FUNC func) - { - for(int i = 0; i < groups.len(); i++) - groups[i]->modify_envelope_index(func); - } - - void clean(); - void create_default(int entities_texture); - - // io - int save(const char *filename); - int load(const char *filename); -}; - - -struct PROPERTY -{ - const char *name; - int value; - int type; - int min; - int max; -}; - -enum -{ - PROPTYPE_NULL=0, - PROPTYPE_BOOL, - PROPTYPE_INT_STEP, - PROPTYPE_INT_SCROLL, - PROPTYPE_COLOR, - PROPTYPE_IMAGE, - PROPTYPE_ENVELOPE, -}; - -class EDITOR : public IEditor -{ - class IGraphics *m_pGraphics; - CRenderTools m_RenderTools; - CUI m_UI; -public: - - class IGraphics *Graphics() { return m_pGraphics; }; - CUI *UI() { return &m_UI; } - CRenderTools *RenderTools() { return &m_RenderTools; } - - EDITOR() - { - mode = MODE_LAYERS; - dialog = 0; - tooltip = 0; - - world_offset_x = 0; - world_offset_y = 0; - editor_offset_x = 0.0f; - editor_offset_y = 0.0f; - - world_zoom = 1.0f; - zoom_level = 100; - lock_mouse = false; - show_mouse_pointer = true; - mouse_delta_x = 0; - mouse_delta_y = 0; - mouse_delta_wx = 0; - mouse_delta_wy = 0; - - gui_active = true; - proof_borders = false; - - show_detail = true; - animate = false; - animate_start = 0; - animate_time = 0; - animate_speed = 1; - - show_envelope_editor = 0; - } - - virtual void Init(class IGraphics *pGraphics); - virtual void UpdateAndRender(); - - void invoke_file_dialog(int listdir_type, const char *title, const char *button_text, - const char *basepath, const char *default_name, - void (*func)(const char *filename, void *user), void *user); - - void reset(bool create_default=true); - int save(const char *filename); - int load(const char *filename); - int append(const char *filename); - void render(); - - QUAD *get_selected_quad(); - LAYER *get_selected_layer_type(int index, int type); - LAYER *get_selected_layer(int index); - LAYERGROUP *get_selected_group(); - - int do_properties(CUIRect *toolbox, PROPERTY *props, int *ids, int *new_val); - - int mode; - int dialog; - const char *tooltip; - - float world_offset_x; - float world_offset_y; - float editor_offset_x; - float editor_offset_y; - float world_zoom; - int zoom_level; - bool lock_mouse; - bool show_mouse_pointer; - bool gui_active; - bool proof_borders; - float mouse_delta_x; - float mouse_delta_y; - float mouse_delta_wx; - float mouse_delta_wy; - - bool show_detail; - bool animate; - int64 animate_start; - float animate_time; - float animate_speed; - - int show_envelope_editor; - - int selected_layer; - int selected_group; - int selected_quad; - int selected_points; - int selected_envelope; - int selected_image; - - MAP map; - - int DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - - int DoButton_ButtonL(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_ButtonM(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_ButtonR(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - - int DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - - int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); - int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0); - - int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false); - //static void draw_editor_button(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - //static void draw_editor_button_menuitem(const void *id, const char *text, int checked, const CUIRect *r, const void *extra); - - void render_background(CUIRect view, int texture, float size, float brightness); - - void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(EDITOR *pEditor, CUIRect rect), void *extra=0); - void ui_do_popup_menu(); - - int ui_do_value_selector(void *id, CUIRect *r, const char *label, int current, int min, int max, float scale); - - static int popup_group(EDITOR *pEditor, CUIRect view); - static int popup_layer(EDITOR *pEditor, CUIRect view); - static int popup_quad(EDITOR *pEditor, CUIRect view); - static int popup_point(EDITOR *pEditor, CUIRect view); - static int popup_select_image(EDITOR *pEditor, CUIRect view); - static int popup_image(EDITOR *pEditor, CUIRect view); - static int popup_menu_file(EDITOR *pEditor, CUIRect view); - - - void popup_select_image_invoke(int current, float x, float y); - int popup_select_image_result(); - - vec4 button_color_mul(const void *id); - - void do_quad_point(QUAD *q, int quad_index, int v); - void do_map_editor(CUIRect view, CUIRect toolbar); - void do_toolbar(CUIRect toolbar); - void do_quad(QUAD *q, int index); - float ui_do_scrollbar_v(const void *id, const CUIRect *rect, float current); - vec4 get_button_color(const void *id, int checked); - - static void replace_image(const char *filename, void *user); - static void add_image(const char *filename, void *user); - - void render_images(CUIRect toolbox, CUIRect toolbar, CUIRect view); - void render_layers(CUIRect toolbox, CUIRect toolbar, CUIRect view); - void render_modebar(CUIRect view); - void render_statusbar(CUIRect view); - void render_envelopeeditor(CUIRect view); - - void render_menubar(CUIRect menubar); - void render_file_dialog(); -}; - -// make sure to inline this function -inline class IGraphics *LAYER::Graphics() { return editor->Graphics(); } - -//extern EDITOR editor; - -typedef struct -{ - int x, y; - int w, h; -} RECTi; - -class LAYER_TILES : public LAYER -{ -public: - LAYER_TILES(int w, int h); - ~LAYER_TILES(); - - void resize(int new_w, int new_h); - - void make_palette(); - virtual void render(); - - int convert_x(float x) const; - int convert_y(float y) const; - void convert(CUIRect rect, RECTi *out); - void snap(CUIRect *rect); - void clamp(RECTi *rect); - - virtual void brush_selecting(CUIRect rect); - virtual int brush_grab(LAYERGROUP *brush, CUIRect rect); - virtual void brush_draw(LAYER *brush, float wx, float wy); - virtual void brush_flip_x(); - virtual void brush_flip_y(); - - virtual int render_properties(CUIRect *toolbox); - - virtual void modify_image_index(INDEX_MODIFY_FUNC func); - virtual void modify_envelope_index(INDEX_MODIFY_FUNC func); - - void prepare_for_save(); - - void get_size(float *w, float *h) { *w = width*32.0f; *h = height*32.0f; } - - int tex_id; - int game; - int image; - int width; - int height; - TILE *tiles; -}; - -class LAYER_QUADS : public LAYER -{ -public: - LAYER_QUADS(); - ~LAYER_QUADS(); - - virtual void render(); - QUAD *new_quad(); - - virtual void brush_selecting(CUIRect rect); - virtual int brush_grab(LAYERGROUP *brush, CUIRect rect); - virtual void brush_place(LAYER *brush, float wx, float wy); - virtual void brush_flip_x(); - virtual void brush_flip_y(); - virtual void brush_rotate(float amount); - - virtual int render_properties(CUIRect *toolbox); - - virtual void modify_image_index(INDEX_MODIFY_FUNC func); - virtual void modify_envelope_index(INDEX_MODIFY_FUNC func); - - void get_size(float *w, float *h); - - int image; - array<QUAD> quads; -}; - -class LAYER_GAME : public LAYER_TILES -{ -public: - LAYER_GAME(int w, int h); - ~LAYER_GAME(); - - virtual int render_properties(CUIRect *toolbox); -}; diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/ed_io.cpp index b8c025fb..8ca4f704 100644 --- a/src/game/editor/ed_io.cpp +++ b/src/game/editor/ed_io.cpp @@ -1,10 +1,9 @@ -#include <string.h> -#include <stdio.h> -#include <engine/client/graphics.h> -#include "ed_editor.hpp" +#include <engine/graphics.h> +#include <engine/storage.h> +#include "ed_editor.h" template<typename T> -static int make_version(int i, const T &v) +static int MakeVersion(int i, const T &v) { return (i<<16)+sizeof(T); } // backwards compatiblity @@ -93,7 +92,7 @@ void editor_load_old(DATAFILE *df, MAP *map) { mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0); - LAYER_TILES *l = new LAYER_TILES(tmap->width, tmap->height); + CLayerTiles *l = new CLayerTiles(tmap->width, tmap->height); if(tmap->main) { @@ -113,7 +112,7 @@ void editor_load_old(DATAFILE *df, MAP *map) // process the data unsigned char *src_data = (unsigned char *)datafile_get_data(df, tmap->data); - TILE *dst_data = l->tiles; + CTile *dst_data = l->tiles; for(int y = 0; y < tmap->height; y++) for(int x = 0; x < tmap->width; x++, dst_data++, src_data+=2) @@ -138,12 +137,12 @@ void editor_load_old(DATAFILE *df, MAP *map) EDITOR_IMAGE *img = new EDITOR_IMAGE; img->width = imgres->width; img->height = imgres->height; - img->format = IMG_RGBA; + img->format = CImageInfo::FORMAT_RGBA; // copy image data img->data = mem_alloc(img->width*img->height*4, 1); mem_copy(img->data, data, img->width*img->height*4); - img->tex_id = Graphics()->LoadTextureRaw(img->width, img->height, img->format, img->data, IMG_AUTO, 0); + img->tex_id = Graphics()->LoadTextureRaw(img->width, img->height, img->format, img->data, CImageInfo::FORMAT_AUTO, 0); map->images.add(img); // unload image @@ -153,7 +152,7 @@ void editor_load_old(DATAFILE *df, MAP *map) // load entities { - LAYER_GAME *g = map->game_layer; + CLayerGame *g = map->game_layer; g->resize(game_width, game_height); for(int t = MAPRES_ENTS_START; t < MAPRES_ENTS_END; t++) { @@ -190,190 +189,192 @@ void editor_load_old(DATAFILE *df, MAP *map) } }*/ -int EDITOR::save(const char *filename) +int CEditor::Save(const char *pFilename) { - return map.save(filename); + return m_Map.Save(Kernel()->RequestInterface<IStorage>(), pFilename); } -int MAP::save(const char *filename) +int CEditorMap::Save(class IStorage *pStorage, const char *pFileName) { - dbg_msg("editor", "saving to '%s'...", filename); - DATAFILE_OUT *df = datafile_create(filename); - if(!df) + dbg_msg("editor", "saving to '%s'...", pFileName); + CDataFileWriter df; + if(!df.Open(pStorage, pFileName)) { - dbg_msg("editor", "failed to open file '%s'...", filename); + dbg_msg("editor", "failed to open file '%s'...", pFileName); return 0; } // save version { - MAPITEM_VERSION item; - item.version = 1; - datafile_add_item(df, MAPITEMTYPE_VERSION, 0, sizeof(item), &item); + CMapItemVersion Item; + Item.m_Version = 1; + df.AddItem(MAPITEMTYPE_VERSION, 0, sizeof(Item), &Item); } // save images - for(int i = 0; i < images.len(); i++) + for(int i = 0; i < m_lImages.size(); i++) { - EDITOR_IMAGE *img = images[i]; + CEditorImage *pImg = m_lImages[i]; // analyse the image for when saving (should be done when we load the image) // TODO! - img->analyse_tileflags(); + pImg->AnalyseTileFlags(); - MAPITEM_IMAGE item; - item.version = 1; + CMapItemImage Item; + Item.m_Version = 1; - item.width = img->width; - item.height = img->height; - item.external = img->external; - item.image_name = datafile_add_data(df, strlen(img->name)+1, img->name); - if(img->external) - item.image_data = -1; + Item.m_Width = pImg->m_Width; + Item.m_Height = pImg->m_Height; + Item.m_External = pImg->m_External; + Item.m_ImageName = df.AddData(str_length(pImg->m_aName)+1, pImg->m_aName); + if(pImg->m_External) + Item.m_ImageData = -1; else - item.image_data = datafile_add_data(df, item.width*item.height*4, img->data); - datafile_add_item(df, MAPITEMTYPE_IMAGE, i, sizeof(item), &item); + Item.m_ImageData = df.AddData(Item.m_Width*Item.m_Height*4, pImg->m_pData); + df.AddItem(MAPITEMTYPE_IMAGE, i, sizeof(Item), &Item); } // save layers - int layer_count = 0; - for(int g = 0; g < groups.len(); g++) + int LayerCount = 0; + for(int g = 0; g < m_lGroups.size(); g++) { - LAYERGROUP *group = groups[g]; - MAPITEM_GROUP gitem; - gitem.version = MAPITEM_GROUP::CURRENT_VERSION; + CLayerGroup *pGroup = m_lGroups[g]; + CMapItemGroup GItem; + GItem.m_Version = CMapItemGroup::CURRENT_VERSION; - gitem.parallax_x = group->parallax_x; - gitem.parallax_y = group->parallax_y; - gitem.offset_x = group->offset_x; - gitem.offset_y = group->offset_y; - gitem.use_clipping = group->use_clipping; - gitem.clip_x = group->clip_x; - gitem.clip_y = group->clip_y; - gitem.clip_w = group->clip_w; - gitem.clip_h = group->clip_h; - gitem.start_layer = layer_count; - gitem.num_layers = 0; + GItem.m_ParallaxX = pGroup->m_ParallaxX; + GItem.m_ParallaxY = pGroup->m_ParallaxY; + GItem.m_OffsetX = pGroup->m_OffsetX; + GItem.m_OffsetY = pGroup->m_OffsetY; + GItem.m_UseClipping = pGroup->m_UseClipping; + GItem.m_ClipX = pGroup->m_ClipX; + GItem.m_ClipY = pGroup->m_ClipY; + GItem.m_ClipW = pGroup->m_ClipW; + GItem.m_ClipH = pGroup->m_ClipH; + GItem.m_StartLayer = LayerCount; + GItem.m_NumLayers = 0; - for(int l = 0; l < group->layers.len(); l++) + for(int l = 0; l < pGroup->m_lLayers.size(); l++) { - if(group->layers[l]->type == LAYERTYPE_TILES) + if(pGroup->m_lLayers[l]->m_Type == LAYERTYPE_TILES) { dbg_msg("editor", "saving tiles layer"); - LAYER_TILES *layer = (LAYER_TILES *)group->layers[l]; - layer->prepare_for_save(); + CLayerTiles *pLayer = (CLayerTiles *)pGroup->m_lLayers[l]; + pLayer->PrepareForSave(); - MAPITEM_LAYER_TILEMAP item; - item.version = 2; + CMapItemLayerTilemap Item; + Item.m_Version = 2; - item.layer.flags = layer->flags; - item.layer.type = layer->type; + Item.m_Layer.m_Flags = pLayer->m_Flags; + Item.m_Layer.m_Type = pLayer->m_Type; - item.color.r = 255; // not in use right now - item.color.g = 255; - item.color.b = 255; - item.color.a = 255; - item.color_env = -1; - item.color_env_offset = 0; + Item.m_Color.r = 255; // not in use right now + Item.m_Color.g = 255; + Item.m_Color.b = 255; + Item.m_Color.a = 255; + Item.m_ColorEnv = -1; + Item.m_ColorEnvOffset = 0; - item.width = layer->width; - item.height = layer->height; - item.flags = layer->game; - item.image = layer->image; - item.data = datafile_add_data(df, layer->width*layer->height*sizeof(TILE), layer->tiles); - datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item); + Item.m_Width = pLayer->m_Width; + Item.m_Height = pLayer->m_Height; + Item.m_Flags = pLayer->m_Game; + Item.m_Image = pLayer->m_Image; + Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles); + df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item); - gitem.num_layers++; - layer_count++; + GItem.m_NumLayers++; + LayerCount++; } - else if(group->layers[l]->type == LAYERTYPE_QUADS) + else if(pGroup->m_lLayers[l]->m_Type == LAYERTYPE_QUADS) { dbg_msg("editor", "saving quads layer"); - LAYER_QUADS *layer = (LAYER_QUADS *)group->layers[l]; - if(layer->quads.len()) + CLayerQuads *pLayer = (CLayerQuads *)pGroup->m_lLayers[l]; + if(pLayer->m_lQuads.size()) { - MAPITEM_LAYER_QUADS item; - item.version = 1; - item.layer.flags = layer->flags; - item.layer.type = layer->type; - item.image = layer->image; + CMapItemLayerQuads Item; + Item.m_Version = 1; + Item.m_Layer.m_Flags = pLayer->m_Flags; + Item.m_Layer.m_Type = pLayer->m_Type; + Item.m_Image = pLayer->m_Image; // add the data - item.num_quads = layer->quads.len(); - item.data = datafile_add_data_swapped(df, layer->quads.len()*sizeof(QUAD), layer->quads.getptr()); - datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item); + Item.m_NumQuads = pLayer->m_lQuads.size(); + Item.m_Data = df.AddDataSwapped(pLayer->m_lQuads.size()*sizeof(CQuad), pLayer->m_lQuads.base_ptr()); + df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item); // clean up //mem_free(quads); - gitem.num_layers++; - layer_count++; + GItem.m_NumLayers++; + LayerCount++; } } } - datafile_add_item(df, MAPITEMTYPE_GROUP, g, sizeof(gitem), &gitem); + df.AddItem(MAPITEMTYPE_GROUP, g, sizeof(GItem), &GItem); } // save envelopes - int point_count = 0; - for(int e = 0; e < envelopes.len(); e++) + int PointCount = 0; + for(int e = 0; e < m_lEnvelopes.size(); e++) { - MAPITEM_ENVELOPE item; - item.version = 1; - item.channels = envelopes[e]->channels; - item.start_point = point_count; - item.num_points = envelopes[e]->points.len(); - item.name = -1; + CMapItemEnvelope Item; + Item.m_Version = 1; + Item.m_Channels = m_lEnvelopes[e]->m_Channels; + Item.m_StartPoint = PointCount; + Item.m_NumPoints = m_lEnvelopes[e]->m_lPoints.size(); + Item.m_Name = -1; - datafile_add_item(df, MAPITEMTYPE_ENVELOPE, e, sizeof(item), &item); - point_count += item.num_points; + df.AddItem(MAPITEMTYPE_ENVELOPE, e, sizeof(Item), &Item); + PointCount += Item.m_NumPoints; } // save points - int totalsize = sizeof(ENVPOINT) * point_count; - ENVPOINT *points = (ENVPOINT *)mem_alloc(totalsize, 1); - point_count = 0; + int TotalSize = sizeof(CEnvPoint) * PointCount; + CEnvPoint *pPoints = (CEnvPoint *)mem_alloc(TotalSize, 1); + PointCount = 0; - for(int e = 0; e < envelopes.len(); e++) + for(int e = 0; e < m_lEnvelopes.size(); e++) { - int count = envelopes[e]->points.len(); - mem_copy(&points[point_count], envelopes[e]->points.getptr(), sizeof(ENVPOINT)*count); - point_count += count; + int Count = m_lEnvelopes[e]->m_lPoints.size(); + mem_copy(&pPoints[PointCount], m_lEnvelopes[e]->m_lPoints.base_ptr(), sizeof(CEnvPoint)*Count); + PointCount += Count; } - datafile_add_item(df, MAPITEMTYPE_ENVPOINTS, 0, totalsize, points); + df.AddItem(MAPITEMTYPE_ENVPOINTS, 0, TotalSize, pPoints); // finish the data file - datafile_finish(df); + df.Finish(); dbg_msg("editor", "done"); // send rcon.. if we can - if(client_rcon_authed()) + /* + if(Client()->RconAuthed()) { - client_rcon("sv_map_reload 1"); - } + Client()->Rcon("sv_map_reload 1"); + }*/ return 1; } -int EDITOR::load(const char *filename) +int CEditor::Load(const char *pFileName) { - reset(); - return map.load(filename); + Reset(); + return m_Map.Load(Kernel()->RequestInterface<IStorage>(), pFileName); } -int MAP::load(const char *filename) +int CEditorMap::Load(class IStorage *pStorage, const char *pFileName) { - DATAFILE *df = datafile_load(filename); - if(!df) + CDataFileReader DataFile; + //DATAFILE *df = datafile_load(filename); + if(!DataFile.Open(pStorage, pFileName)) return 0; - clean(); + Clean(); // check version - MAPITEM_VERSION *item = (MAPITEM_VERSION *)datafile_find_item(df, MAPITEMTYPE_VERSION, 0); - if(!item) + CMapItemVersion *pItem = (CMapItemVersion *)DataFile.FindItem(MAPITEMTYPE_VERSION, 0); + if(!pItem) { // import old map /*MAP old_mapstuff; @@ -381,224 +382,231 @@ int MAP::load(const char *filename) editor_load_old(df, this); */ } - else if(item->version == 1) + else if(pItem->m_Version == 1) { //editor.reset(false); // load images { - int start, num; - datafile_get_type(df, MAPITEMTYPE_IMAGE, &start, &num); - for(int i = 0; i < num; i++) + int Start, Num; + DataFile.GetType( MAPITEMTYPE_IMAGE, &Start, &Num); + for(int i = 0; i < Num; i++) { - MAPITEM_IMAGE *item = (MAPITEM_IMAGE *)datafile_get_item(df, start+i, 0, 0); - char *name = (char *)datafile_get_data(df, item->image_name); + CMapItemImage *pItem = (CMapItemImage *)DataFile.GetItem(Start+i, 0, 0); + char *pName = (char *)DataFile.GetData(pItem->m_ImageName); // copy base info - EDITOR_IMAGE *img = new EDITOR_IMAGE(editor); - img->external = item->external; + CEditorImage *pImg = new CEditorImage(m_pEditor); + pImg->m_External = pItem->m_External; - if(item->external) + if(pItem->m_External) { - char buf[256]; - sprintf(buf, "mapres/%s.png", name); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf),"mapres/%s.png", pName); // load external - EDITOR_IMAGE imginfo(editor); - if(editor->Graphics()->LoadPNG(&imginfo, buf)) + CEditorImage ImgInfo(m_pEditor); + if(m_pEditor->Graphics()->LoadPNG(&ImgInfo, aBuf)) { - *img = imginfo; - img->tex_id = editor->Graphics()->LoadTextureRaw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0); - img->external = 1; + *pImg = ImgInfo; + pImg->m_TexId = m_pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0); + pImg->m_External = 1; } } else { - img->width = item->width; - img->height = item->height; - img->format = IMG_RGBA; + pImg->m_Width = pItem->m_Width; + pImg->m_Height = pItem->m_Height; + pImg->m_Format = CImageInfo::FORMAT_RGBA; // copy image data - void *data = datafile_get_data(df, item->image_data); - img->data = mem_alloc(img->width*img->height*4, 1); - mem_copy(img->data, data, img->width*img->height*4); - img->tex_id = editor->Graphics()->LoadTextureRaw(img->width, img->height, img->format, img->data, IMG_AUTO, 0); + void *pData = DataFile.GetData(pItem->m_ImageData); + pImg->m_pData = mem_alloc(pImg->m_Width*pImg->m_Height*4, 1); + mem_copy(pImg->m_pData, pData, pImg->m_Width*pImg->m_Height*4); + pImg->m_TexId = m_pEditor->Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, pImg->m_Format, pImg->m_pData, CImageInfo::FORMAT_AUTO, 0); } // copy image name - if(name) - strncpy(img->name, name, 128); + if(pName) + str_copy(pImg->m_aName, pName, 128); - images.add(img); + m_lImages.add(pImg); // unload image - datafile_unload_data(df, item->image_data); - datafile_unload_data(df, item->image_name); + DataFile.UnloadData(pItem->m_ImageData); + DataFile.UnloadData(pItem->m_ImageName); } } // load groups { - int layers_start, layers_num; - datafile_get_type(df, MAPITEMTYPE_LAYER, &layers_start, &layers_num); + int LayersStart, LayersNum; + DataFile.GetType(MAPITEMTYPE_LAYER, &LayersStart, &LayersNum); - int start, num; - datafile_get_type(df, MAPITEMTYPE_GROUP, &start, &num); - for(int g = 0; g < num; g++) + int Start, Num; + DataFile.GetType(MAPITEMTYPE_GROUP, &Start, &Num); + for(int g = 0; g < Num; g++) { - MAPITEM_GROUP *gitem = (MAPITEM_GROUP *)datafile_get_item(df, start+g, 0, 0); + CMapItemGroup *pGItem = (CMapItemGroup *)DataFile.GetItem(Start+g, 0, 0); - if(gitem->version < 1 || gitem->version > MAPITEM_GROUP::CURRENT_VERSION) + if(pGItem->m_Version < 1 || pGItem->m_Version > CMapItemGroup::CURRENT_VERSION) continue; - LAYERGROUP *group = new_group(); - group->parallax_x = gitem->parallax_x; - group->parallax_y = gitem->parallax_y; - group->offset_x = gitem->offset_x; - group->offset_y = gitem->offset_y; + CLayerGroup *pGroup = NewGroup(); + pGroup->m_ParallaxX = pGItem->m_ParallaxX; + pGroup->m_ParallaxY = pGItem->m_ParallaxY; + pGroup->m_OffsetX = pGItem->m_OffsetX; + pGroup->m_OffsetY = pGItem->m_OffsetY; - if(gitem->version >= 2) + if(pGItem->m_Version >= 2) { - group->use_clipping = gitem->use_clipping; - group->clip_x = gitem->clip_x; - group->clip_y = gitem->clip_y; - group->clip_w = gitem->clip_w; - group->clip_h = gitem->clip_h; + pGroup->m_UseClipping = pGItem->m_UseClipping; + pGroup->m_ClipX = pGItem->m_ClipX; + pGroup->m_ClipY = pGItem->m_ClipY; + pGroup->m_ClipW = pGItem->m_ClipW; + pGroup->m_ClipH = pGItem->m_ClipH; } - for(int l = 0; l < gitem->num_layers; l++) + for(int l = 0; l < pGItem->m_NumLayers; l++) { - LAYER *layer = 0; - MAPITEM_LAYER *layer_item = (MAPITEM_LAYER *)datafile_get_item(df, layers_start+gitem->start_layer+l, 0, 0); - if(!layer_item) + CLayer *pLayer = 0; + CMapItemLayer *pLayerItem = (CMapItemLayer *)DataFile.GetItem(LayersStart+pGItem->m_StartLayer+l, 0, 0); + if(!pLayerItem) continue; - if(layer_item->type == LAYERTYPE_TILES) + if(pLayerItem->m_Type == LAYERTYPE_TILES) { - MAPITEM_LAYER_TILEMAP *tilemap_item = (MAPITEM_LAYER_TILEMAP *)layer_item; - LAYER_TILES *tiles = 0; + CMapItemLayerTilemap *pTilemapItem = (CMapItemLayerTilemap *)pLayerItem; + CLayerTiles *pTiles = 0; - if(tilemap_item->flags&1) + if(pTilemapItem->m_Flags&1) { - tiles = new LAYER_GAME(tilemap_item->width, tilemap_item->height); - make_game_layer(tiles); - make_game_group(group); + pTiles = new CLayerGame(pTilemapItem->m_Width, pTilemapItem->m_Height); + MakeGameLayer(pTiles); + MakeGameGroup(pGroup); } else - tiles = new LAYER_TILES(tilemap_item->width, tilemap_item->height); + { + pTiles = new CLayerTiles(pTilemapItem->m_Width, pTilemapItem->m_Height); + pTiles->m_pEditor = m_pEditor; + } - layer = tiles; + pLayer = pTiles; - group->add_layer(tiles); - void *data = datafile_get_data(df, tilemap_item->data); - tiles->image = tilemap_item->image; - tiles->game = tilemap_item->flags&1; + pGroup->AddLayer(pTiles); + void *pData = DataFile.GetData(pTilemapItem->m_Data); + pTiles->m_Image = pTilemapItem->m_Image; + pTiles->m_Game = pTilemapItem->m_Flags&1; - mem_copy(tiles->tiles, data, tiles->width*tiles->height*sizeof(TILE)); + mem_copy(pTiles->m_pTiles, pData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile)); - if(tiles->game && tilemap_item->version == make_version(1, *tilemap_item)) + if(pTiles->m_Game && pTilemapItem->m_Version == MakeVersion(1, *pTilemapItem)) { - for(int i = 0; i < tiles->width*tiles->height; i++) + for(int i = 0; i < pTiles->m_Width*pTiles->m_Height; i++) { - if(tiles->tiles[i].index) - tiles->tiles[i].index += ENTITY_OFFSET; + if(pTiles->m_pTiles[i].m_Index) + pTiles->m_pTiles[i].m_Index += ENTITY_OFFSET; } } - datafile_unload_data(df, tilemap_item->data); + DataFile.UnloadData(pTilemapItem->m_Data); } - else if(layer_item->type == LAYERTYPE_QUADS) + else if(pLayerItem->m_Type == LAYERTYPE_QUADS) { - MAPITEM_LAYER_QUADS *quads_item = (MAPITEM_LAYER_QUADS *)layer_item; - LAYER_QUADS *quads = new LAYER_QUADS; - layer = quads; - quads->image = quads_item->image; - if(quads->image < -1 || quads->image >= images.len()) - quads->image = -1; - void *data = datafile_get_data_swapped(df, quads_item->data); - group->add_layer(quads); - quads->quads.setsize(quads_item->num_quads); - mem_copy(quads->quads.getptr(), data, sizeof(QUAD)*quads_item->num_quads); - datafile_unload_data(df, quads_item->data); + CMapItemLayerQuads *pQuadsItem = (CMapItemLayerQuads *)pLayerItem; + CLayerQuads *pQuads = new CLayerQuads; + pQuads->m_pEditor = m_pEditor; + pLayer = pQuads; + pQuads->m_Image = pQuadsItem->m_Image; + if(pQuads->m_Image < -1 || pQuads->m_Image >= m_lImages.size()) + pQuads->m_Image = -1; + void *pData = DataFile.GetDataSwapped(pQuadsItem->m_Data); + pGroup->AddLayer(pQuads); + pQuads->m_lQuads.set_size(pQuadsItem->m_NumQuads); + mem_copy(pQuads->m_lQuads.base_ptr(), pData, sizeof(CQuad)*pQuadsItem->m_NumQuads); + DataFile.UnloadData(pQuadsItem->m_Data); } - if(layer) - layer->flags = layer_item->flags; + if(pLayer) + pLayer->m_Flags = pLayerItem->m_Flags; } } } // load envelopes { - ENVPOINT *points = 0; + CEnvPoint *pPoints = 0; { - int start, num; - datafile_get_type(df, MAPITEMTYPE_ENVPOINTS, &start, &num); - if(num) - points = (ENVPOINT *)datafile_get_item(df, start, 0, 0); + int Start, Num; + DataFile.GetType(MAPITEMTYPE_ENVPOINTS, &Start, &Num); + if(Num) + pPoints = (CEnvPoint *)DataFile.GetItem(Start, 0, 0); } - int start, num; - datafile_get_type(df, MAPITEMTYPE_ENVELOPE, &start, &num); - for(int e = 0; e < num; e++) + int Start, Num; + DataFile.GetType(MAPITEMTYPE_ENVELOPE, &Start, &Num); + for(int e = 0; e < Num; e++) { - MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)datafile_get_item(df, start+e, 0, 0); - ENVELOPE *env = new ENVELOPE(item->channels); - env->points.setsize(item->num_points); - mem_copy(env->points.getptr(), &points[item->start_point], sizeof(ENVPOINT)*item->num_points); - envelopes.add(env); + CMapItemEnvelope *pItem = (CMapItemEnvelope *)DataFile.GetItem(Start+e, 0, 0); + CEnvelope *pEnv = new CEnvelope(pItem->m_Channels); + pEnv->m_lPoints.set_size(pItem->m_NumPoints); + mem_copy(pEnv->m_lPoints.base_ptr(), &pPoints[pItem->m_StartPoint], sizeof(CEnvPoint)*pItem->m_NumPoints); + m_lEnvelopes.add(pEnv); } } } - datafile_unload(df); - - return 0; + return 1; } -static int modify_add_amount = 0; -static void modify_add(int *index) +static int gs_ModifyAddAmount = 0; +static void ModifyAdd(int *pIndex) { - if(*index >= 0) - *index += modify_add_amount; + if(*pIndex >= 0) + *pIndex += gs_ModifyAddAmount; } -int EDITOR::append(const char *filename) +int CEditor::Append(const char *pFileName) { - MAP new_map; - int err; - err = new_map.load(filename); - if(err) - return err; + CEditorMap NewMap; + NewMap.m_pEditor = this; + + int Err; + Err = NewMap.Load(Kernel()->RequestInterface<IStorage>(), pFileName); + if(Err) + return Err; // modify indecies - modify_add_amount = map.images.len(); - new_map.modify_image_index(modify_add); + gs_ModifyAddAmount = m_Map.m_lImages.size(); + NewMap.ModifyImageIndex(ModifyAdd); - modify_add_amount = map.envelopes.len(); - new_map.modify_envelope_index(modify_add); + gs_ModifyAddAmount = m_Map.m_lEnvelopes.size(); + NewMap.ModifyEnvelopeIndex(ModifyAdd); // transfer images - for(int i = 0; i < new_map.images.len(); i++) - map.images.add(new_map.images[i]); - new_map.images.clear(); + for(int i = 0; i < NewMap.m_lImages.size(); i++) + m_Map.m_lImages.add(NewMap.m_lImages[i]); + NewMap.m_lImages.clear(); // transfer envelopes - for(int i = 0; i < new_map.envelopes.len(); i++) - map.envelopes.add(new_map.envelopes[i]); - new_map.envelopes.clear(); + for(int i = 0; i < NewMap.m_lEnvelopes.size(); i++) + m_Map.m_lEnvelopes.add(NewMap.m_lEnvelopes[i]); + NewMap.m_lEnvelopes.clear(); // transfer groups - for(int i = 0; i < new_map.groups.len(); i++) + for(int i = 0; i < NewMap.m_lGroups.size(); i++) { - if(new_map.groups[i] == new_map.game_group) - delete new_map.groups[i]; + if(NewMap.m_lGroups[i] == NewMap.m_pGameGroup) + delete NewMap.m_lGroups[i]; else - map.groups.add(new_map.groups[i]); + { + NewMap.m_lGroups[i]->m_pMap = &m_Map; + m_Map.m_lGroups.add(NewMap.m_lGroups[i]); + } } - new_map.groups.clear(); + NewMap.m_lGroups.clear(); // all done \o/ return 0; diff --git a/src/game/editor/ed_layer_game.cpp b/src/game/editor/ed_layer_game.cpp index 9010bc71..82a9cb1d 100644 --- a/src/game/editor/ed_layer_game.cpp +++ b/src/game/editor/ed_layer_game.cpp @@ -1,20 +1,20 @@ -#include "ed_editor.hpp" +#include "ed_editor.h" -LAYER_GAME::LAYER_GAME(int w, int h) -: LAYER_TILES(w, h) +CLayerGame::CLayerGame(int w, int h) +: CLayerTiles(w, h) { - type_name = "Game"; - game = 1; + m_pTypeName = "Game"; + m_Game = 1; } -LAYER_GAME::~LAYER_GAME() +CLayerGame::~CLayerGame() { } -int LAYER_GAME::render_properties(CUIRect *toolbox) +int CLayerGame::RenderProperties(CUIRect *pToolbox) { - int r = LAYER_TILES::render_properties(toolbox); - image = -1; + int r = CLayerTiles::RenderProperties(pToolbox); + m_Image = -1; return r; } diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/ed_layer_quads.cpp index ce1ba4b6..3aeffc73 100644 --- a/src/game/editor/ed_layer_quads.cpp +++ b/src/game/editor/ed_layer_quads.cpp @@ -1,213 +1,216 @@ -#include <base/math.hpp> +#include <base/math.h> -#include <engine/client/graphics.h> +#include <engine/graphics.h> -#include "ed_editor.hpp" -#include <game/generated/gc_data.hpp> -#include <game/client/render.hpp> +#include "ed_editor.h" +#include <game/generated/client_data.h> +#include <game/client/render.h> -LAYER_QUADS::LAYER_QUADS() +CLayerQuads::CLayerQuads() { - type = LAYERTYPE_QUADS; - type_name = "Quads"; - image = -1; + m_Type = LAYERTYPE_QUADS; + m_pTypeName = "Quads"; + m_Image = -1; } -LAYER_QUADS::~LAYER_QUADS() +CLayerQuads::~CLayerQuads() { } -static void envelope_eval(float time_offset, int env, float *channels, void *user) +static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser) { - EDITOR *pEditor = (EDITOR *)user; - if(env < 0 || env > pEditor->map.envelopes.len()) + CEditor *pEditor = (CEditor *)pUser; + if(Env < 0 || Env > pEditor->m_Map.m_lEnvelopes.size()) { - channels[0] = 0; - channels[1] = 0; - channels[2] = 0; - channels[3] = 0; + pChannels[0] = 0; + pChannels[1] = 0; + pChannels[2] = 0; + pChannels[3] = 0; return; } - ENVELOPE *e = pEditor->map.envelopes[env]; - float t = pEditor->animate_time+time_offset; - t *= pEditor->animate_speed; - e->eval(t, channels); + CEnvelope *e = pEditor->m_Map.m_lEnvelopes[Env]; + float t = pEditor->m_AnimateTime+TimeOffset; + t *= pEditor->m_AnimateSpeed; + e->Eval(t, pChannels); } -void LAYER_QUADS::render() +void CLayerQuads::Render() { Graphics()->TextureSet(-1); - if(image >= 0 && image < editor->map.images.len()) - Graphics()->TextureSet(editor->map.images[image]->tex_id); + if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size()) + Graphics()->TextureSet(m_pEditor->m_Map.m_lImages[m_Image]->m_TexId); - editor->RenderTools()->render_quads(quads.getptr(), quads.len(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, envelope_eval, editor); + m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, EnvelopeEval, m_pEditor); } -QUAD *LAYER_QUADS::new_quad() +CQuad *CLayerQuads::NewQuad() { - QUAD *q = &quads[quads.add(QUAD())]; + CQuad *q = &m_lQuads[m_lQuads.add(CQuad())]; - q->pos_env = -1; - q->color_env = -1; - q->pos_env_offset = 0; - q->color_env_offset = 0; + q->m_PosEnv = -1; + q->m_ColorEnv = -1; + q->m_PosEnvOffset = 0; + q->m_ColorEnvOffset = 0; int x = 0, y = 0; - q->points[0].x = x; - q->points[0].y = y; - q->points[1].x = x+64; - q->points[1].y = y; - q->points[2].x = x; - q->points[2].y = y+64; - q->points[3].x = x+64; - q->points[3].y = y+64; - - q->points[4].x = x+32; // pivot - q->points[4].y = y+32; + q->m_aPoints[0].x = x; + q->m_aPoints[0].y = y; + q->m_aPoints[1].x = x+64; + q->m_aPoints[1].y = y; + q->m_aPoints[2].x = x; + q->m_aPoints[2].y = y+64; + q->m_aPoints[3].x = x+64; + q->m_aPoints[3].y = y+64; + + q->m_aPoints[4].x = x+32; // pivot + q->m_aPoints[4].y = y+32; for(int i = 0; i < 5; i++) { - q->points[i].x <<= 10; - q->points[i].y <<= 10; + q->m_aPoints[i].x <<= 10; + q->m_aPoints[i].y <<= 10; } - q->texcoords[0].x = 0; - q->texcoords[0].y = 0; + q->m_aTexcoords[0].x = 0; + q->m_aTexcoords[0].y = 0; - q->texcoords[1].x = 1<<10; - q->texcoords[1].y = 0; + q->m_aTexcoords[1].x = 1<<10; + q->m_aTexcoords[1].y = 0; - q->texcoords[2].x = 0; - q->texcoords[2].y = 1<<10; + q->m_aTexcoords[2].x = 0; + q->m_aTexcoords[2].y = 1<<10; - q->texcoords[3].x = 1<<10; - q->texcoords[3].y = 1<<10; + q->m_aTexcoords[3].x = 1<<10; + q->m_aTexcoords[3].y = 1<<10; - q->colors[0].r = 255; q->colors[0].g = 255; q->colors[0].b = 255; q->colors[0].a = 255; - q->colors[1].r = 255; q->colors[1].g = 255; q->colors[1].b = 255; q->colors[1].a = 255; - q->colors[2].r = 255; q->colors[2].g = 255; q->colors[2].b = 255; q->colors[2].a = 255; - q->colors[3].r = 255; q->colors[3].g = 255; q->colors[3].b = 255; q->colors[3].a = 255; + q->m_aColors[0].r = 255; q->m_aColors[0].g = 255; q->m_aColors[0].b = 255; q->m_aColors[0].a = 255; + q->m_aColors[1].r = 255; q->m_aColors[1].g = 255; q->m_aColors[1].b = 255; q->m_aColors[1].a = 255; + q->m_aColors[2].r = 255; q->m_aColors[2].g = 255; q->m_aColors[2].b = 255; q->m_aColors[2].a = 255; + q->m_aColors[3].r = 255; q->m_aColors[3].g = 255; q->m_aColors[3].b = 255; q->m_aColors[3].a = 255; return q; } -void LAYER_QUADS::brush_selecting(CUIRect rect) +void CLayerQuads::BrushSelecting(CUIRect Rect) { // draw selection rectangle + IGraphics::CLineItem Array[4] = { + IGraphics::CLineItem(Rect.x, Rect.y, Rect.x+Rect.w, Rect.y), + IGraphics::CLineItem(Rect.x+Rect.w, Rect.y, Rect.x+Rect.w, Rect.y+Rect.h), + IGraphics::CLineItem(Rect.x+Rect.w, Rect.y+Rect.h, Rect.x, Rect.y+Rect.h), + IGraphics::CLineItem(Rect.x, Rect.y+Rect.h, Rect.x, Rect.y)}; Graphics()->TextureSet(-1); Graphics()->LinesBegin(); - Graphics()->LinesDraw(rect.x, rect.y, rect.x+rect.w, rect.y); - Graphics()->LinesDraw(rect.x+rect.w, rect.y, rect.x+rect.w, rect.y+rect.h); - Graphics()->LinesDraw(rect.x+rect.w, rect.y+rect.h, rect.x, rect.y+rect.h); - Graphics()->LinesDraw(rect.x, rect.y+rect.h, rect.x, rect.y); + Graphics()->LinesDraw(Array, 4); Graphics()->LinesEnd(); } -int LAYER_QUADS::brush_grab(LAYERGROUP *brush, CUIRect rect) +int CLayerQuads::BrushGrab(CLayerGroup *pBrush, CUIRect Rect) { // create new layers - LAYER_QUADS *grabbed = new LAYER_QUADS(); - grabbed->image = image; - brush->add_layer(grabbed); + CLayerQuads *pGrabbed = new CLayerQuads(); + pGrabbed->m_pEditor = m_pEditor; + pGrabbed->m_Image = m_Image; + pBrush->AddLayer(pGrabbed); //dbg_msg("", "%f %f %f %f", rect.x, rect.y, rect.w, rect.h); - for(int i = 0; i < quads.len(); i++) + for(int i = 0; i < m_lQuads.size(); i++) { - QUAD *q = &quads[i]; - float px = fx2f(q->points[4].x); - float py = fx2f(q->points[4].y); + CQuad *q = &m_lQuads[i]; + float px = fx2f(q->m_aPoints[4].x); + float py = fx2f(q->m_aPoints[4].y); - if(px > rect.x && px < rect.x+rect.w && py > rect.y && py < rect.y+rect.h) + if(px > Rect.x && px < Rect.x+Rect.w && py > Rect.y && py < Rect.y+Rect.h) { dbg_msg("", "grabbed one"); - QUAD n; + CQuad n; n = *q; for(int p = 0; p < 5; p++) { - n.points[p].x -= f2fx(rect.x); - n.points[p].y -= f2fx(rect.y); + n.m_aPoints[p].x -= f2fx(Rect.x); + n.m_aPoints[p].y -= f2fx(Rect.y); } - grabbed->quads.add(n); + pGrabbed->m_lQuads.add(n); } } - return grabbed->quads.len()?1:0; + return pGrabbed->m_lQuads.size()?1:0; } -void LAYER_QUADS::brush_place(LAYER *brush, float wx, float wy) +void CLayerQuads::BrushPlace(CLayer *pBrush, float wx, float wy) { - LAYER_QUADS *l = (LAYER_QUADS *)brush; - for(int i = 0; i < l->quads.len(); i++) + CLayerQuads *l = (CLayerQuads *)pBrush; + for(int i = 0; i < l->m_lQuads.size(); i++) { - QUAD n = l->quads[i]; + CQuad n = l->m_lQuads[i]; for(int p = 0; p < 5; p++) { - n.points[p].x += f2fx(wx); - n.points[p].y += f2fx(wy); + n.m_aPoints[p].x += f2fx(wx); + n.m_aPoints[p].y += f2fx(wy); } - quads.add(n); + m_lQuads.add(n); } } -void LAYER_QUADS::brush_flip_x() +void CLayerQuads::BrushFlipX() { } -void LAYER_QUADS::brush_flip_y() +void CLayerQuads::BrushFlipY() { } -void rotate(vec2 *center, vec2 *point, float rotation) +void Rotate(vec2 *pCenter, vec2 *pPoint, float Rotation) { - float x = point->x - center->x; - float y = point->y - center->y; - point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; - point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; + float x = pPoint->x - pCenter->x; + float y = pPoint->y - pCenter->y; + pPoint->x = x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x; + pPoint->y = x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y; } -void LAYER_QUADS::brush_rotate(float amount) +void CLayerQuads::BrushRotate(float Amount) { - vec2 center; - get_size(¢er.x, ¢er.y); - center.x /= 2; - center.y /= 2; + vec2 Center; + GetSize(&Center.x, &Center.y); + Center.x /= 2; + Center.y /= 2; - for(int i = 0; i < quads.len(); i++) + for(int i = 0; i < m_lQuads.size(); i++) { - QUAD *q = &quads[i]; + CQuad *q = &m_lQuads[i]; for(int p = 0; p < 5; p++) { - vec2 pos(fx2f(q->points[p].x), fx2f(q->points[p].y)); - rotate(¢er, &pos, amount); - q->points[p].x = f2fx(pos.x); - q->points[p].y = f2fx(pos.y); + vec2 Pos(fx2f(q->m_aPoints[p].x), fx2f(q->m_aPoints[p].y)); + Rotate(&Center, &Pos, Amount); + q->m_aPoints[p].x = f2fx(Pos.x); + q->m_aPoints[p].y = f2fx(Pos.y); } } } -void LAYER_QUADS::get_size(float *w, float *h) +void CLayerQuads::GetSize(float *w, float *h) { *w = 0; *h = 0; - for(int i = 0; i < quads.len(); i++) + for(int i = 0; i < m_lQuads.size(); i++) { for(int p = 0; p < 5; p++) { - *w = max(*w, fx2f(quads[i].points[p].x)); - *h = max(*h, fx2f(quads[i].points[p].y)); + *w = max(*w, fx2f(m_lQuads[i].m_aPoints[p].x)); + *h = max(*h, fx2f(m_lQuads[i].m_aPoints[p].y)); } } } -extern int selected_points; +extern int gs_SelectedPoints; -int LAYER_QUADS::render_properties(CUIRect *toolbox) +int CLayerQuads::RenderProperties(CUIRect *pToolBox) { // layer props enum @@ -216,37 +219,37 @@ int LAYER_QUADS::render_properties(CUIRect *toolbox) NUM_PROPS, }; - PROPERTY props[] = { - {"Image", image, PROPTYPE_IMAGE, -1, 0}, + CProperty aProps[] = { + {"Image", m_Image, PROPTYPE_IMAGE, -1, 0}, {0}, }; - static int ids[NUM_PROPS] = {0}; - int new_val = 0; - int prop = editor->do_properties(toolbox, props, ids, &new_val); + static int s_aIds[NUM_PROPS] = {0}; + int NewVal = 0; + int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); - if(prop == PROP_IMAGE) + if(Prop == PROP_IMAGE) { - if(new_val >= 0) - image = new_val%editor->map.images.len(); + if(NewVal >= 0) + m_Image = NewVal%m_pEditor->m_Map.m_lImages.size(); else - image = -1; + m_Image = -1; } return 0; } -void LAYER_QUADS::modify_image_index(INDEX_MODIFY_FUNC func) +void CLayerQuads::ModifyImageIndex(INDEX_MODIFY_FUNC Func) { - func(&image); + Func(&m_Image); } -void LAYER_QUADS::modify_envelope_index(INDEX_MODIFY_FUNC func) +void CLayerQuads::ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func) { - for(int i = 0; i < quads.len(); i++) + for(int i = 0; i < m_lQuads.size(); i++) { - func(&quads[i].pos_env); - func(&quads[i].color_env); + Func(&m_lQuads[i].m_PosEnv); + Func(&m_lQuads[i].m_ColorEnv); } } diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/ed_layer_tiles.cpp index 0d42cb78..ecd7c62c 100644 --- a/src/game/editor/ed_layer_tiles.cpp +++ b/src/game/editor/ed_layer_tiles.cpp @@ -1,247 +1,281 @@ -#include <base/math.hpp> +#include <base/math.h> -#include <engine/client/graphics.h> +#include <engine/graphics.h> +#include <engine/textrender.h> -#include <game/generated/gc_data.hpp> -#include <game/client/render.hpp> -#include "ed_editor.hpp" +#include <game/generated/client_data.h> +#include <game/client/render.h> +#include "ed_editor.h" -LAYER_TILES::LAYER_TILES(int w, int h) +CLayerTiles::CLayerTiles(int w, int h) { - type = LAYERTYPE_TILES; - type_name = "Tiles"; - width = w; - height = h; - image = -1; - tex_id = -1; - game = 0; + m_Type = LAYERTYPE_TILES; + m_pTypeName = "Tiles"; + m_Width = w; + m_Height = h; + m_Image = -1; + m_TexId = -1; + m_Game = 0; - tiles = new TILE[width*height]; - mem_zero(tiles, width*height*sizeof(TILE)); + m_pTiles = new CTile[m_Width*m_Height]; + mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile)); } -LAYER_TILES::~LAYER_TILES() +CLayerTiles::~CLayerTiles() { - delete [] tiles; + delete [] m_pTiles; } -void LAYER_TILES::prepare_for_save() +void CLayerTiles::PrepareForSave() { - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - tiles[y*width+x].flags &= TILEFLAG_VFLIP|TILEFLAG_HFLIP; + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) + m_pTiles[y*m_Width+x].m_Flags &= TILEFLAG_VFLIP|TILEFLAG_HFLIP; - if(image != -1) + if(m_Image != -1) { - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - tiles[y*width+x].flags |= editor->map.images[image]->tileflags[tiles[y*width+x].index]; + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) + m_pTiles[y*m_Width+x].m_Flags |= m_pEditor->m_Map.m_lImages[m_Image]->m_aTileFlags[m_pTiles[y*m_Width+x].m_Index]; } } -void LAYER_TILES::make_palette() +void CLayerTiles::MakePalette() { - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - tiles[y*width+x].index = y*16+x; + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) + m_pTiles[y*m_Width+x].m_Index = y*16+x; } -void LAYER_TILES::render() +void CLayerTiles::Render() { - if(image >= 0 && image < editor->map.images.len()) - tex_id = editor->map.images[image]->tex_id; - Graphics()->TextureSet(tex_id); - editor->RenderTools()->render_tilemap(tiles, width, height, 32.0f, vec4(1,1,1,1), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); + if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size()) + m_TexId = m_pEditor->m_Map.m_lImages[m_Image]->m_TexId; + Graphics()->TextureSet(m_TexId); + m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, vec4(1,1,1,1), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT); } -int LAYER_TILES::convert_x(float x) const { return (int)(x/32.0f); } -int LAYER_TILES::convert_y(float y) const { return (int)(y/32.0f); } +int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); } +int CLayerTiles::ConvertY(float y) const { return (int)(y/32.0f); } -void LAYER_TILES::convert(CUIRect rect, RECTi *out) +void CLayerTiles::Convert(CUIRect Rect, RECTi *pOut) { - out->x = convert_x(rect.x); - out->y = convert_y(rect.y); - out->w = convert_x(rect.x+rect.w+31) - out->x; - out->h = convert_y(rect.y+rect.h+31) - out->y; + pOut->x = ConvertX(Rect.x); + pOut->y = ConvertY(Rect.y); + pOut->w = ConvertX(Rect.x+Rect.w+31) - pOut->x; + pOut->h = ConvertY(Rect.y+Rect.h+31) - pOut->y; } -void LAYER_TILES::snap(CUIRect *rect) +void CLayerTiles::Snap(CUIRect *pRect) { - RECTi out; - convert(*rect, &out); - rect->x = out.x*32.0f; - rect->y = out.y*32.0f; - rect->w = out.w*32.0f; - rect->h = out.h*32.0f; + RECTi Out; + Convert(*pRect, &Out); + pRect->x = Out.x*32.0f; + pRect->y = Out.y*32.0f; + pRect->w = Out.w*32.0f; + pRect->h = Out.h*32.0f; } -void LAYER_TILES::clamp(RECTi *rect) +void CLayerTiles::Clamp(RECTi *pRect) { - if(rect->x < 0) + if(pRect->x < 0) { - rect->w += rect->x; - rect->x = 0; + pRect->w += pRect->x; + pRect->x = 0; } - if(rect->y < 0) + if(pRect->y < 0) { - rect->h += rect->y; - rect->y = 0; + pRect->h += pRect->y; + pRect->y = 0; } - if(rect->x+rect->w > width) - rect->w = width-rect->x; + if(pRect->x+pRect->w > m_Width) + pRect->w = m_Width - pRect->x; - if(rect->y+rect->h > height) - rect->h = height-rect->y; + if(pRect->y+pRect->h > m_Height) + pRect->h = m_Height - pRect->y; - if(rect->h < 0) - rect->h = 0; - if(rect->w < 0) - rect->w = 0; + if(pRect->h < 0) + pRect->h = 0; + if(pRect->w < 0) + pRect->w = 0; } -void LAYER_TILES::brush_selecting(CUIRect rect) +void CLayerTiles::BrushSelecting(CUIRect Rect) { Graphics()->TextureSet(-1); - editor->Graphics()->QuadsBegin(); - editor->Graphics()->SetColor(1,1,1,0.4f); - snap(&rect); - editor->Graphics()->QuadsDrawTL(rect.x, rect.y, rect.w, rect.h); - editor->Graphics()->QuadsEnd(); - char buf[16]; - str_format(buf, sizeof(buf), "%d,%d", convert_x(rect.w), convert_y(rect.h)); - gfx_text(0, rect.x+3.0f, rect.y+3.0f, 15.0f*editor->world_zoom, buf, -1); + m_pEditor->Graphics()->QuadsBegin(); + m_pEditor->Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.4f); + Snap(&Rect); + IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h); + m_pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); + m_pEditor->Graphics()->QuadsEnd(); + char aBuf[16]; + str_format(aBuf, sizeof(aBuf), "%d,%d", ConvertX(Rect.w), ConvertY(Rect.h)); + TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, 15.0f*m_pEditor->m_WorldZoom, aBuf, -1); } -int LAYER_TILES::brush_grab(LAYERGROUP *brush, CUIRect rect) +int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect) { RECTi r; - convert(rect, &r); - clamp(&r); + Convert(Rect, &r); + Clamp(&r); if(!r.w || !r.h) return 0; // create new layers - LAYER_TILES *grabbed = new LAYER_TILES(r.w, r.h); - grabbed->tex_id = tex_id; - grabbed->image = image; - brush->add_layer(grabbed); + CLayerTiles *pGrabbed = new CLayerTiles(r.w, r.h); + pGrabbed->m_pEditor = m_pEditor; + pGrabbed->m_TexId = m_TexId; + pGrabbed->m_Image = m_Image; + pBrush->AddLayer(pGrabbed); // copy the tiles for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) - grabbed->tiles[y*grabbed->width+x] = tiles[(r.y+y)*width+(r.x+x)]; + pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = m_pTiles[(r.y+y)*m_Width+(r.x+x)]; return 1; } -void LAYER_TILES::brush_draw(LAYER *brush, float wx, float wy) +void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { - if(readonly) + if(m_Readonly) + return; + + int sx = ConvertX(Rect.x); + int sy = ConvertY(Rect.y); + int w = ConvertX(Rect.w); + int h = ConvertY(Rect.h); + + CLayerTiles *pLt = static_cast<CLayerTiles*>(pBrush); + + for(int y = 0; y <= h; y++) + { + for(int x = 0; x <= w; x++) + { + int fx = x+sx; + int fy = y+sy; + + if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) + continue; + + if(Empty) + m_pTiles[fy*m_Width+fx].m_Index = 1; + else + m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]; + } + } +} + +void CLayerTiles::BrushDraw(CLayer *pBrush, float wx, float wy) +{ + if(m_Readonly) return; // - LAYER_TILES *l = (LAYER_TILES *)brush; - int sx = convert_x(wx); - int sy = convert_y(wy); + CLayerTiles *l = (CLayerTiles *)pBrush; + int sx = ConvertX(wx); + int sy = ConvertY(wy); - for(int y = 0; y < l->height; y++) - for(int x = 0; x < l->width; x++) + for(int y = 0; y < l->m_Height; y++) + for(int x = 0; x < l->m_Width; x++) { int fx = x+sx; int fy = y+sy; - if(fx<0 || fx >= width || fy < 0 || fy >= height) + if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; - tiles[fy*width+fx] = l->tiles[y*l->width+x]; + m_pTiles[fy*m_Width+fx] = l->m_pTiles[y*l->m_Width+x]; } } -void LAYER_TILES::brush_flip_x() +void CLayerTiles::BrushFlipX() { - for(int y = 0; y < height; y++) - for(int x = 0; x < width/2; x++) + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width/2; x++) { - TILE tmp = tiles[y*width+x]; - tiles[y*width+x] = tiles[y*width+width-1-x]; - tiles[y*width+width-1-x] = tmp; + CTile Tmp = m_pTiles[y*m_Width+x]; + m_pTiles[y*m_Width+x] = m_pTiles[y*m_Width+m_Width-1-x]; + m_pTiles[y*m_Width+m_Width-1-x] = Tmp; } - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - tiles[y*width+x].flags ^= TILEFLAG_VFLIP; + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) + m_pTiles[y*m_Width+x].m_Flags ^= TILEFLAG_VFLIP; } -void LAYER_TILES::brush_flip_y() +void CLayerTiles::BrushFlipY() { - for(int y = 0; y < height/2; y++) - for(int x = 0; x < width; x++) + for(int y = 0; y < m_Height/2; y++) + for(int x = 0; x < m_Width; x++) { - TILE tmp = tiles[y*width+x]; - tiles[y*width+x] = tiles[(height-1-y)*width+x]; - tiles[(height-1-y)*width+x] = tmp; + CTile Tmp = m_pTiles[y*m_Width+x]; + m_pTiles[y*m_Width+x] = m_pTiles[(m_Height-1-y)*m_Width+x]; + m_pTiles[(m_Height-1-y)*m_Width+x] = Tmp; } - for(int y = 0; y < height; y++) - for(int x = 0; x < width; x++) - tiles[y*width+x].flags ^= TILEFLAG_HFLIP; + for(int y = 0; y < m_Height; y++) + for(int x = 0; x < m_Width; x++) + m_pTiles[y*m_Width+x].m_Flags ^= TILEFLAG_HFLIP; } -void LAYER_TILES::resize(int new_w, int new_h) +void CLayerTiles::Resize(int NewW, int NewH) { - TILE *new_data = new TILE[new_w*new_h]; - mem_zero(new_data, new_w*new_h*sizeof(TILE)); + CTile *pNewData = new CTile[NewW*NewH]; + mem_zero(pNewData, NewW*NewH*sizeof(CTile)); // copy old data - for(int y = 0; y < min(new_h, height); y++) - mem_copy(&new_data[y*new_w], &tiles[y*width], min(width, new_w)*sizeof(TILE)); + for(int y = 0; y < min(NewH, m_Height); y++) + mem_copy(&pNewData[y*NewW], &m_pTiles[y*m_Width], min(m_Width, NewW)*sizeof(CTile)); // replace old - delete [] tiles; - tiles = new_data; - width = new_w; - height = new_h; + delete [] m_pTiles; + m_pTiles = pNewData; + m_Width = NewW; + m_Height = NewH; } -int LAYER_TILES::render_properties(CUIRect *toolbox) +int CLayerTiles::RenderProperties(CUIRect *pToolBox) { - CUIRect button; - toolbox->HSplitBottom(12.0f, toolbox, &button); - bool in_gamegroup = editor->map.game_group->layers.find(this) != -1; - if(editor->map.game_layer == this) - in_gamegroup = false; - static int colcl_button = 0; - if(editor->DoButton_Editor(&colcl_button, "Clear Collision", in_gamegroup?0:-1, &button, 0, "Removes collision from this layer")) + CUIRect Button; + pToolBox->HSplitBottom(12.0f, pToolBox, &Button); + + bool InGameGroup = !find_linear(m_pEditor->m_Map.m_pGameGroup->m_lLayers.all(), this).empty(); + if(m_pEditor->m_Map.m_pGameLayer == this) + InGameGroup = false; + static int s_ColclButton = 0; + if(m_pEditor->DoButton_Editor(&s_ColclButton, "Clear Collision", InGameGroup?0:-1, &Button, 0, "Removes collision from this layer")) { - LAYER_TILES *gl = editor->map.game_layer; - int w = min(gl->width, width); - int h = min(gl->height, height); + CLayerTiles *gl = m_pEditor->m_Map.m_pGameLayer; + int w = min(gl->m_Width, m_Width); + int h = min(gl->m_Height, m_Height); for(int y = 0; y < h; y++) for(int x = 0; x < w; x++) { - if(gl->tiles[y*gl->width+x].index <= TILE_SOLID) - if(tiles[y*width+x].index) - gl->tiles[y*gl->width+x].index = TILE_AIR; + if(gl->m_pTiles[y*gl->m_Width+x].m_Index <= TILE_SOLID) + if(m_pTiles[y*m_Width+x].m_Index) + gl->m_pTiles[y*gl->m_Width+x].m_Index = TILE_AIR; } return 1; } - static int col_button = 0; - toolbox->HSplitBottom(5.0f, toolbox, &button); - toolbox->HSplitBottom(12.0f, toolbox, &button); - if(editor->DoButton_Editor(&col_button, "Make Collision", in_gamegroup?0:-1, &button, 0, "Constructs collision from this layer")) + static int s_ColButton = 0; + pToolBox->HSplitBottom(5.0f, pToolBox, &Button); + pToolBox->HSplitBottom(12.0f, pToolBox, &Button); + if(m_pEditor->DoButton_Editor(&s_ColButton, "Make Collision", InGameGroup?0:-1, &Button, 0, "Constructs collision from this layer")) { - LAYER_TILES *gl = editor->map.game_layer; - int w = min(gl->width, width); - int h = min(gl->height, height); + CLayerTiles *gl = m_pEditor->m_Map.m_pGameLayer; + int w = min(gl->m_Width, m_Width); + int h = min(gl->m_Height, m_Height); for(int y = 0; y < h; y++) for(int x = 0; x < w; x++) { - if(gl->tiles[y*gl->width+x].index <= TILE_SOLID) - gl->tiles[y*gl->width+x].index = tiles[y*width+x].index?TILE_SOLID:TILE_AIR; + if(gl->m_pTiles[y*gl->m_Width+x].m_Index <= TILE_SOLID) + gl->m_pTiles[y*gl->m_Width+x].m_Index = m_pTiles[y*m_Width+x].m_Index?TILE_SOLID:TILE_AIR; } return 1; @@ -255,44 +289,44 @@ int LAYER_TILES::render_properties(CUIRect *toolbox) NUM_PROPS, }; - PROPERTY props[] = { - {"Width", width, PROPTYPE_INT_STEP, 1, 1000000000}, - {"Height", height, PROPTYPE_INT_STEP, 1, 1000000000}, - {"Image", image, PROPTYPE_IMAGE, 0, 0}, + CProperty aProps[] = { + {"Width", m_Width, PROPTYPE_INT_STEP, 1, 1000000000}, + {"Height", m_Height, PROPTYPE_INT_STEP, 1, 1000000000}, + {"Image", m_Image, PROPTYPE_IMAGE, 0, 0}, {0}, }; - if(editor->map.game_layer == this) // remove the image from the selection if this is the game layer - props[2].name = 0; + if(m_pEditor->m_Map.m_pGameLayer == this) // remove the image from the selection if this is the game layer + aProps[2].m_pName = 0; - static int ids[NUM_PROPS] = {0}; - int new_val = 0; - int prop = editor->do_properties(toolbox, props, ids, &new_val); + static int s_aIds[NUM_PROPS] = {0}; + int NewVal = 0; + int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); - if(prop == PROP_WIDTH && new_val > 1) - resize(new_val, height); - else if(prop == PROP_HEIGHT && new_val > 1) - resize(width, new_val); - else if(prop == PROP_IMAGE) + if(Prop == PROP_WIDTH && NewVal > 1) + Resize(NewVal, m_Height); + else if(Prop == PROP_HEIGHT && NewVal > 1) + Resize(m_Width, NewVal); + else if(Prop == PROP_IMAGE) { - if (new_val == -1) + if (NewVal == -1) { - tex_id = -1; - image = -1; + m_TexId = -1; + m_Image = -1; } else - image = new_val%editor->map.images.len(); + m_Image = NewVal%m_pEditor->m_Map.m_lImages.size(); } return 0; } -void LAYER_TILES::modify_image_index(INDEX_MODIFY_FUNC func) +void CLayerTiles::ModifyImageIndex(INDEX_MODIFY_FUNC Func) { - func(&image); + Func(&m_Image); } -void LAYER_TILES::modify_envelope_index(INDEX_MODIFY_FUNC func) +void CLayerTiles::ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func) { } diff --git a/src/game/editor/ed_popups.cpp b/src/game/editor/ed_popups.cpp index 59140153..2e58ae3a 100644 --- a/src/game/editor/ed_popups.cpp +++ b/src/game/editor/ed_popups.cpp @@ -1,111 +1,114 @@ -#include <stdio.h> -#include <engine/client/graphics.h> -#include "ed_editor.hpp" +#include <engine/graphics.h> +#include <engine/input.h> +#include <engine/keys.h> +#include "ed_editor.h" // popup menu handling static struct { - CUIRect rect; - void *id; - int (*func)(EDITOR *pEditor, CUIRect rect); - int is_menu; - void *extra; -} ui_popups[8]; + CUIRect m_Rect; + void *m_pId; + int (*m_pfnFunc)(CEditor *pEditor, CUIRect Rect); + int m_IsMenu; + void *m_pExtra; +} s_UiPopups[8]; -static int ui_num_popups = 0; +static int g_UiNumPopups = 0; -void EDITOR::ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(EDITOR *pEditor, CUIRect rect), void *extra) +void CEditor::UiInvokePopupMenu(void *Id, int Flags, float x, float y, float w, float h, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra) { dbg_msg("", "invoked"); - ui_popups[ui_num_popups].id = id; - ui_popups[ui_num_popups].is_menu = flags; - ui_popups[ui_num_popups].rect.x = x; - ui_popups[ui_num_popups].rect.y = y; - ui_popups[ui_num_popups].rect.w = w; - ui_popups[ui_num_popups].rect.h = h; - ui_popups[ui_num_popups].func = func; - ui_popups[ui_num_popups].extra = extra; - ui_num_popups++; + s_UiPopups[g_UiNumPopups].m_pId = Id; + s_UiPopups[g_UiNumPopups].m_IsMenu = Flags; + s_UiPopups[g_UiNumPopups].m_Rect.x = x; + s_UiPopups[g_UiNumPopups].m_Rect.y = y; + s_UiPopups[g_UiNumPopups].m_Rect.w = w; + s_UiPopups[g_UiNumPopups].m_Rect.h = h; + s_UiPopups[g_UiNumPopups].m_pfnFunc = pfnFunc; + s_UiPopups[g_UiNumPopups].m_pExtra = pExtra; + g_UiNumPopups++; } -void EDITOR::ui_do_popup_menu() +void CEditor::UiDoPopupMenu() { - for(int i = 0; i < ui_num_popups; i++) + for(int i = 0; i < g_UiNumPopups; i++) { - bool inside = UI()->MouseInside(&ui_popups[i].rect); - UI()->SetHotItem(&ui_popups[i].id); + bool Inside = UI()->MouseInside(&s_UiPopups[i].m_Rect); + UI()->SetHotItem(&s_UiPopups[i].m_pId); - if(UI()->ActiveItem() == &ui_popups[i].id) + if(UI()->ActiveItem() == &s_UiPopups[i].m_pId) { if(!UI()->MouseButton(0)) { - if(!inside) - ui_num_popups--; + if(!Inside) + g_UiNumPopups--; UI()->SetActiveItem(0); } } - else if(UI()->HotItem() == &ui_popups[i].id) + else if(UI()->HotItem() == &s_UiPopups[i].m_pId) { if(UI()->MouseButton(0)) - UI()->SetActiveItem(&ui_popups[i].id); + UI()->SetActiveItem(&s_UiPopups[i].m_pId); } - int corners = CUI::CORNER_ALL; - if(ui_popups[i].is_menu) - corners = CUI::CORNER_R|CUI::CORNER_B; + int Corners = CUI::CORNER_ALL; + if(s_UiPopups[i].m_IsMenu) + Corners = CUI::CORNER_R|CUI::CORNER_B; - CUIRect r = ui_popups[i].rect; - RenderTools()->DrawUIRect(&r, vec4(0.5f,0.5f,0.5f,0.75f), corners, 3.0f); + CUIRect r = s_UiPopups[i].m_Rect; + RenderTools()->DrawUIRect(&r, vec4(0.5f,0.5f,0.5f,0.75f), Corners, 3.0f); r.Margin(1.0f, &r); - RenderTools()->DrawUIRect(&r, vec4(0,0,0,0.75f), corners, 3.0f); + RenderTools()->DrawUIRect(&r, vec4(0,0,0,0.75f), Corners, 3.0f); r.Margin(4.0f, &r); - if(ui_popups[i].func(this, r)) - ui_num_popups--; + if(s_UiPopups[i].m_pfnFunc(this, r)) + g_UiNumPopups--; - if(inp_key_down(KEY_ESCAPE)) - ui_num_popups--; + if(Input()->KeyDown(KEY_ESCAPE)) + g_UiNumPopups--; } } -int EDITOR::popup_group(EDITOR *pEditor, CUIRect view) +int CEditor::PopupGroup(CEditor *pEditor, CUIRect View) { // remove group button - CUIRect button; - view.HSplitBottom(12.0f, &view, &button); - static int delete_button = 0; + CUIRect Button; + View.HSplitBottom(12.0f, &View, &Button); + static int s_DeleteButton = 0; // don't allow deletion of game group - if(pEditor->map.game_group != pEditor->get_selected_group() && - pEditor->DoButton_Editor(&delete_button, "Delete Group", 0, &button, 0, "Delete group")) + if(pEditor->m_Map.m_pGameGroup != pEditor->GetSelectedGroup() && + pEditor->DoButton_Editor(&s_DeleteButton, "Delete Group", 0, &Button, 0, "Delete group")) { - pEditor->map.delete_group(pEditor->selected_group); + pEditor->m_Map.DeleteGroup(pEditor->m_SelectedGroup); return 1; } // new tile layer - view.HSplitBottom(10.0f, &view, &button); - view.HSplitBottom(12.0f, &view, &button); - static int new_quad_layer_button = 0; - if(pEditor->DoButton_Editor(&new_quad_layer_button, "Add Quads Layer", 0, &button, 0, "Creates a new quad layer")) + View.HSplitBottom(10.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static int s_NewQuadLayerButton = 0; + if(pEditor->DoButton_Editor(&s_NewQuadLayerButton, "Add Quads Layer", 0, &Button, 0, "Creates a new quad layer")) { - LAYER *l = new LAYER_QUADS; - pEditor->map.groups[pEditor->selected_group]->add_layer(l); - pEditor->selected_layer = pEditor->map.groups[pEditor->selected_group]->layers.len()-1; + CLayer *l = new CLayerQuads; + l->m_pEditor = pEditor; + pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l); + pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1; return 1; } // new quad layer - view.HSplitBottom(5.0f, &view, &button); - view.HSplitBottom(12.0f, &view, &button); - static int new_tile_layer_button = 0; - if(pEditor->DoButton_Editor(&new_tile_layer_button, "Add Tile Layer", 0, &button, 0, "Creates a new tile layer")) + View.HSplitBottom(5.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static int s_NewTileLayerButton = 0; + if(pEditor->DoButton_Editor(&s_NewTileLayerButton, "Add Tile Layer", 0, &Button, 0, "Creates a new tile layer")) { - LAYER *l = new LAYER_TILES(50, 50); - pEditor->map.groups[pEditor->selected_group]->add_layer(l); - pEditor->selected_layer = pEditor->map.groups[pEditor->selected_group]->layers.len()-1; + CLayer *l = new CLayerTiles(50, 50); + l->m_pEditor = pEditor; + pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l); + pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1; return 1; } @@ -124,68 +127,68 @@ int EDITOR::popup_group(EDITOR *pEditor, CUIRect view) NUM_PROPS, }; - PROPERTY props[] = { - {"Order", pEditor->selected_group, PROPTYPE_INT_STEP, 0, pEditor->map.groups.len()-1}, - {"Pos X", -pEditor->map.groups[pEditor->selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Pos Y", -pEditor->map.groups[pEditor->selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Para X", pEditor->map.groups[pEditor->selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Para Y", pEditor->map.groups[pEditor->selected_group]->parallax_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - - {"Use Clipping", pEditor->map.groups[pEditor->selected_group]->use_clipping, PROPTYPE_BOOL, 0, 1}, - {"Clip X", pEditor->map.groups[pEditor->selected_group]->clip_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Clip Y", pEditor->map.groups[pEditor->selected_group]->clip_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Clip W", pEditor->map.groups[pEditor->selected_group]->clip_w, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Clip H", pEditor->map.groups[pEditor->selected_group]->clip_h, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + CProperty aProps[] = { + {"Order", pEditor->m_SelectedGroup, PROPTYPE_INT_STEP, 0, pEditor->m_Map.m_lGroups.size()-1}, + {"Pos X", -pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_OffsetX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Pos Y", -pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_OffsetY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para X", pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ParallaxX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para Y", pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ParallaxY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + + {"Use Clipping", pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_UseClipping, PROPTYPE_BOOL, 0, 1}, + {"Clip X", pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipX, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Clip Y", pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipY, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Clip W", pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipW, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Clip H", pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipH, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {0}, }; - static int ids[NUM_PROPS] = {0}; - int new_val = 0; + static int s_aIds[NUM_PROPS] = {0}; + int NewVal = 0; // cut the properties that isn't needed - if(pEditor->get_selected_group()->game_group) - props[PROP_POS_X].name = 0; + if(pEditor->GetSelectedGroup()->m_GameGroup) + aProps[PROP_POS_X].m_pName = 0; - int prop = pEditor->do_properties(&view, props, ids, &new_val); - if(prop == PROP_ORDER) - pEditor->selected_group = pEditor->map.swap_groups(pEditor->selected_group, new_val); + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + if(Prop == PROP_ORDER) + pEditor->m_SelectedGroup = pEditor->m_Map.SwapGroups(pEditor->m_SelectedGroup, NewVal); // these can not be changed on the game group - if(!pEditor->get_selected_group()->game_group) + if(!pEditor->GetSelectedGroup()->m_GameGroup) { - if(prop == PROP_PARA_X) pEditor->map.groups[pEditor->selected_group]->parallax_x = new_val; - else if(prop == PROP_PARA_Y) pEditor->map.groups[pEditor->selected_group]->parallax_y = new_val; - else if(prop == PROP_POS_X) pEditor->map.groups[pEditor->selected_group]->offset_x = -new_val; - else if(prop == PROP_POS_Y) pEditor->map.groups[pEditor->selected_group]->offset_y = -new_val; - else if(prop == PROP_USE_CLIPPING) pEditor->map.groups[pEditor->selected_group]->use_clipping = new_val; - else if(prop == PROP_CLIP_X) pEditor->map.groups[pEditor->selected_group]->clip_x = new_val; - else if(prop == PROP_CLIP_Y) pEditor->map.groups[pEditor->selected_group]->clip_y = new_val; - else if(prop == PROP_CLIP_W) pEditor->map.groups[pEditor->selected_group]->clip_w = new_val; - else if(prop == PROP_CLIP_H) pEditor->map.groups[pEditor->selected_group]->clip_h = new_val; + if(Prop == PROP_PARA_X) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ParallaxX = NewVal; + else if(Prop == PROP_PARA_Y) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ParallaxY = NewVal; + else if(Prop == PROP_POS_X) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_OffsetX = -NewVal; + else if(Prop == PROP_POS_Y) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_OffsetY = -NewVal; + else if(Prop == PROP_USE_CLIPPING) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_UseClipping = NewVal; + else if(Prop == PROP_CLIP_X) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipX = NewVal; + else if(Prop == PROP_CLIP_Y) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipY = NewVal; + else if(Prop == PROP_CLIP_W) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipW = NewVal; + else if(Prop == PROP_CLIP_H) pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_ClipH = NewVal; } return 0; } -int EDITOR::popup_layer(EDITOR *pEditor, CUIRect view) +int CEditor::PopupLayer(CEditor *pEditor, CUIRect View) { // remove layer button - CUIRect button; - view.HSplitBottom(12.0f, &view, &button); - static int delete_button = 0; + CUIRect Button; + View.HSplitBottom(12.0f, &View, &Button); + static int s_DeleteButton = 0; // don't allow deletion of game layer - if(pEditor->map.game_layer != pEditor->get_selected_layer(0) && - pEditor->DoButton_Editor(&delete_button, "Delete Layer", 0, &button, 0, "Deletes the layer")) + if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0) && + pEditor->DoButton_Editor(&s_DeleteButton, "Delete Layer", 0, &Button, 0, "Deletes the layer")) { - pEditor->map.groups[pEditor->selected_group]->delete_layer(pEditor->selected_layer); + pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->DeleteLayer(pEditor->m_SelectedLayer); return 1; } - view.HSplitBottom(10.0f, &view, 0); + View.HSplitBottom(10.0f, &View, 0); - LAYERGROUP *current_group = pEditor->map.groups[pEditor->selected_group]; - LAYER *current_layer = pEditor->get_selected_layer(0); + CLayerGroup *pCurrentGroup = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]; + CLayer *pCurrentLayer = pEditor->GetSelectedLayer(0); enum { @@ -195,82 +198,88 @@ int EDITOR::popup_layer(EDITOR *pEditor, CUIRect view) NUM_PROPS, }; - PROPERTY props[] = { - {"Group", pEditor->selected_group, PROPTYPE_INT_STEP, 0, pEditor->map.groups.len()-1}, - {"Order", pEditor->selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()}, - {"Detail", current_layer->flags&LAYERFLAG_DETAIL, PROPTYPE_BOOL, 0, 1}, + CProperty aProps[] = { + {"Group", pEditor->m_SelectedGroup, PROPTYPE_INT_STEP, 0, pEditor->m_Map.m_lGroups.size()-1}, + {"Order", pEditor->m_SelectedLayer, PROPTYPE_INT_STEP, 0, pCurrentGroup->m_lLayers.size()}, + {"Detail", pCurrentLayer->m_Flags&LAYERFLAG_DETAIL, PROPTYPE_BOOL, 0, 1}, {0}, }; + + if(pEditor->m_Map.m_pGameLayer == pEditor->GetSelectedLayer(0)) // dont use Group and Detail from the selection if this is the game layer + { + aProps[0].m_Type = PROPTYPE_NULL; + aProps[2].m_Type = PROPTYPE_NULL; + } - static int ids[NUM_PROPS] = {0}; - int new_val = 0; - int prop = pEditor->do_properties(&view, props, ids, &new_val); + static int s_aIds[NUM_PROPS] = {0}; + int NewVal = 0; + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); - if(prop == PROP_ORDER) - pEditor->selected_layer = current_group->swap_layers(pEditor->selected_layer, new_val); - else if(prop == PROP_GROUP && current_layer->type != LAYERTYPE_GAME) + if(Prop == PROP_ORDER) + pEditor->m_SelectedLayer = pCurrentGroup->SwapLayers(pEditor->m_SelectedLayer, NewVal); + else if(Prop == PROP_GROUP && pCurrentLayer->m_Type != LAYERTYPE_GAME) { - if(new_val >= 0 && new_val < pEditor->map.groups.len()) + if(NewVal >= 0 && NewVal < pEditor->m_Map.m_lGroups.size()) { - current_group->layers.remove(current_layer); - pEditor->map.groups[new_val]->layers.add(current_layer); - pEditor->selected_group = new_val; - pEditor->selected_layer = pEditor->map.groups[new_val]->layers.len()-1; + pCurrentGroup->m_lLayers.remove(pCurrentLayer); + pEditor->m_Map.m_lGroups[NewVal]->m_lLayers.add(pCurrentLayer); + pEditor->m_SelectedGroup = NewVal; + pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[NewVal]->m_lLayers.size()-1; } } - else if(prop == PROP_HQ) + else if(Prop == PROP_HQ) { - current_layer->flags &= ~LAYERFLAG_DETAIL; - if(new_val) - current_layer->flags |= LAYERFLAG_DETAIL; + pCurrentLayer->m_Flags &= ~LAYERFLAG_DETAIL; + if(NewVal) + pCurrentLayer->m_Flags |= LAYERFLAG_DETAIL; } - return current_layer->render_properties(&view); + return pCurrentLayer->RenderProperties(&View); } -int EDITOR::popup_quad(EDITOR *pEditor, CUIRect view) +int CEditor::PopupQuad(CEditor *pEditor, CUIRect View) { - QUAD *quad = pEditor->get_selected_quad(); + CQuad *pQuad = pEditor->GetSelectedQuad(); - CUIRect button; + CUIRect Button; // delete button - view.HSplitBottom(12.0f, &view, &button); - static int delete_button = 0; - if(pEditor->DoButton_Editor(&delete_button, "Delete", 0, &button, 0, "Deletes the current quad")) + View.HSplitBottom(12.0f, &View, &Button); + static int s_DeleteButton = 0; + if(pEditor->DoButton_Editor(&s_DeleteButton, "Delete", 0, &Button, 0, "Deletes the current quad")) { - LAYER_QUADS *layer = (LAYER_QUADS *)pEditor->get_selected_layer_type(0, LAYERTYPE_QUADS); - if(layer) + CLayerQuads *pLayer = (CLayerQuads *)pEditor->GetSelectedLayerType(0, LAYERTYPE_QUADS); + if(pLayer) { - layer->quads.removebyindex(pEditor->selected_quad); - pEditor->selected_quad--; + pLayer->m_lQuads.remove_index(pEditor->m_SelectedQuad); + pEditor->m_SelectedQuad--; } return 1; } // square button - view.HSplitBottom(10.0f, &view, &button); - view.HSplitBottom(12.0f, &view, &button); - static int sq_button = 0; - if(pEditor->DoButton_Editor(&sq_button, "Square", 0, &button, 0, "Squares the current quad")) + View.HSplitBottom(10.0f, &View, &Button); + View.HSplitBottom(12.0f, &View, &Button); + static int s_Button = 0; + if(pEditor->DoButton_Editor(&s_Button, "Square", 0, &Button, 0, "Squares the current quad")) { - int top = quad->points[0].y; - int left = quad->points[0].x; - int bottom = quad->points[0].y; - int right = quad->points[0].x; + int Top = pQuad->m_aPoints[0].y; + int Left = pQuad->m_aPoints[0].x; + int Bottom = pQuad->m_aPoints[0].y; + int Right = pQuad->m_aPoints[0].x; for(int k = 1; k < 4; k++) { - if(quad->points[k].y < top) top = quad->points[k].y; - if(quad->points[k].x < left) left = quad->points[k].x; - if(quad->points[k].y > bottom) bottom = quad->points[k].y; - if(quad->points[k].x > right) right = quad->points[k].x; + if(pQuad->m_aPoints[k].y < Top) Top = pQuad->m_aPoints[k].y; + if(pQuad->m_aPoints[k].x < Left) Left = pQuad->m_aPoints[k].x; + if(pQuad->m_aPoints[k].y > Bottom) Bottom = pQuad->m_aPoints[k].y; + if(pQuad->m_aPoints[k].x > Right) Right = pQuad->m_aPoints[k].x; } - quad->points[0].x = left; quad->points[0].y = top; - quad->points[1].x = right; quad->points[1].y = top; - quad->points[2].x = left; quad->points[2].y = bottom; - quad->points[3].x = right; quad->points[3].y = bottom; + pQuad->m_aPoints[0].x = Left; pQuad->m_aPoints[0].y = Top; + pQuad->m_aPoints[1].x = Right; pQuad->m_aPoints[1].y = Top; + pQuad->m_aPoints[2].x = Left; pQuad->m_aPoints[2].y = Bottom; + pQuad->m_aPoints[3].x = Right; pQuad->m_aPoints[3].y = Bottom; return 1; } @@ -284,30 +293,30 @@ int EDITOR::popup_quad(EDITOR *pEditor, CUIRect view) NUM_PROPS, }; - PROPERTY props[] = { - {"Pos. Env", quad->pos_env, PROPTYPE_INT_STEP, -1, pEditor->map.envelopes.len()}, - {"Pos. TO", quad->pos_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Color Env", quad->color_env, PROPTYPE_INT_STEP, -1, pEditor->map.envelopes.len()}, - {"Color TO", quad->color_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + CProperty aProps[] = { + {"Pos. Env", pQuad->m_PosEnv, PROPTYPE_INT_STEP, -1, pEditor->m_Map.m_lEnvelopes.size()}, + {"Pos. TO", pQuad->m_PosEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Color Env", pQuad->m_ColorEnv, PROPTYPE_INT_STEP, -1, pEditor->m_Map.m_lEnvelopes.size()}, + {"Color TO", pQuad->m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {0}, }; - static int ids[NUM_PROPS] = {0}; - int new_val = 0; - int prop = pEditor->do_properties(&view, props, ids, &new_val); + static int s_aIds[NUM_PROPS] = {0}; + int NewVal = 0; + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); - if(prop == PROP_POS_ENV) quad->pos_env = clamp(new_val, -1, pEditor->map.envelopes.len()-1); - if(prop == PROP_POS_ENV_OFFSET) quad->pos_env_offset = new_val; - if(prop == PROP_COLOR_ENV) quad->color_env = clamp(new_val, -1, pEditor->map.envelopes.len()-1); - if(prop == PROP_COLOR_ENV_OFFSET) quad->color_env_offset = new_val; + if(Prop == PROP_POS_ENV) pQuad->m_PosEnv = clamp(NewVal, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + if(Prop == PROP_POS_ENV_OFFSET) pQuad->m_PosEnvOffset = NewVal; + if(Prop == PROP_COLOR_ENV) pQuad->m_ColorEnv = clamp(NewVal, -1, pEditor->m_Map.m_lEnvelopes.size()-1); + if(Prop == PROP_COLOR_ENV_OFFSET) pQuad->m_ColorEnvOffset = NewVal; return 0; } -int EDITOR::popup_point(EDITOR *pEditor, CUIRect view) +int CEditor::PopupPoint(CEditor *pEditor, CUIRect View) { - QUAD *quad = pEditor->get_selected_quad(); + CQuad *pQuad = pEditor->GetSelectedQuad(); enum { @@ -315,40 +324,40 @@ int EDITOR::popup_point(EDITOR *pEditor, CUIRect view) NUM_PROPS, }; - int color = 0; + int Color = 0; for(int v = 0; v < 4; v++) { - if(pEditor->selected_points&(1<<v)) + if(pEditor->m_SelectedPoints&(1<<v)) { - color = 0; - color |= quad->colors[v].r<<24; - color |= quad->colors[v].g<<16; - color |= quad->colors[v].b<<8; - color |= quad->colors[v].a; + Color = 0; + Color |= pQuad->m_aColors[v].r<<24; + Color |= pQuad->m_aColors[v].g<<16; + Color |= pQuad->m_aColors[v].b<<8; + Color |= pQuad->m_aColors[v].a; } } - PROPERTY props[] = { - {"Color", color, PROPTYPE_COLOR, -1, pEditor->map.envelopes.len()}, + CProperty aProps[] = { + {"Color", Color, PROPTYPE_COLOR, -1, pEditor->m_Map.m_lEnvelopes.size()}, {0}, }; - static int ids[NUM_PROPS] = {0}; - int new_val = 0; - int prop = pEditor->do_properties(&view, props, ids, &new_val); - if(prop == PROP_COLOR) + static int s_aIds[NUM_PROPS] = {0}; + int NewVal = 0; + int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); + if(Prop == PROP_COLOR) { for(int v = 0; v < 4; v++) { - if(pEditor->selected_points&(1<<v)) + if(pEditor->m_SelectedPoints&(1<<v)) { - color = 0; - quad->colors[v].r = (new_val>>24)&0xff; - quad->colors[v].g = (new_val>>16)&0xff; - quad->colors[v].b = (new_val>>8)&0xff; - quad->colors[v].a = new_val&0xff; + Color = 0; + pQuad->m_aColors[v].r = (NewVal>>24)&0xff; + pQuad->m_aColors[v].g = (NewVal>>16)&0xff; + pQuad->m_aColors[v].b = (NewVal>>8)&0xff; + pQuad->m_aColors[v].a = NewVal&0xff; } } } @@ -358,65 +367,66 @@ int EDITOR::popup_point(EDITOR *pEditor, CUIRect view) -static int select_image_selected = -100; -static int select_image_current = -100; +static int g_SelectImageSelected = -100; +static int g_SelectImageCurrent = -100; -int EDITOR::popup_select_image(EDITOR *pEditor, CUIRect view) +int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View) { - CUIRect buttonbar, imageview; - view.VSplitLeft(80.0f, &buttonbar, &view); - view.Margin(10.0f, &imageview); + CUIRect ButtonBar, ImageView; + View.VSplitLeft(80.0f, &ButtonBar, &View); + View.Margin(10.0f, &ImageView); - int show_image = select_image_current; + int ShowImage = g_SelectImageCurrent; - for(int i = -1; i < pEditor->map.images.len(); i++) + for(int i = -1; i < pEditor->m_Map.m_lImages.size(); i++) { - CUIRect button; - buttonbar.HSplitTop(12.0f, &button, &buttonbar); - buttonbar.HSplitTop(2.0f, 0, &buttonbar); + CUIRect Button; + ButtonBar.HSplitTop(12.0f, &Button, &ButtonBar); + ButtonBar.HSplitTop(2.0f, 0, &ButtonBar); - if(pEditor->UI()->MouseInside(&button)) - show_image = i; + if(pEditor->UI()->MouseInside(&Button)) + ShowImage = i; if(i == -1) { - if(pEditor->DoButton_MenuItem(&pEditor->map.images[i], "None", i==select_image_current, &button)) - select_image_selected = -1; + if(pEditor->DoButton_MenuItem(&pEditor->m_Map.m_lImages[i], "None", i==g_SelectImageCurrent, &Button)) + g_SelectImageSelected = -1; } else { - if(pEditor->DoButton_MenuItem(&pEditor->map.images[i], pEditor->map.images[i]->name, i==select_image_current, &button)) - select_image_selected = i; + if(pEditor->DoButton_MenuItem(&pEditor->m_Map.m_lImages[i], pEditor->m_Map.m_lImages[i]->m_aName, i==g_SelectImageCurrent, &Button)) + g_SelectImageSelected = i; } } - if(show_image >= 0 && show_image < pEditor->map.images.len()) - pEditor->Graphics()->TextureSet(pEditor->map.images[show_image]->tex_id); + if(ShowImage >= 0 && ShowImage < pEditor->m_Map.m_lImages.size()) + pEditor->Graphics()->TextureSet(pEditor->m_Map.m_lImages[ShowImage]->m_TexId); else pEditor->Graphics()->TextureSet(-1); pEditor->Graphics()->QuadsBegin(); - pEditor->Graphics()->QuadsDrawTL(imageview.x, imageview.y, imageview.w, imageview.h); + IGraphics::CQuadItem QuadItem(ImageView.x, ImageView.y, ImageView.w, ImageView.h); + pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); pEditor->Graphics()->QuadsEnd(); return 0; } -void EDITOR::popup_select_image_invoke(int current, float x, float y) +void CEditor::PopupSelectImageInvoke(int Current, float x, float y) { - static int select_image_popup_id = 0; - select_image_selected = -100; - select_image_current = current; - ui_invoke_popup_menu(&select_image_popup_id, 0, x, y, 400, 300, popup_select_image); + static int s_SelectImagePopupId = 0; + g_SelectImageSelected = -100; + g_SelectImageCurrent = Current; + UiInvokePopupMenu(&s_SelectImagePopupId, 0, x, y, 400, 300, PopupSelectImage); } -int EDITOR::popup_select_image_result() +int CEditor::PopupSelectImageResult() { - if(select_image_selected == -100) + if(g_SelectImageSelected == -100) return -100; - select_image_current = select_image_selected; - select_image_selected = -100; - return select_image_current; + g_SelectImageCurrent = g_SelectImageSelected; + g_SelectImageSelected = -100; + return g_SelectImageCurrent; } diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index ceb63be3..f5aa18cf 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -1,417 +1,311 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> -#include "gamecore.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include "gamecore.h" -const char *TUNING_PARAMS::names[] = +const char *CTuningParams::m_apNames[] = { - #define MACRO_TUNING_PARAM(name,value) #name, - #include "tuning.hpp" + #define MACRO_TUNING_PARAM(Name,ScriptName,Value) #ScriptName, + #include "tuning.h" #undef MACRO_TUNING_PARAM }; -bool TUNING_PARAMS::set(int index, float value) +bool CTuningParams::Set(int Index, float Value) { - if(index < 0 || index >= num()) + if(Index < 0 || Index >= Num()) return false; - ((tune_param *)this)[index] = value; + ((CTuneParam *)this)[Index] = Value; return true; } -bool TUNING_PARAMS::get(int index, float *value) +bool CTuningParams::Get(int Index, float *pValue) { - if(index < 0 || index >= num()) + if(Index < 0 || Index >= Num()) return false; - *value = (float)((tune_param *)this)[index]; + *pValue = (float)((CTuneParam *)this)[Index]; return true; } -bool TUNING_PARAMS::set(const char *name, float value) +bool CTuningParams::Set(const char *pName, float Value) { - for(int i = 0; i < num(); i++) - if(strcmp(name, names[i]) == 0) - return set(i, value); + for(int i = 0; i < Num(); i++) + if(str_comp_nocase(pName, m_apNames[i]) == 0) + return Set(i, Value); return false; } -bool TUNING_PARAMS::get(const char *name, float *value) +bool CTuningParams::Get(const char *pName, float *pValue) { - for(int i = 0; i < num(); i++) - if(strcmp(name, names[i]) == 0) - return get(i, value); + for(int i = 0; i < Num(); i++) + if(str_comp_nocase(pName, m_apNames[i]) == 0) + return Get(i, pValue); return false; } -// TODO: OPT: rewrite this smarter! -void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces) +float HermiteBasis1(float v) { - if(bounces) - *bounces = 0; - - vec2 pos = *inout_pos; - vec2 vel = *inout_vel; - if(col_check_point(pos + vel)) - { - int affected = 0; - if(col_check_point(pos.x + vel.x, pos.y)) - { - inout_vel->x *= -elasticity; - if(bounces) - (*bounces)++; - affected++; - } - - if(col_check_point(pos.x, pos.y + vel.y)) - { - inout_vel->y *= -elasticity; - if(bounces) - (*bounces)++; - affected++; - } - - if(affected == 0) - { - inout_vel->x *= -elasticity; - inout_vel->y *= -elasticity; - } - } - else - { - *inout_pos = pos + vel; - } -} - -bool test_box(vec2 pos, vec2 size) -{ - size *= 0.5f; - if(col_check_point(pos.x-size.x, pos.y-size.y)) - return true; - if(col_check_point(pos.x+size.x, pos.y-size.y)) - return true; - if(col_check_point(pos.x-size.x, pos.y+size.y)) - return true; - if(col_check_point(pos.x+size.x, pos.y+size.y)) - return true; - return false; -} - -void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity) -{ - // do the move - vec2 pos = *inout_pos; - vec2 vel = *inout_vel; - - float distance = length(vel); - int max = (int)distance; - - if(distance > 0.00001f) - { - //vec2 old_pos = pos; - float fraction = 1.0f/(float)(max+1); - for(int i = 0; i <= max; i++) - { - //float amount = i/(float)max; - //if(max == 0) - //amount = 0; - - vec2 new_pos = pos + vel*fraction; // TODO: this row is not nice - - if(test_box(vec2(new_pos.x, new_pos.y), size)) - { - int hits = 0; - - if(test_box(vec2(pos.x, new_pos.y), size)) - { - new_pos.y = pos.y; - vel.y *= -elasticity; - hits++; - } - - if(test_box(vec2(new_pos.x, pos.y), size)) - { - new_pos.x = pos.x; - vel.x *= -elasticity; - hits++; - } - - // neither of the tests got a collision. - // this is a real _corner case_! - if(hits == 0) - { - new_pos.y = pos.y; - vel.y *= -elasticity; - new_pos.x = pos.x; - vel.x *= -elasticity; - } - } - - pos = new_pos; - } - } - - *inout_pos = pos; - *inout_vel = vel; + return 2*v*v*v - 3*v*v+1; } -float hermite_basis1(float v) +float VelocityRamp(float Value, float Start, float Range, float Curvature) { - return 2*v*v*v - 3*v*v+1; + if(Value < Start) + return 1.0f; + return 1.0f/powf(Curvature, (Value-Start)/Range); } -float velocity_ramp(float value, float start, float range, float curvature) +void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision) { - if(value < start) - return 1.0f; - return 1.0f/pow(curvature, (value-start)/range); + m_pWorld = pWorld; + m_pCollision = pCollision; } -void CHARACTER_CORE::reset() +void CCharacterCore::Reset() { - pos = vec2(0,0); - vel = vec2(0,0); - hook_pos = vec2(0,0); - hook_dir = vec2(0,0); - hook_tick = 0; - hook_state = HOOK_IDLE; - hooked_player = -1; - jumped = 0; - triggered_events = 0; + m_Pos = vec2(0,0); + m_Vel = vec2(0,0); + m_HookPos = vec2(0,0); + m_HookDir = vec2(0,0); + m_HookTick = 0; + m_HookState = HOOK_IDLE; + m_HookedPlayer = -1; + m_Jumped = 0; + m_TriggeredEvents = 0; } -void CHARACTER_CORE::tick(bool use_input) +void CCharacterCore::Tick(bool UseInput) { - float phys_size = 28.0f; - triggered_events = 0; + float PhysSize = 28.0f; + m_TriggeredEvents = 0; // get ground state - bool grounded = false; - if(col_check_point(pos.x+phys_size/2, pos.y+phys_size/2+5)) - grounded = true; - if(col_check_point(pos.x-phys_size/2, pos.y+phys_size/2+5)) - grounded = true; + bool Grounded = false; + if(m_pCollision->CheckPoint(m_Pos.x+PhysSize/2, m_Pos.y+PhysSize/2+5)) + Grounded = true; + if(m_pCollision->CheckPoint(m_Pos.x-PhysSize/2, m_Pos.y+PhysSize/2+5)) + Grounded = true; - vec2 target_direction = normalize(vec2(input.target_x, input.target_y)); + vec2 TargetDirection = normalize(vec2(m_Input.m_TargetX, m_Input.m_TargetY)); - vel.y += world->tuning.gravity; + m_Vel.y += m_pWorld->m_Tuning.m_Gravity; - float max_speed = grounded ? world->tuning.ground_control_speed : world->tuning.air_control_speed; - float accel = grounded ? world->tuning.ground_control_accel : world->tuning.air_control_accel; - float friction = grounded ? world->tuning.ground_friction : world->tuning.air_friction; + float MaxSpeed = Grounded ? m_pWorld->m_Tuning.m_GroundControlSpeed : m_pWorld->m_Tuning.m_AirControlSpeed; + float Accel = Grounded ? m_pWorld->m_Tuning.m_GroundControlAccel : m_pWorld->m_Tuning.m_AirControlAccel; + float Friction = Grounded ? m_pWorld->m_Tuning.m_GroundFriction : m_pWorld->m_Tuning.m_AirFriction; // handle input - if(use_input) + if(UseInput) { - direction = input.direction; + m_Direction = m_Input.m_Direction; // setup angle float a = 0; - if(input.target_x == 0) - a = atan((float)input.target_y); + if(m_Input.m_TargetX == 0) + a = atanf((float)m_Input.m_TargetY); else - a = atan((float)input.target_y/(float)input.target_x); + a = atanf((float)m_Input.m_TargetY/(float)m_Input.m_TargetX); - if(input.target_x < 0) + if(m_Input.m_TargetX < 0) a = a+pi; - angle = (int)(a*256.0f); + m_Angle = (int)(a*256.0f); // handle jump - if(input.jump) + if(m_Input.m_Jump) { - if(!(jumped&1)) + if(!(m_Jumped&1)) { - if(grounded) + if(Grounded) { - triggered_events |= COREEVENT_GROUND_JUMP; - vel.y = -world->tuning.ground_jump_impulse; - jumped |= 1; + m_TriggeredEvents |= COREEVENT_GROUND_JUMP; + m_Vel.y = -m_pWorld->m_Tuning.m_GroundJumpImpulse; + m_Jumped |= 1; } - else if(!(jumped&2)) + else if(!(m_Jumped&2)) { - triggered_events |= COREEVENT_AIR_JUMP; - vel.y = -world->tuning.air_jump_impulse; - jumped |= 3; + m_TriggeredEvents |= COREEVENT_AIR_JUMP; + m_Vel.y = -m_pWorld->m_Tuning.m_AirJumpImpulse; + m_Jumped |= 3; } } } else - jumped &= ~1; + m_Jumped &= ~1; // handle hook - if(input.hook) + if(m_Input.m_Hook) { - if(hook_state == HOOK_IDLE) + if(m_HookState == HOOK_IDLE) { - hook_state = HOOK_FLYING; - hook_pos = pos+target_direction*phys_size*1.5f; - hook_dir = target_direction; - hooked_player = -1; - hook_tick = 0; - triggered_events |= COREEVENT_HOOK_LAUNCH; + m_HookState = HOOK_FLYING; + m_HookPos = m_Pos+TargetDirection*PhysSize*1.5f; + m_HookDir = TargetDirection; + m_HookedPlayer = -1; + m_HookTick = 0; + m_TriggeredEvents |= COREEVENT_HOOK_LAUNCH; } } else { - hooked_player = -1; - hook_state = HOOK_IDLE; - hook_pos = pos; + m_HookedPlayer = -1; + m_HookState = HOOK_IDLE; + m_HookPos = m_Pos; } } // add the speed modification according to players wanted direction - if(direction < 0) - vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel); - if(direction > 0) - vel.x = saturated_add(-max_speed, max_speed, vel.x, accel); - if(direction == 0) - vel.x *= friction; + if(m_Direction < 0) + m_Vel.x = SaturatedAdd(-MaxSpeed, MaxSpeed, m_Vel.x, -Accel); + if(m_Direction > 0) + m_Vel.x = SaturatedAdd(-MaxSpeed, MaxSpeed, m_Vel.x, Accel); + if(m_Direction == 0) + m_Vel.x *= Friction; // handle jumping // 1 bit = to keep track if a jump has been made on this input // 2 bit = to keep track if a air-jump has been made - if(grounded) - jumped &= ~2; + if(Grounded) + m_Jumped &= ~2; // do hook - if(hook_state == HOOK_IDLE) + if(m_HookState == HOOK_IDLE) { - hooked_player = -1; - hook_state = HOOK_IDLE; - hook_pos = pos; + m_HookedPlayer = -1; + m_HookState = HOOK_IDLE; + m_HookPos = m_Pos; } - else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END) + else if(m_HookState >= HOOK_RETRACT_START && m_HookState < HOOK_RETRACT_END) { - hook_state++; + m_HookState++; } - else if(hook_state == HOOK_RETRACT_END) + else if(m_HookState == HOOK_RETRACT_END) { - hook_state = HOOK_RETRACTED; - triggered_events |= COREEVENT_HOOK_RETRACT; - hook_state = HOOK_RETRACTED; + m_HookState = HOOK_RETRACTED; + m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; + m_HookState = HOOK_RETRACTED; } - else if(hook_state == HOOK_FLYING) + else if(m_HookState == HOOK_FLYING) { - vec2 new_pos = hook_pos+hook_dir*world->tuning.hook_fire_speed; - if(distance(pos, new_pos) > world->tuning.hook_length) + vec2 NewPos = m_HookPos+m_HookDir*m_pWorld->m_Tuning.m_HookFireSpeed; + if(distance(m_Pos, NewPos) > m_pWorld->m_Tuning.m_HookLength) { - hook_state = HOOK_RETRACT_START; - new_pos = pos + normalize(new_pos-pos) * world->tuning.hook_length; + m_HookState = HOOK_RETRACT_START; + NewPos = m_Pos + normalize(NewPos-m_Pos) * m_pWorld->m_Tuning.m_HookLength; } // make sure that the hook doesn't go though the ground - bool going_to_hit_ground = false; - bool going_to_retract = false; - int hit = col_intersect_line(hook_pos, new_pos, &new_pos, 0); - if(hit) + bool GoingToHitGround = false; + bool GoingToRetract = false; + int Hit = m_pCollision->IntersectLine(m_HookPos, NewPos, &NewPos, 0); + if(Hit) { - if(hit&COLFLAG_NOHOOK) - going_to_retract = true; + if(Hit&CCollision::COLFLAG_NOHOOK) + GoingToRetract = true; else - going_to_hit_ground = true; + GoingToHitGround = true; } // Check against other players first - if(world && world->tuning.player_hooking) + if(m_pWorld && m_pWorld->m_Tuning.m_PlayerHooking) { - float dist = 0.0f; + float Dist = 0.0f; for(int i = 0; i < MAX_CLIENTS; i++) { - CHARACTER_CORE *p = world->characters[i]; + CCharacterCore *p = m_pWorld->m_apCharacters[i]; if(!p || p == this) continue; - vec2 closest_point = closest_point_on_line(hook_pos, new_pos, p->pos); - if(distance(p->pos, closest_point) < phys_size+2.0f) + vec2 ClosestPoint = closest_point_on_line(m_HookPos, NewPos, p->m_Pos); + if(distance(p->m_Pos, ClosestPoint) < PhysSize+2.0f) { - if (hooked_player == -1 || distance (hook_pos, p->pos) < dist) + if (m_HookedPlayer == -1 || distance(m_HookPos, p->m_Pos) < Dist) { - triggered_events |= COREEVENT_HOOK_ATTACH_PLAYER; - hook_state = HOOK_GRABBED; - hooked_player = i; - dist = distance (hook_pos, p->pos); + m_TriggeredEvents |= COREEVENT_HOOK_ATTACH_PLAYER; + m_HookState = HOOK_GRABBED; + m_HookedPlayer = i; + Dist = distance(m_HookPos, p->m_Pos); } } } } - if(hook_state == HOOK_FLYING) + if(m_HookState == HOOK_FLYING) { // check against ground - if(going_to_hit_ground) + if(GoingToHitGround) { - triggered_events |= COREEVENT_HOOK_ATTACH_GROUND; - hook_state = HOOK_GRABBED; + m_TriggeredEvents |= COREEVENT_HOOK_ATTACH_GROUND; + m_HookState = HOOK_GRABBED; } - else if(going_to_retract) + else if(GoingToRetract) { - triggered_events |= COREEVENT_HOOK_HIT_NOHOOK; - hook_state = HOOK_RETRACT_START; + m_TriggeredEvents |= COREEVENT_HOOK_HIT_NOHOOK; + m_HookState = HOOK_RETRACT_START; } - hook_pos = new_pos; + m_HookPos = NewPos; } } - if(hook_state == HOOK_GRABBED) + if(m_HookState == HOOK_GRABBED) { - if(hooked_player != -1) + if(m_HookedPlayer != -1) { - CHARACTER_CORE *p = world->characters[hooked_player]; + CCharacterCore *p = m_pWorld->m_apCharacters[m_HookedPlayer]; if(p) - hook_pos = p->pos; + m_HookPos = p->m_Pos; else { // release hook - hooked_player = -1; - hook_state = HOOK_RETRACTED; - hook_pos = pos; + m_HookedPlayer = -1; + m_HookState = HOOK_RETRACTED; + m_HookPos = m_Pos; } // keep players hooked for a max of 1.5sec - //if(server_tick() > hook_tick+(server_tickspeed()*3)/2) + //if(Server()->Tick() > hook_tick+(Server()->TickSpeed()*3)/2) //release_hooked(); } // don't do this hook rutine when we are hook to a player - if(hooked_player == -1 && distance(hook_pos, pos) > 46.0f) + if(m_HookedPlayer == -1 && distance(m_HookPos, m_Pos) > 46.0f) { - vec2 hookvel = normalize(hook_pos-pos)*world->tuning.hook_drag_accel; + vec2 HookVel = normalize(m_HookPos-m_Pos)*m_pWorld->m_Tuning.m_HookDragAccel; // the hook as more power to drag you up then down. // this makes it easier to get on top of an platform - if(hookvel.y > 0) - hookvel.y *= 0.3f; + if(HookVel.y > 0) + HookVel.y *= 0.3f; // the hook will boost it's power if the player wants to move // in that direction. otherwise it will dampen everything abit - if((hookvel.x < 0 && direction < 0) || (hookvel.x > 0 && direction > 0)) - hookvel.x *= 0.95f; + if((HookVel.x < 0 && m_Direction < 0) || (HookVel.x > 0 && m_Direction > 0)) + HookVel.x *= 0.95f; else - hookvel.x *= 0.75f; + HookVel.x *= 0.75f; - vec2 new_vel = vel+hookvel; + vec2 NewVel = m_Vel+HookVel; // check if we are under the legal limit for the hook - if(length(new_vel) < world->tuning.hook_drag_speed || length(new_vel) < length(vel)) - vel = new_vel; // no problem. apply + if(length(NewVel) < m_pWorld->m_Tuning.m_HookDragSpeed || length(NewVel) < length(m_Vel)) + m_Vel = NewVel; // no problem. apply } // release hook (max hook time is 1.25 - hook_tick++; - if(hooked_player != -1 && (hook_tick > SERVER_TICK_SPEED+SERVER_TICK_SPEED/5 || !world->characters[hooked_player])) + m_HookTick++; + if(m_HookedPlayer != -1 && (m_HookTick > SERVER_TICK_SPEED+SERVER_TICK_SPEED/5 || !m_pWorld->m_apCharacters[m_HookedPlayer])) { - hooked_player = -1; - hook_state = HOOK_RETRACTED; - hook_pos = pos; + m_HookedPlayer = -1; + m_HookState = HOOK_RETRACTED; + m_HookPos = m_Pos; } } - if(world && world->tuning.player_collision) + if(m_pWorld && m_pWorld->m_Tuning.m_PlayerCollision) { for(int i = 0; i < MAX_CLIENTS; i++) { - CHARACTER_CORE *p = world->characters[i]; + CCharacterCore *p = m_pWorld->m_apCharacters[i]; if(!p) continue; @@ -420,95 +314,95 @@ void CHARACTER_CORE::tick(bool use_input) continue; // make sure that we don't nudge our self // handle player <-> player collision - float d = distance(pos, p->pos); - vec2 dir = normalize(pos - p->pos); - if(d < phys_size*1.25f && d > 1.0f) + float d = distance(m_Pos, p->m_Pos); + vec2 Dir = normalize(m_Pos - p->m_Pos); + if(d < PhysSize*1.25f && d > 1.0f) { - float a = (phys_size*1.45f - d); + float a = (PhysSize*1.45f - d); // make sure that we don't add excess force by checking the // direction against the current velocity - vec2 veldir = normalize(vel); - float v = 1-(dot(veldir, dir)+1)/2; - vel = vel + dir*a*(v*0.75f); - vel = vel * 0.85f; + vec2 VelDir = normalize(m_Vel); + float v = 1-(dot(VelDir, Dir)+1)/2; + m_Vel = m_Vel + Dir*a*(v*0.75f); + m_Vel = m_Vel * 0.85f; } // handle hook influence - if(hooked_player == i) + if(m_HookedPlayer == i) { - if(d > phys_size*1.50f) // TODO: fix tweakable variable + if(d > PhysSize*1.50f) // TODO: fix tweakable variable { - float accel = world->tuning.hook_drag_accel * (d/world->tuning.hook_length); - float drag_speed = world->tuning.hook_drag_speed; + float Accel = m_pWorld->m_Tuning.m_HookDragAccel * (d/m_pWorld->m_Tuning.m_HookLength); + float DragSpeed = m_pWorld->m_Tuning.m_HookDragSpeed; // add force to the hooked player - p->vel.x = saturated_add(-drag_speed, drag_speed, p->vel.x, accel*dir.x*1.5f); - p->vel.y = saturated_add(-drag_speed, drag_speed, p->vel.y, accel*dir.y*1.5f); + p->m_Vel.x = SaturatedAdd(-DragSpeed, DragSpeed, p->m_Vel.x, Accel*Dir.x*1.5f); + p->m_Vel.y = SaturatedAdd(-DragSpeed, DragSpeed, p->m_Vel.y, Accel*Dir.y*1.5f); // add a little bit force to the guy who has the grip - vel.x = saturated_add(-drag_speed, drag_speed, vel.x, -accel*dir.x*0.25f); - vel.y = saturated_add(-drag_speed, drag_speed, vel.y, -accel*dir.y*0.25f); + m_Vel.x = SaturatedAdd(-DragSpeed, DragSpeed, m_Vel.x, -Accel*Dir.x*0.25f); + m_Vel.y = SaturatedAdd(-DragSpeed, DragSpeed, m_Vel.y, -Accel*Dir.y*0.25f); } } } } // clamp the velocity to something sane - if(length(vel) > 6000) - vel = normalize(vel) * 6000; + if(length(m_Vel) > 6000) + m_Vel = normalize(m_Vel) * 6000; } -void CHARACTER_CORE::move() +void CCharacterCore::Move() { - float rampvalue = velocity_ramp(length(vel)*50, world->tuning.velramp_start, world->tuning.velramp_range, world->tuning.velramp_curvature); + float RampValue = VelocityRamp(length(m_Vel)*50, m_pWorld->m_Tuning.m_VelrampStart, m_pWorld->m_Tuning.m_VelrampRange, m_pWorld->m_Tuning.m_VelrampCurvature); - vel.x = vel.x*rampvalue; - move_box(&pos, &vel, vec2(28.0f, 28.0f), 0); - vel.x = vel.x*(1.0f/rampvalue); + m_Vel.x = m_Vel.x*RampValue; + m_pCollision->MoveBox(&m_Pos, &m_Vel, vec2(28.0f, 28.0f), 0); + m_Vel.x = m_Vel.x*(1.0f/RampValue); } -void CHARACTER_CORE::write(NETOBJ_CHARACTER_CORE *obj_core) +void CCharacterCore::Write(CNetObj_CharacterCore *pObjCore) { - obj_core->x = round(pos.x); - obj_core->y = round(pos.y); + pObjCore->m_X = round(m_Pos.x); + pObjCore->m_Y = round(m_Pos.y); - obj_core->vx = round(vel.x*256.0f); - obj_core->vy = round(vel.y*256.0f); - obj_core->hook_state = hook_state; - obj_core->hook_tick = hook_tick; - obj_core->hook_x = round(hook_pos.x); - obj_core->hook_y = round(hook_pos.y); - obj_core->hook_dx = round(hook_dir.x*256.0f); - obj_core->hook_dy = round(hook_dir.y*256.0f); - obj_core->hooked_player = hooked_player; - obj_core->jumped = jumped; - obj_core->direction = direction; - obj_core->angle = angle; + pObjCore->m_VelX = round(m_Vel.x*256.0f); + pObjCore->m_VelY = round(m_Vel.y*256.0f); + pObjCore->m_HookState = m_HookState; + pObjCore->m_HookTick = m_HookTick; + pObjCore->m_HookX = round(m_HookPos.x); + pObjCore->m_HookY = round(m_HookPos.y); + pObjCore->m_HookDx = round(m_HookDir.x*256.0f); + pObjCore->m_HookDy = round(m_HookDir.y*256.0f); + pObjCore->m_HookedPlayer = m_HookedPlayer; + pObjCore->m_Jumped = m_Jumped; + pObjCore->m_Direction = m_Direction; + pObjCore->m_Angle = m_Angle; } -void CHARACTER_CORE::read(const NETOBJ_CHARACTER_CORE *obj_core) +void CCharacterCore::Read(const CNetObj_CharacterCore *pObjCore) { - pos.x = obj_core->x; - pos.y = obj_core->y; - vel.x = obj_core->vx/256.0f; - vel.y = obj_core->vy/256.0f; - hook_state = obj_core->hook_state; - hook_tick = obj_core->hook_tick; - hook_pos.x = obj_core->hook_x; - hook_pos.y = obj_core->hook_y; - hook_dir.x = obj_core->hook_dx/256.0f; - hook_dir.y = obj_core->hook_dy/256.0f; - hooked_player = obj_core->hooked_player; - jumped = obj_core->jumped; - direction = obj_core->direction; - angle = obj_core->angle; + m_Pos.x = pObjCore->m_X; + m_Pos.y = pObjCore->m_Y; + m_Vel.x = pObjCore->m_VelX/256.0f; + m_Vel.y = pObjCore->m_VelY/256.0f; + m_HookState = pObjCore->m_HookState; + m_HookTick = pObjCore->m_HookTick; + m_HookPos.x = pObjCore->m_HookX; + m_HookPos.y = pObjCore->m_HookY; + m_HookDir.x = pObjCore->m_HookDx/256.0f; + m_HookDir.y = pObjCore->m_HookDy/256.0f; + m_HookedPlayer = pObjCore->m_HookedPlayer; + m_Jumped = pObjCore->m_Jumped; + m_Direction = pObjCore->m_Direction; + m_Angle = pObjCore->m_Angle; } -void CHARACTER_CORE::quantize() +void CCharacterCore::Quantize() { - NETOBJ_CHARACTER_CORE c; - write(&c); - read(&c); + CNetObj_CharacterCore Core; + Write(&Core); + Read(&Core); } diff --git a/src/game/gamecore.h b/src/game/gamecore.h new file mode 100644 index 00000000..816e1771 --- /dev/null +++ b/src/game/gamecore.h @@ -0,0 +1,205 @@ +#ifndef GAME_GAMECORE_H +#define GAME_GAMECORE_H + +#include <base/system.h> +#include <base/math.h> + +#include <math.h> +#include "collision.h" +#include <engine/shared/protocol.h> +#include <game/generated/protocol.h> + + +class CTuneParam +{ + int m_Value; +public: + void Set(int v) { m_Value = v; } + int Get() const { return m_Value; } + CTuneParam &operator = (int v) { m_Value = (int)(v*100.0f); return *this; } + CTuneParam &operator = (float v) { m_Value = (int)(v*100.0f); return *this; } + operator float() const { return m_Value/100.0f; } +}; + +class CTuningParams +{ +public: + CTuningParams() + { + const float TicksPerSecond = 50.0f; + #define MACRO_TUNING_PARAM(Name,ScriptName,Value) m_##Name.Set((int)(Value*100.0f)); + #include "tuning.h" + #undef MACRO_TUNING_PARAM + } + + static const char *m_apNames[]; + + #define MACRO_TUNING_PARAM(Name,ScriptName,Value) CTuneParam m_##Name; + #include "tuning.h" + #undef MACRO_TUNING_PARAM + + static int Num() { return sizeof(CTuningParams)/sizeof(int); } + bool Set(int Index, float Value); + bool Set(const char *pName, float Value); + bool Get(int Index, float *pValue); + bool Get(const char *pName, float *pValue); +}; + + +inline vec2 GetDirection(int Angle) +{ + float a = Angle/256.0f; + return vec2(cosf(a), sinf(a)); +} + +inline vec2 GetDir(float a) +{ + return vec2(cosf(a), sinf(a)); +} + +inline float GetAngle(vec2 Dir) +{ + if(Dir.x == 0 && Dir.y == 0) + return 0.0f; + float a = atanf(Dir.y/Dir.x); + if(Dir.x < 0) + a = a+pi; + return a; +} + +inline void StrToInts(int *pInts, int Num, const char *pStr) +{ + int Index = 0; + while(Num) + { + char aBuf[4] = {0,0,0,0}; + for(int c = 0; c < 4 && pStr[Index]; c++, Index++) + aBuf[c] = pStr[Index]; + *pInts = ((aBuf[0]+128)<<24)|((aBuf[1]+128)<<16)|((aBuf[2]+128)<<8)|(aBuf[3]+128); + pInts++; + Num--; + } + + // null terminate + pInts[-1] &= 0xffffff00; +} + +inline void IntsToStr(const int *pInts, int Num, char *pStr) +{ + while(Num) + { + pStr[0] = (((*pInts)>>24)&0xff)-128; + pStr[1] = (((*pInts)>>16)&0xff)-128; + pStr[2] = (((*pInts)>>8)&0xff)-128; + pStr[3] = ((*pInts)&0xff)-128; + pStr += 4; + pInts++; + Num--; + } + + // null terminate + pStr[-1] = 0; +} + + + +inline vec2 CalcPos(vec2 p, vec2 v, float Curvature, float Speed, float t) +{ + vec2 n; + t *= Speed; + n.x = p.x + v.x*t; + n.y = p.y + v.y*t + Curvature/10000*(t*t); + return n; +} + + +template<typename T> +inline T SaturatedAdd(T Min, T Max, T Current, T Modifier) +{ + if(Modifier < 0) + { + if(Current < Min) + return Current; + Current += Modifier; + if(Current < Min) + Current = Min; + return Current; + } + else + { + if(Current > Max) + return Current; + Current += Modifier; + if(Current > Max) + Current = Max; + return Current; + } +} + + +float VelocityRamp(float Value, float Start, float Range, float Curvature); + +// hooking stuff +enum +{ + HOOK_RETRACTED=-1, + HOOK_IDLE=0, + HOOK_RETRACT_START=1, + HOOK_RETRACT_END=3, + HOOK_FLYING, + HOOK_GRABBED, + + COREEVENT_GROUND_JUMP=0x01, + COREEVENT_AIR_JUMP=0x02, + COREEVENT_HOOK_LAUNCH=0x04, + COREEVENT_HOOK_ATTACH_PLAYER=0x08, + COREEVENT_HOOK_ATTACH_GROUND=0x10, + COREEVENT_HOOK_HIT_NOHOOK=0x20, + COREEVENT_HOOK_RETRACT=0x40, +}; + +class CWorldCore +{ +public: + CWorldCore() + { + mem_zero(m_apCharacters, sizeof(m_apCharacters)); + } + + CTuningParams m_Tuning; + class CCharacterCore *m_apCharacters[MAX_CLIENTS]; +}; + +class CCharacterCore +{ + CWorldCore *m_pWorld; + CCollision *m_pCollision; +public: + vec2 m_Pos; + vec2 m_Vel; + + vec2 m_HookPos; + vec2 m_HookDir; + int m_HookTick; + int m_HookState; + int m_HookedPlayer; + + int m_Jumped; + + int m_Direction; + int m_Angle; + CNetObj_PlayerInput m_Input; + + int m_TriggeredEvents; + + void Init(CWorldCore *pWorld, CCollision *pCollision); + void Reset(); + void Tick(bool UseInput); + void Move(); + + void Read(const CNetObj_CharacterCore *pObjCore); + void Write(CNetObj_CharacterCore *pObjCore); + void Quantize(); +}; + +#endif diff --git a/src/game/gamecore.hpp b/src/game/gamecore.hpp deleted file mode 100644 index 2734820c..00000000 --- a/src/game/gamecore.hpp +++ /dev/null @@ -1,200 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef GAME_GAME_H -#define GAME_GAME_H - -#include <base/system.h> -#include <base/math.hpp> - -#include <engine/e_common_interface.h> -#include <math.h> -#include "collision.hpp" -#include <game/generated/g_protocol.hpp> - -struct TUNING_PARAMS -{ - TUNING_PARAMS() - { - const float ticks_per_second = 50.0f; - #define MACRO_TUNING_PARAM(name,value) name.set((int)(value*100.0f)); - #include "tuning.hpp" - #undef MACRO_TUNING_PARAM - } - - static const char *names[]; - - #define MACRO_TUNING_PARAM(name,value) tune_param name; - #include "tuning.hpp" - #undef MACRO_TUNING_PARAM - - static int num() { return sizeof(TUNING_PARAMS)/sizeof(int); } - bool set(int index, float value); - bool set(const char *name, float value); - bool get(int index, float *value); - bool get(const char *name, float *value); -}; - - -inline vec2 get_direction(int angle) -{ - float a = angle/256.0f; - return vec2(cosf(a), sinf(a)); -} - -inline vec2 get_dir(float a) -{ - return vec2(cosf(a), sinf(a)); -} - -inline float get_angle(vec2 dir) -{ - float a = atan(dir.y/dir.x); - if(dir.x < 0) - a = a+pi; - return a; -} - -inline void str_to_ints(int *ints, int num, const char *str) -{ - int index = 0; - while(num) - { - char buf[4] = {0,0,0,0}; - for(int c = 0; c < 4 && str[index]; c++, index++) - buf[c] = str[index]; - *ints = ((buf[0]+128)<<24)|((buf[1]+128)<<16)|((buf[2]+128)<<8)|(buf[3]+128); - ints++; - num--; - } - - // null terminate - ints[-1] &= 0xffffff00; -} - -inline void ints_to_str(const int *ints, int num, char *str) -{ - while(num) - { - str[0] = (((*ints)>>24)&0xff)-128; - str[1] = (((*ints)>>16)&0xff)-128; - str[2] = (((*ints)>>8)&0xff)-128; - str[3] = ((*ints)&0xff)-128; - str += 4; - ints++; - num--; - } - - // null terminate - str[-1] = 0; -} - - - -inline vec2 calc_pos(vec2 p, vec2 v, float curvature, float speed, float t) -{ - vec2 n; - t *= speed; - n.x = p.x + v.x*t; - n.y = p.y + v.y*t + curvature/10000*(t*t); - return n; -} - - -template<typename T> -inline T saturated_add(T min, T max, T current, T modifier) -{ - if(modifier < 0) - { - if(current < min) - return current; - current += modifier; - if(current < min) - current = min; - return current; - } - else - { - if(current > max) - return current; - current += modifier; - if(current > max) - current = max; - return current; - } -} - -void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces); -void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity); -bool test_box(vec2 pos, vec2 size); -float velocity_ramp(float value, float start, float range, float curvature); - -// hooking stuff -enum -{ - HOOK_RETRACTED=-1, - HOOK_IDLE=0, - HOOK_RETRACT_START=1, - HOOK_RETRACT_END=3, - HOOK_FLYING, - HOOK_GRABBED, - - COREEVENT_GROUND_JUMP=0x01, - COREEVENT_AIR_JUMP=0x02, - COREEVENT_HOOK_LAUNCH=0x04, - COREEVENT_HOOK_ATTACH_PLAYER=0x08, - COREEVENT_HOOK_ATTACH_GROUND=0x10, - COREEVENT_HOOK_HIT_NOHOOK=0x20, - COREEVENT_HOOK_RETRACT=0x40, -}; - -class WORLD_CORE -{ -public: - WORLD_CORE() - { - mem_zero(characters, sizeof(characters)); - } - - TUNING_PARAMS tuning; - class CHARACTER_CORE *characters[MAX_CLIENTS]; -}; - -class CHARACTER_CORE -{ -public: - WORLD_CORE *world; - - vec2 pos; - vec2 vel; - - vec2 hook_pos; - vec2 hook_dir; - int hook_tick; - int hook_state; - int hooked_player; - - int jumped; - - int direction; - int angle; - NETOBJ_PLAYER_INPUT input; - - int triggered_events; - - void reset(); - void tick(bool use_input); - void move(); - - void read(const NETOBJ_CHARACTER_CORE *obj_core); - void write(NETOBJ_CHARACTER_CORE *obj_core); - void quantize(); -}; - - -#define LERP(a,b,t) (a + (b-a) * t) -#define min(a, b) ( a > b ? b : a) -#define max(a, b) ( a > b ? a : b) - -inline bool col_check_point(float x, float y) { return col_is_solid(round(x), round(y)) != 0; } -inline bool col_check_point(vec2 p) { return col_check_point(p.x, p.y); } - -#endif diff --git a/src/game/layers.cpp b/src/game/layers.cpp index 832dc766..e99befd2 100644 --- a/src/game/layers.cpp +++ b/src/game/layers.cpp @@ -1,57 +1,61 @@ -#include <engine/e_common_interface.h> -#include "layers.hpp" +#include "layers.h" -static MAPITEM_LAYER_TILEMAP *game_layer = 0; -static MAPITEM_GROUP *game_group = 0; - -static int groups_start = 0; -static int groups_num = 0; -static int layers_start = 0; -static int layers_num = 0; +CLayers::CLayers() +{ + m_GroupsNum = 0; + m_GroupsStart = 0; + m_LayersNum = 0; + m_LayersStart = 0; + m_pGameGroup = 0; + m_pGameLayer = 0; + m_pMap = 0; +} -void layers_init() +void CLayers::Init(class IKernel *pKernel) { - map_get_type(MAPITEMTYPE_GROUP, &groups_start, &groups_num); - map_get_type(MAPITEMTYPE_LAYER, &layers_start, &layers_num); + m_pMap = pKernel->RequestInterface<IMap>(); + m_pMap->GetType(MAPITEMTYPE_GROUP, &m_GroupsStart, &m_GroupsNum); + m_pMap->GetType(MAPITEMTYPE_LAYER, &m_LayersStart, &m_LayersNum); - for(int g = 0; g < layers_num_groups(); g++) + for(int g = 0; g < NumGroups(); g++) { - MAPITEM_GROUP *group = layers_get_group(g); - for(int l = 0; l < group->num_layers; l++) + CMapItemGroup *pGroup = GetGroup(g); + for(int l = 0; l < pGroup->m_NumLayers; l++) { - MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l); + CMapItemLayer *pLayer = GetLayer(pGroup->m_StartLayer+l); - if(layer->type == LAYERTYPE_TILES) + if(pLayer->m_Type == LAYERTYPE_TILES) { - MAPITEM_LAYER_TILEMAP *tilemap = (MAPITEM_LAYER_TILEMAP *)layer; - if(tilemap->flags&1) + CMapItemLayerTilemap *pTilemap = reinterpret_cast<CMapItemLayerTilemap *>(pLayer); + if(pTilemap->m_Flags&1) { - game_layer = tilemap; - game_group = group; + m_pGameLayer = pTilemap; + m_pGameGroup = pGroup; + + // make sure the game group has standard settings + m_pGameGroup->m_OffsetX = 0; + m_pGameGroup->m_OffsetY = 0; + m_pGameGroup->m_ParallaxX = 100; + m_pGameGroup->m_ParallaxY = 100; + m_pGameGroup->m_UseClipping = 0; + m_pGameGroup->m_ClipX = 0; + m_pGameGroup->m_ClipY = 0; + m_pGameGroup->m_ClipW = 0; + m_pGameGroup->m_ClipH = 0; + + break; } } } } } -int layers_num_groups() { return groups_num; } -MAPITEM_GROUP *layers_get_group(int index) -{ - return (MAPITEM_GROUP *)map_get_item(groups_start+index, 0, 0); -} - -MAPITEM_LAYER *layers_get_layer(int index) +CMapItemGroup *CLayers::GetGroup(int Index) const { - return (MAPITEM_LAYER *)map_get_item(layers_start+index, 0, 0); + return static_cast<CMapItemGroup *>(m_pMap->GetItem(m_GroupsStart+Index, 0, 0)); } -MAPITEM_LAYER_TILEMAP *layers_game_layer() +CMapItemLayer *CLayers::GetLayer(int Index) const { - return game_layer; + return static_cast<CMapItemLayer *>(m_pMap->GetItem(m_LayersStart+Index, 0, 0)); } - -MAPITEM_GROUP *layers_game_group() -{ - return game_group; -} - diff --git a/src/game/layers.h b/src/game/layers.h new file mode 100644 index 00000000..198fe695 --- /dev/null +++ b/src/game/layers.h @@ -0,0 +1,28 @@ +#ifndef GAME_LAYERS_H +#define GAME_LAYERS_H + +#include <engine/map.h> +#include <game/mapitems.h> + +class CLayers +{ + int m_GroupsNum; + int m_GroupsStart; + int m_LayersNum; + int m_LayersStart; + CMapItemGroup *m_pGameGroup; + CMapItemLayerTilemap *m_pGameLayer; + class IMap *m_pMap; + +public: + CLayers(); + void Init(class IKernel *pKernel); + int NumGroups() const { return m_GroupsNum; }; + class IMap *Map() const { return m_pMap; }; + CMapItemGroup *GameGroup() const { return m_pGameGroup; }; + CMapItemLayerTilemap *GameLayer() const { return m_pGameLayer; }; + CMapItemGroup *GetGroup(int Index) const; + CMapItemLayer *GetLayer(int Index) const; +}; + +#endif diff --git a/src/game/layers.hpp b/src/game/layers.hpp deleted file mode 100644 index cb18419b..00000000 --- a/src/game/layers.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "mapitems.hpp" - -void layers_init(); - -MAPITEM_LAYER_TILEMAP *layers_game_layer(); -MAPITEM_GROUP *layers_game_group(); - -int layers_num_groups(); -MAPITEM_GROUP *layers_get_group(int index); -MAPITEM_LAYER *layers_get_layer(int index); - - diff --git a/src/game/localization.cpp b/src/game/localization.cpp index 3a1b0411..02065e36 100644 --- a/src/game/localization.cpp +++ b/src/game/localization.cpp @@ -1,100 +1,101 @@ -#include "localization.hpp" -#include <base/tl/algorithm.hpp> +#include "localization.h" +#include <base/tl/algorithm.h> -#include <engine/e_linereader.h> +#include <engine/shared/linereader.h> -const char *localize(const char *str) +const char *Localize(const char *pStr) { - const char *new_str = localization.find_string(str_quickhash(str)); - return new_str ? new_str : str; + const char *pNewStr = g_Localization.FindString(str_quickhash(pStr)); + return pNewStr ? pNewStr : pStr; } -LOC_CONSTSTRING::LOC_CONSTSTRING(const char *str) +CLocConstString::CLocConstString(const char *pStr) { - default_str = str; - hash = str_quickhash(default_str); - version = -1; + m_pDefaultStr = pStr; + m_Hash = str_quickhash(m_pDefaultStr); + m_Version = -1; } -void LOC_CONSTSTRING::reload() +void CLocConstString::Reload() { - version = localization.version(); - const char *new_str = localization.find_string(hash); - current_str = new_str; - if(!current_str) - current_str = default_str; + m_Version = g_Localization.Version(); + const char *pNewStr = g_Localization.FindString(m_Hash); + m_pCurrentStr = pNewStr; + if(!m_pCurrentStr) + m_pCurrentStr = m_pDefaultStr; } -LOCALIZATIONDATABASE::LOCALIZATIONDATABASE() +CLocalizationDatabase::CLocalizationDatabase() { - current_version = 0; + m_CurrentVersion = 0; } -void LOCALIZATIONDATABASE::add_string(const char *org_str, const char *new_str) +void CLocalizationDatabase::AddString(const char *pOrgStr, const char *pNewStr) { - STRING s; - s.hash = str_quickhash(org_str); - s.replacement = new_str; - strings.add(s); + CString s; + s.m_Hash = str_quickhash(pOrgStr); + s.m_Replacement = pNewStr; + m_Strings.add(s); } -bool LOCALIZATIONDATABASE::load(const char *filename) +bool CLocalizationDatabase::Load(const char *pFilename) { // empty string means unload - if(filename[0] == 0) + if(pFilename[0] == 0) { - strings.clear(); + m_Strings.clear(); + m_CurrentVersion = 0; return true; } - LINEREADER lr; - IOHANDLE io = io_open(filename, IOFLAG_READ); - if(!io) + IOHANDLE IoHandle = io_open(pFilename, IOFLAG_READ); + if(!IoHandle) return false; - dbg_msg("localization", "loaded '%s'", filename); - strings.clear(); + dbg_msg("localization", "loaded '%s'", pFilename); + m_Strings.clear(); - linereader_init(&lr, io); - char *line; - while((line = linereader_get(&lr))) + CLineReader LineReader; + LineReader.Init(IoHandle); + char *pLine; + while((pLine = LineReader.Get())) { - if(!str_length(line)) + if(!str_length(pLine)) continue; - if(line[0] == '#') // skip comments + if(pLine[0] == '#') // skip comments continue; - char *replacement = linereader_get(&lr); - if(!replacement) + char *pReplacement = LineReader.Get(); + if(!pReplacement) { dbg_msg("", "unexpected end of file"); break; } - if(replacement[0] != '=' || replacement[1] != '=' || replacement[2] != ' ') + if(pReplacement[0] != '=' || pReplacement[1] != '=' || pReplacement[2] != ' ') { - dbg_msg("", "malform replacement line for '%s'", line); + dbg_msg("", "malform replacement line for '%s'", pLine); continue; } - replacement += 3; - localization.add_string(line, replacement); + pReplacement += 3; + AddString(pLine, pReplacement); } - current_version++; + m_CurrentVersion++; return true; } -const char *LOCALIZATIONDATABASE::find_string(unsigned hash) +const char *CLocalizationDatabase::FindString(unsigned Hash) { - STRING s; - s.hash = hash; - sorted_array<STRING>::range r = ::find_binary(strings.all(), s); + CString String; + String.m_Hash = Hash; + sorted_array<CString>::range r = ::find_binary(m_Strings.all(), String); if(r.empty()) return 0; - return r.front().replacement; + return r.front().m_Replacement; } -LOCALIZATIONDATABASE localization; +CLocalizationDatabase g_Localization; diff --git a/src/game/localization.h b/src/game/localization.h new file mode 100644 index 00000000..2e96ef04 --- /dev/null +++ b/src/game/localization.h @@ -0,0 +1,57 @@ +#ifndef GAME_LOCALIZATION_H +#define GAME_LOCALIZATION_H +#include <base/tl/string.h> +#include <base/tl/sorted_array.h> + +class CLocalizationDatabase +{ + class CString + { + public: + unsigned m_Hash; + + // TODO: do this as an const char * and put everything on a incremental heap + string m_Replacement; + + bool operator <(const CString &Other) const { return m_Hash < Other.m_Hash; } + bool operator <=(const CString &Other) const { return m_Hash <= Other.m_Hash; } + bool operator ==(const CString &Other) const { return m_Hash == Other.m_Hash; } + }; + + sorted_array<CString> m_Strings; + int m_CurrentVersion; + +public: + CLocalizationDatabase(); + + bool Load(const char *pFilename); + + int Version() { return m_CurrentVersion; } + + void AddString(const char *pOrgStr, const char *pNewStr); + const char *FindString(unsigned Hash); +}; + +extern CLocalizationDatabase g_Localization; + +class CLocConstString +{ + const char *m_pDefaultStr; + const char *m_pCurrentStr; + unsigned m_Hash; + int m_Version; +public: + CLocConstString(const char *pStr); + void Reload(); + + inline operator const char *() + { + if(m_Version != g_Localization.Version()) + Reload(); + return m_pCurrentStr; + } +}; + + +extern const char *Localize(const char *pStr); +#endif diff --git a/src/game/localization.hpp b/src/game/localization.hpp deleted file mode 100644 index 3f79d687..00000000 --- a/src/game/localization.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#include <base/tl/string.hpp> -#include <base/tl/sorted_array.hpp> - -class LOCALIZATIONDATABASE -{ - class STRING - { - public: - unsigned hash; - - // TODO: do this as an const char * and put everything on a incremental heap - string replacement; - - bool operator <(const STRING &other) const { return hash < other.hash; } - bool operator <=(const STRING &other) const { return hash <= other.hash; } - bool operator ==(const STRING &other) const { return hash == other.hash; } - }; - - sorted_array<STRING> strings; - int current_version; - -public: - LOCALIZATIONDATABASE(); - - bool load(const char *filename); - - int version() { return current_version; } - - void add_string(const char *org_str, const char *new_str); - const char *find_string(unsigned hash); -}; - -extern LOCALIZATIONDATABASE localization; - -class LOC_CONSTSTRING -{ - const char *default_str; - const char *current_str; - unsigned hash; - int version; -public: - LOC_CONSTSTRING(const char *str); - void reload(); - - inline operator const char *() - { - if(version != localization.version()) - reload(); - return current_str; - } -}; - - -extern const char *localize(const char *str); diff --git a/src/game/mapitems.h b/src/game/mapitems.h new file mode 100644 index 00000000..efbd96f2 --- /dev/null +++ b/src/game/mapitems.h @@ -0,0 +1,179 @@ +#ifndef GAME_MAPITEMS_H +#define GAME_MAPITEMS_H + +// layer types +enum +{ + LAYERTYPE_INVALID=0, + LAYERTYPE_GAME, // not used + LAYERTYPE_TILES, + LAYERTYPE_QUADS, + + MAPITEMTYPE_VERSION=0, + MAPITEMTYPE_INFO, + MAPITEMTYPE_IMAGE, + MAPITEMTYPE_ENVELOPE, + MAPITEMTYPE_GROUP, + MAPITEMTYPE_LAYER, + MAPITEMTYPE_ENVPOINTS, + + + CURVETYPE_STEP=0, + CURVETYPE_LINEAR, + CURVETYPE_SLOW, + CURVETYPE_FAST, + CURVETYPE_SMOOTH, + NUM_CURVETYPES, + + // game layer tiles + ENTITY_NULL=0, + ENTITY_SPAWN, + ENTITY_SPAWN_RED, + ENTITY_SPAWN_BLUE, + ENTITY_FLAGSTAND_RED, + ENTITY_FLAGSTAND_BLUE, + ENTITY_ARMOR_1, + ENTITY_HEALTH_1, + ENTITY_WEAPON_SHOTGUN, + ENTITY_WEAPON_GRENADE, + ENTITY_POWERUP_NINJA, + ENTITY_WEAPON_RIFLE, + NUM_ENTITIES, + + TILE_AIR=0, + TILE_SOLID, + TILE_DEATH, + TILE_NOHOOK, + + TILEFLAG_VFLIP=1, + TILEFLAG_HFLIP=2, + TILEFLAG_OPAQUE=4, + + LAYERFLAG_DETAIL=1, + + ENTITY_OFFSET=255-16*4, +}; + +struct CPoint +{ + int x, y; // 22.10 fixed point +}; + +struct CColor +{ + int r, g, b, a; +}; + +struct CQuad +{ + CPoint m_aPoints[5]; + CColor m_aColors[4]; + CPoint m_aTexcoords[4]; + + int m_PosEnv; + int m_PosEnvOffset; + + int m_ColorEnv; + int m_ColorEnvOffset; +}; + +struct CTile +{ + unsigned char m_Index; + unsigned char m_Flags; + unsigned char m_Skip; + unsigned char m_Reserved; +}; + +struct CMapItemImage +{ + int m_Version; + int m_Width; + int m_Height; + int m_External; + int m_ImageName; + int m_ImageData; +} ; + +struct CMapItemGroup_v1 +{ + int m_Version; + int m_OffsetX; + int m_OffsetY; + int m_ParallaxX; + int m_ParallaxY; + + int m_StartLayer; + int m_NumLayers; +} ; + + +struct CMapItemGroup : public CMapItemGroup_v1 +{ + enum { CURRENT_VERSION=2 }; + + int m_UseClipping; + int m_ClipX; + int m_ClipY; + int m_ClipW; + int m_ClipH; +} ; + +struct CMapItemLayer +{ + int m_Version; + int m_Type; + int m_Flags; +} ; + +struct CMapItemLayerTilemap +{ + CMapItemLayer m_Layer; + int m_Version; + + int m_Width; + int m_Height; + int m_Flags; + + CColor m_Color; + int m_ColorEnv; + int m_ColorEnvOffset; + + int m_Image; + int m_Data; +} ; + +struct CMapItemLayerQuads +{ + CMapItemLayer m_Layer; + int m_Version; + + int m_NumQuads; + int m_Data; + int m_Image; +} ; + +struct CMapItemVersion +{ + int m_Version; +} ; + +struct CEnvPoint +{ + int m_Time; // in ms + int m_Curvetype; + int m_aValues[4]; // 1-4 depending on envelope (22.10 fixed point) + + bool operator<(const CEnvPoint &Other) { return m_Time < Other.m_Time; } +} ; + +struct CMapItemEnvelope +{ + int m_Version; + int m_Channels; + int m_StartPoint; + int m_NumPoints; + int m_Name; +} ; + +#endif diff --git a/src/game/mapitems.hpp b/src/game/mapitems.hpp deleted file mode 100644 index 9c9936d2..00000000 --- a/src/game/mapitems.hpp +++ /dev/null @@ -1,178 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef GAME_MAPITEMS_H -#define GAME_MAPITEMS_H - -// layer types -enum -{ - LAYERTYPE_INVALID=0, - LAYERTYPE_GAME, // not used - LAYERTYPE_TILES, - LAYERTYPE_QUADS, - - MAPITEMTYPE_VERSION=0, - MAPITEMTYPE_INFO, - MAPITEMTYPE_IMAGE, - MAPITEMTYPE_ENVELOPE, - MAPITEMTYPE_GROUP, - MAPITEMTYPE_LAYER, - MAPITEMTYPE_ENVPOINTS, - - - CURVETYPE_STEP=0, - CURVETYPE_LINEAR, - CURVETYPE_SLOW, - CURVETYPE_FAST, - CURVETYPE_SMOOTH, - NUM_CURVETYPES, - - // game layer tiles - ENTITY_NULL=0, - ENTITY_SPAWN, - ENTITY_SPAWN_RED, - ENTITY_SPAWN_BLUE, - ENTITY_FLAGSTAND_RED, - ENTITY_FLAGSTAND_BLUE, - ENTITY_ARMOR_1, - ENTITY_HEALTH_1, - ENTITY_WEAPON_SHOTGUN, - ENTITY_WEAPON_GRENADE, - ENTITY_POWERUP_NINJA, - ENTITY_WEAPON_RIFLE, - NUM_ENTITIES, - - TILE_AIR=0, - TILE_SOLID, - TILE_DEATH, - TILE_NOHOOK, - - TILEFLAG_VFLIP=1, - TILEFLAG_HFLIP=2, - TILEFLAG_OPAQUE=4, - - LAYERFLAG_DETAIL=1, - - ENTITY_OFFSET=255-16*4, -}; - -typedef struct -{ - int x, y; // 22.10 fixed point -} POINT; - -typedef struct -{ - int r, g, b, a; -} COLOR; - -typedef struct -{ - POINT points[5]; - COLOR colors[4]; - POINT texcoords[4]; - - int pos_env; - int pos_env_offset; - - int color_env; - int color_env_offset; -} QUAD; - -typedef struct -{ - unsigned char index; - unsigned char flags; - unsigned char skip; - unsigned char reserved; -} TILE; - -typedef struct -{ - int version; - int width; - int height; - int external; - int image_name; - int image_data; -} MAPITEM_IMAGE; - -struct MAPITEM_GROUP_v1 -{ - int version; - int offset_x; - int offset_y; - int parallax_x; - int parallax_y; - - int start_layer; - int num_layers; -} ; - - -struct MAPITEM_GROUP : public MAPITEM_GROUP_v1 -{ - enum { CURRENT_VERSION=2 }; - - int use_clipping; - int clip_x; - int clip_y; - int clip_w; - int clip_h; -} ; - -typedef struct -{ - int version; - int type; - int flags; -} MAPITEM_LAYER; - -typedef struct -{ - MAPITEM_LAYER layer; - int version; - - int width; - int height; - int flags; - - COLOR color; - int color_env; - int color_env_offset; - - int image; - int data; -} MAPITEM_LAYER_TILEMAP; - -typedef struct -{ - MAPITEM_LAYER layer; - int version; - - int num_quads; - int data; - int image; -} MAPITEM_LAYER_QUADS; - -typedef struct -{ - int version; -} MAPITEM_VERSION; - -typedef struct -{ - int time; // in ms - int curvetype; - int values[4]; // 1-4 depending on envelope (22.10 fixed point) -} ENVPOINT; - -typedef struct -{ - int version; - int channels; - int start_point; - int num_points; - int name; -} MAPITEM_ENVELOPE; - -#endif diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 8ba91a80..839088dd 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1,838 +1,820 @@ #include <new> -#include <engine/e_server_interface.h> -#include <engine/e_config.h> -#include <game/server/gamecontext.hpp> -#include <game/mapitems.hpp> +#include <engine/shared/config.h> +#include <game/server/gamecontext.h> +#include <game/mapitems.h> -#include "character.hpp" -#include "laser.hpp" -#include "projectile.hpp" +#include "character.h" +#include "laser.h" +#include "projectile.h" -struct INPUT_COUNT +//input count +struct CInputCount { - int presses; - int releases; + int m_Presses; + int m_Releases; }; -static INPUT_COUNT count_input(int prev, int cur) +CInputCount CountInput(int Prev, int Cur) { - INPUT_COUNT c = {0,0}; - prev &= INPUT_STATE_MASK; - cur &= INPUT_STATE_MASK; - int i = prev; - while(i != cur) + CInputCount c = {0, 0}; + Prev &= INPUT_STATE_MASK; + Cur &= INPUT_STATE_MASK; + int i = Prev; + + while(i != Cur) { i = (i+1)&INPUT_STATE_MASK; if(i&1) - c.presses++; + c.m_Presses++; else - c.releases++; + c.m_Releases++; } return c; } -MACRO_ALLOC_POOL_ID_IMPL(CHARACTER, MAX_CLIENTS) +MACRO_ALLOC_POOL_ID_IMPL(CCharacter, MAX_CLIENTS) -// player -CHARACTER::CHARACTER() -: ENTITY(NETOBJTYPE_CHARACTER) +// Character, "physical" player's part +CCharacter::CCharacter(CGameWorld *pWorld) +: CEntity(pWorld, NETOBJTYPE_CHARACTER) { - proximity_radius = phys_size; + m_ProximityRadius = g_CharPhysSize; + m_Health = 0; + m_Armor = 0; } -void CHARACTER::reset() +void CCharacter::Reset() { - destroy(); + Destroy(); } -bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team) +bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) { - player_state = PLAYERSTATE_UNKNOWN; - emote_stop = -1; - last_action = -1; - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; - queued_weapon = -1; + m_PlayerState = PLAYERSTATE_UNKNOWN; + m_EmoteStop = -1; + m_LastAction = -1; + m_ActiveWeapon = WEAPON_GUN; + m_LastWeapon = WEAPON_HAMMER; + m_QueuedWeapon = -1; - //clear(); - this->player = player; - this->pos = pos; - this->team = team; + m_pPlayer = pPlayer; + m_Pos = Pos; - core.reset(); - core.world = &game.world.core; - core.pos = pos; - game.world.core.characters[player->client_id] = &core; - - reckoning_tick = 0; - mem_zero(&sendcore, sizeof(sendcore)); - mem_zero(&reckoningcore, sizeof(reckoningcore)); + m_Core.Reset(); + m_Core.Init(&GameServer()->m_World.m_Core, GameServer()->Collision()); + m_Core.m_Pos = m_Pos; + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = &m_Core; + + m_ReckoningTick = 0; + mem_zero(&m_SendCore, sizeof(m_SendCore)); + mem_zero(&m_ReckoningCore, sizeof(m_ReckoningCore)); - game.world.insert_entity(this); - alive = true; - player->force_balanced = false; + GameServer()->m_World.InsertEntity(this); + m_Alive = true; - game.controller->on_character_spawn(this); + GameServer()->m_pController->OnCharacterSpawn(this); return true; } -void CHARACTER::destroy() +void CCharacter::Destroy() { - game.world.core.characters[player->client_id] = 0; - alive = false; + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; + m_Alive = false; } -void CHARACTER::set_weapon(int w) +void CCharacter::SetWeapon(int W) { - if(w == active_weapon) + if(W == m_ActiveWeapon) return; - last_weapon = active_weapon; - queued_weapon = -1; - active_weapon = w; - if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) - active_weapon = 0; + m_LastWeapon = m_ActiveWeapon; + m_QueuedWeapon = -1; + m_ActiveWeapon = W; + GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SWITCH); - game.create_sound(pos, SOUND_WEAPON_SWITCH); + if(m_ActiveWeapon < 0 || m_ActiveWeapon >= NUM_WEAPONS) + m_ActiveWeapon = 0; } -bool CHARACTER::is_grounded() +bool CCharacter::IsGrounded() { - if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) + if(GameServer()->Collision()->CheckPoint(m_Pos.x+g_CharPhysSize/2, m_Pos.y+g_CharPhysSize/2+5)) return true; - if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) + if(GameServer()->Collision()->CheckPoint(m_Pos.x-g_CharPhysSize/2, m_Pos.y+g_CharPhysSize/2+5)) return true; return false; } -int CHARACTER::handle_ninja() +void CCharacter::HandleNinja() { - if(active_weapon != WEAPON_NINJA) - return 0; + if(m_ActiveWeapon != WEAPON_NINJA) + return; - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); + vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - if ((server_tick() - ninja.activationtick) > (data->weapons.ninja.duration * server_tickspeed() / 1000)) + if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000)) { // time's up, return - weapons[WEAPON_NINJA].got = false; - active_weapon = last_weapon; - if(active_weapon == WEAPON_NINJA) - active_weapon = WEAPON_GUN; - set_weapon(active_weapon); - return 0; + m_aWeapons[WEAPON_NINJA].m_Got = false; + m_ActiveWeapon = m_LastWeapon; + if(m_ActiveWeapon == WEAPON_NINJA) + m_ActiveWeapon = WEAPON_GUN; + + SetWeapon(m_ActiveWeapon); + return; } - // force ninja weapon - set_weapon(WEAPON_NINJA); + // force ninja Weapon + SetWeapon(WEAPON_NINJA); - ninja.currentmovetime--; + m_Ninja.m_CurrentMoveTime--; - if (ninja.currentmovetime == 0) + if (m_Ninja.m_CurrentMoveTime == 0) { - // reset player velocity - core.vel *= 0.2f; - //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; + // reset velocity + m_Core.m_Vel *= 0.2f; } - if (ninja.currentmovetime > 0) + if (m_Ninja.m_CurrentMoveTime > 0) { - // Set player velocity - core.vel = ninja.activationdir * data->weapons.ninja.velocity; - vec2 oldpos = pos; - move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f); + // Set velocity + m_Core.m_Vel = m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity; + vec2 OldPos = m_Pos; + GameServer()->Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(g_CharPhysSize, g_CharPhysSize), 0.f); + // reset velocity so the client doesn't predict stuff - core.vel = vec2(0.0f,0.0f); - if ((ninja.currentmovetime % 2) == 0) - { - //create_smoke(pos); - } + m_Core.m_Vel = vec2(0.f, 0.f); - // check if we hit anything along the way + // check if we Hit anything along the way { - CHARACTER *ents[64]; - vec2 dir = pos - oldpos; - float radius = phys_size * 2.0f; //length(dir * 0.5f); - vec2 center = oldpos + dir * 0.5f; - int num = game.world.find_entities(center, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); + CCharacter *aEnts[64]; + vec2 Dir = m_Pos - OldPos; + float Radius = g_CharPhysSize * 2.0f; + vec2 Center = OldPos + Dir * 0.5f; + int Num = GameServer()->m_World.FindEntities(Center, Radius, (CEntity**)aEnts, 64, NETOBJTYPE_CHARACTER); - for (int i = 0; i < num; i++) + for (int i = 0; i < Num; ++i) { - // Check if entity is a player - if (ents[i] == this) + if (aEnts[i] == this) continue; - // make sure we haven't hit this object before - bool balreadyhit = false; - for (int j = 0; j < numobjectshit; j++) + + // make sure we haven't Hit this object before + bool bAlreadyHit = false; + for (int j = 0; j < m_NumObjectsHit; j++) { - if (hitobjects[j] == ents[i]) - balreadyhit = true; + if (m_apHitObjects[j] == aEnts[i]) + bAlreadyHit = true; } - if (balreadyhit) + if (bAlreadyHit) continue; // check so we are sufficiently close - if (distance(ents[i]->pos, pos) > (phys_size * 2.0f)) + if (distance(aEnts[i]->m_Pos, m_Pos) > (g_CharPhysSize * 2.0f)) continue; - // hit a player, give him damage and stuffs... - game.create_sound(ents[i]->pos, SOUND_NINJA_HIT); + // Hit a player, give him damage and stuffs... + GameServer()->CreateSound(aEnts[i]->m_Pos, SOUND_NINJA_HIT); // set his velocity to fast upward (for now) - if(numobjectshit < 10) - hitobjects[numobjectshit++] = ents[i]; + if(m_NumObjectsHit < 10) + m_apHitObjects[m_NumObjectsHit++] = aEnts[i]; - ents[i]->take_damage(vec2(0,10.0f), data->weapons.ninja.base->damage, player->client_id,WEAPON_NINJA); + aEnts[i]->TakeDamage(vec2(0, 10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); } } - return 0; + + return; } - return 0; + return; } -void CHARACTER::do_weaponswitch() +void CCharacter::DoWeaponSwitch() { - if(reload_timer != 0) // make sure we have reloaded - return; - - if(queued_weapon == -1) // check for a queued weapon + // make sure we can switch + if(m_ReloadTimer != 0 || m_QueuedWeapon == -1 || m_aWeapons[WEAPON_NINJA].m_Got) return; - if(weapons[WEAPON_NINJA].got) // if we have ninja, no weapon selection is possible - return; - - // switch weapon - set_weapon(queued_weapon); + // switch Weapon + SetWeapon(m_QueuedWeapon); } -void CHARACTER::handle_weaponswitch() +void CCharacter::HandleWeaponSwitch() { - int wanted_weapon = active_weapon; - if(queued_weapon != -1) - wanted_weapon = queued_weapon; + int WantedWeapon = m_ActiveWeapon; + if(m_QueuedWeapon != -1) + WantedWeapon = m_QueuedWeapon; - // select weapon - int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses; - int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses; + // select Weapon + int Next = CountInput(m_LatestPrevInput.m_NextWeapon, m_LatestInput.m_NextWeapon).m_Presses; + int Prev = CountInput(m_LatestPrevInput.m_PrevWeapon, m_LatestInput.m_PrevWeapon).m_Presses; - if(next < 128) // make sure we only try sane stuff + if(Next < 128) // make sure we only try sane stuff { - while(next) // next weapon selection + while(Next) // Next Weapon selection { - wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS; - if(weapons[wanted_weapon].got) - next--; + WantedWeapon = (WantedWeapon+1)%NUM_WEAPONS; + if(m_aWeapons[WantedWeapon].m_Got) + Next--; } } - if(prev < 128) // make sure we only try sane stuff + if(Prev < 128) // make sure we only try sane stuff { - while(prev) // prev weapon selection + while(Prev) // Prev Weapon selection { - wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1; - if(weapons[wanted_weapon].got) - prev--; + WantedWeapon = (WantedWeapon-1)<0?NUM_WEAPONS-1:WantedWeapon-1; + if(m_aWeapons[WantedWeapon].m_Got) + Prev--; } } - // direct weapon selection - if(latest_input.wanted_weapon) - wanted_weapon = input.wanted_weapon-1; + // Direct Weapon selection + if(m_LatestInput.m_WantedWeapon) + WantedWeapon = m_Input.m_WantedWeapon-1; // check for insane values - if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got) - queued_weapon = wanted_weapon; + if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_ActiveWeapon && m_aWeapons[WantedWeapon].m_Got) + m_QueuedWeapon = WantedWeapon; - do_weaponswitch(); + DoWeaponSwitch(); } -void CHARACTER::fire_weapon() +void CCharacter::FireWeapon() { - if(reload_timer != 0) + if(m_ReloadTimer != 0) return; - do_weaponswitch(); + DoWeaponSwitch(); + vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - - bool fullauto = false; - if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE) - fullauto = true; + bool FullAuto = false; + if(m_ActiveWeapon == WEAPON_GRENADE || m_ActiveWeapon == WEAPON_SHOTGUN || m_ActiveWeapon == WEAPON_RIFLE) + FullAuto = true; // check if we gonna fire - bool will_fire = false; - if(count_input(latest_previnput.fire, latest_input.fire).presses) will_fire = true; - if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true; - if(!will_fire) + bool WillFire = false; + if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses) + WillFire = true; + + if(FullAuto && (m_LatestInput.m_Fire&1) && m_aWeapons[m_ActiveWeapon].m_Ammo) + WillFire = true; + + if(!WillFire) return; // check for ammo - if(!weapons[active_weapon].ammo) + if(!m_aWeapons[m_ActiveWeapon].m_Ammo) { // 125ms is a magical limit of how fast a human can click - reload_timer = 125 * server_tickspeed() / 1000;; - game.create_sound(pos, SOUND_WEAPON_NOAMMO); + m_ReloadTimer = 125 * Server()->TickSpeed() / 1000; + GameServer()->CreateSound(m_Pos, SOUND_WEAPON_NOAMMO); return; } - vec2 projectile_startpos = pos+direction*phys_size*0.75f; + vec2 ProjStartPos = m_Pos+Direction*g_CharPhysSize*0.75f; - switch(active_weapon) + switch(m_ActiveWeapon) { case WEAPON_HAMMER: { - // reset objects hit - numobjectshit = 0; - game.create_sound(pos, SOUND_HAMMER_FIRE); + // reset objects Hit + m_NumObjectsHit = 0; + GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE); - CHARACTER *ents[64]; - int hits = 0; - int num = game.world.find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); + CCharacter *aEnts[64]; + int Hits = 0; + int Num = GameServer()->m_World.FindEntities(ProjStartPos, g_CharPhysSize*0.5f, (CEntity**)aEnts, + 64, NETOBJTYPE_CHARACTER); - for (int i = 0; i < num; i++) + for (int i = 0; i < Num; ++i) { - CHARACTER *target = ents[i]; - if (target == this) + CCharacter *Target = aEnts[i]; + + //for race mod or any other mod, which needs hammer hits through the wall remove second condition + if ((Target == this) || GameServer()->Collision()->IntersectLine(ProjStartPos, Target->m_Pos, NULL, NULL)) continue; - - // hit a player, give him damage and stuffs... - vec2 fdir = normalize(ents[i]->pos - pos); // set his velocity to fast upward (for now) - game.create_hammerhit(pos); - ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, player->client_id, active_weapon); - vec2 dir; - if (length(target->pos - pos) > 0.0f) - dir = normalize(target->pos - pos); + GameServer()->CreateHammerHit(m_Pos); + aEnts[i]->TakeDamage(vec2(0.f, -1.f), g_pData->m_Weapons.m_Hammer.m_pBase->m_Damage, m_pPlayer->GetCID(), m_ActiveWeapon); + + vec2 Dir; + if (length(Target->m_Pos - m_Pos) > 0.0f) + Dir = normalize(Target->m_Pos - m_Pos); else - dir = vec2(0,-1); + Dir = vec2(0.f, -1.f); - target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f; - hits++; + Target->m_Core.m_Vel += normalize(Dir + vec2(0.f, -1.1f)) * 10.0f; + Hits++; } - // if we hit anything, we have to wait for the reload - if(hits) - reload_timer = server_tickspeed()/3; + // if we Hit anything, we have to wait for the reload + if(Hits) + m_ReloadTimer = Server()->TickSpeed()/3; } break; case WEAPON_GUN: { - PROJECTILE *proj = new PROJECTILE(WEAPON_GUN, - player->client_id, - projectile_startpos, - direction, - (int)(server_tickspeed()*tuning.gun_lifetime), + CProjectile *Proj = new CProjectile(GameWorld(), WEAPON_GUN, + m_pPlayer->GetCID(), + ProjStartPos, + Direction, + (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GunLifetime), 1, 0, 0, -1, WEAPON_GUN); - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + Proj->FillInfo(&p); - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(1); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); - msg_pack_end(); - server_send_msg(player->client_id); - - game.create_sound(pos, SOUND_GUN_FIRE); + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(1); + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); + + Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); + + GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE); } break; case WEAPON_SHOTGUN: { - int shotspread = 2; + int ShotSpread = 2; - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(shotspread*2+1); + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(ShotSpread*2+1); - for(int i = -shotspread; i <= shotspread; i++) + for(int i = -ShotSpread; i <= ShotSpread; ++i) { - float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; - float a = get_angle(direction); - a += spreading[i+2]; - float v = 1-(abs(i)/(float)shotspread); - float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v); - PROJECTILE *proj = new PROJECTILE(WEAPON_SHOTGUN, - player->client_id, - projectile_startpos, - vec2(cosf(a), sinf(a))*speed, - (int)(server_tickspeed()*tuning.shotgun_lifetime), + float Spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; + float a = GetAngle(Direction); + a += Spreading[i+2]; + float v = 1-(absolute(i)/(float)ShotSpread); + float Speed = mix((float)GameServer()->Tuning()->m_ShotgunSpeeddiff, 1.0f, v); + CProjectile *Proj = new CProjectile(GameWorld(), WEAPON_SHOTGUN, + m_pPlayer->GetCID(), + ProjStartPos, + vec2(cosf(a), sinf(a))*Speed, + (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_ShotgunLifetime), 1, 0, 0, -1, WEAPON_SHOTGUN); - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + Proj->FillInfo(&p); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); } - msg_pack_end(); - server_send_msg(player->client_id); + Server()->SendMsg(&Msg, 0,m_pPlayer->GetCID()); - game.create_sound(pos, SOUND_SHOTGUN_FIRE); + GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE); } break; case WEAPON_GRENADE: { - PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE, - player->client_id, - projectile_startpos, - direction, - (int)(server_tickspeed()*tuning.grenade_lifetime), - 1, PROJECTILE::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE); - - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); + CProjectile *Proj = new CProjectile(GameWorld(), WEAPON_GRENADE, + m_pPlayer->GetCID(), + ProjStartPos, + Direction, + (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GrenadeLifetime), + 1, true, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE); + + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + Proj->FillInfo(&p); + + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(1); + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); + Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(1); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); - msg_pack_end(); - server_send_msg(player->client_id); - - game.create_sound(pos, SOUND_GRENADE_FIRE); + GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE); } break; case WEAPON_RIFLE: { - new LASER(pos, direction, tuning.laser_reach, player->client_id); - game.create_sound(pos, SOUND_RIFLE_FIRE); + new CLaser(GameWorld(), m_Pos, Direction, GameServer()->Tuning()->m_LaserReach, m_pPlayer->GetCID()); + GameServer()->CreateSound(m_Pos, SOUND_RIFLE_FIRE); } break; case WEAPON_NINJA: { - attack_tick = server_tick(); - ninja.activationdir = direction; - ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000; - - //reload_timer = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick(); + // reset Hit objects + m_NumObjectsHit = 0; - // reset hit objects - numobjectshit = 0; + m_AttackTick = Server()->Tick(); + m_Ninja.m_ActivationDir = Direction; + m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000; - game.create_sound(pos, SOUND_NINJA_FIRE); - + GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE); } break; } - - if(weapons[active_weapon].ammo > 0) // -1 == unlimited - weapons[active_weapon].ammo--; - attack_tick = server_tick(); - if(!reload_timer) - reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000; + + m_AttackTick = Server()->Tick(); + + if(m_aWeapons[m_ActiveWeapon].m_Ammo > 0) // -1 == unlimited + m_aWeapons[m_ActiveWeapon].m_Ammo--; + + if(!m_ReloadTimer) + m_ReloadTimer = g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Firedelay * Server()->TickSpeed() / 1000; } -int CHARACTER::handle_weapons() +void CCharacter::HandleWeapons() { - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - - /* - if(config.dbg_stress) - { - for(int i = 0; i < NUM_WEAPONS; i++) - { - weapons[i].got = true; - weapons[i].ammo = 10; - } - - if(reload_timer) // twice as fast reload - reload_timer--; - } */ - - //if(active_weapon == WEAPON_NINJA) - handle_ninja(); - + //ninja + HandleNinja(); + + vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); // check reload timer - if(reload_timer) + if(m_ReloadTimer) { - reload_timer--; - return 0; + m_ReloadTimer--; + return; } - - /* - if (active_weapon == WEAPON_NINJA) - { - // don't update other weapons while ninja is active - return handle_ninja(); - }*/ - // fire weapon, if wanted - fire_weapon(); + // fire Weapon, if wanted + FireWeapon(); // ammo regen - int ammoregentime = data->weapons.id[active_weapon].ammoregentime; - if(ammoregentime) + int AmmoRegenTime = g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Ammoregentime; + if(AmmoRegenTime) { // If equipped and not active, regen ammo? - if (reload_timer <= 0) + if (m_ReloadTimer <= 0) { - if (weapons[active_weapon].ammoregenstart < 0) - weapons[active_weapon].ammoregenstart = server_tick(); + if (m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart < 0) + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = Server()->Tick(); - if ((server_tick() - weapons[active_weapon].ammoregenstart) >= ammoregentime * server_tickspeed() / 1000) + if ((Server()->Tick() - m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart) >= AmmoRegenTime * Server()->TickSpeed() / 1000) { // Add some ammo - weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, 10); - weapons[active_weapon].ammoregenstart = -1; + m_aWeapons[m_ActiveWeapon].m_Ammo = min(m_aWeapons[m_ActiveWeapon].m_Ammo + 1, 10); + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; } } else { - weapons[active_weapon].ammoregenstart = -1; + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; } } - return 0; + return; +} + +bool CCharacter::GiveWeapon(int Weapon, int Ammo) +{ + if(m_aWeapons[Weapon].m_Ammo < g_pData->m_Weapons.m_aId[Weapon].m_Maxammo || !m_aWeapons[Weapon].m_Got) + { + m_aWeapons[Weapon].m_Got = true; + m_aWeapons[Weapon].m_Ammo = min(g_pData->m_Weapons.m_aId[Weapon].m_Maxammo, Ammo); + return true; + } + return false; +} + +void CCharacter::GiveNinja() +{ + m_Ninja.m_ActivationTick = Server()->Tick(); + m_aWeapons[WEAPON_NINJA].m_Got = true; + m_aWeapons[WEAPON_NINJA].m_Ammo = -1; + m_LastWeapon = m_ActiveWeapon; + m_ActiveWeapon = WEAPON_NINJA; + + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA); } -void CHARACTER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) +void CCharacter::SetEmote(int Emote, int Tick) +{ + m_EmoteType = Emote; + m_EmoteStop = Tick; +} + +void CCharacter::OnPredictedInput(CNetObj_PlayerInput *pNewInput) { // check for changes - if(mem_comp(&input, new_input, sizeof(NETOBJ_PLAYER_INPUT)) != 0) - last_action = server_tick(); + if(mem_comp(&m_Input, pNewInput, sizeof(CNetObj_PlayerInput)) != 0) + m_LastAction = Server()->Tick(); // copy new input - mem_copy(&input, new_input, sizeof(input)); - num_inputs++; + mem_copy(&m_Input, pNewInput, sizeof(m_Input)); + m_NumInputs++; // or are not allowed to aim in the center - if(input.target_x == 0 && input.target_y == 0) - input.target_y = -1; + if(m_Input.m_TargetX == 0 && m_Input.m_TargetY == 0) + m_Input.m_TargetY = -1; } -void CHARACTER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) +void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) { - mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); - mem_copy(&latest_input, new_input, sizeof(latest_input)); + mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); + mem_copy(&m_LatestInput, pNewInput, sizeof(m_LatestInput)); - if(num_inputs > 2 && team != -1) + if(m_NumInputs > 2 && m_pPlayer->GetTeam() != -1) { - handle_weaponswitch(); - fire_weapon(); + HandleWeaponSwitch(); + FireWeapon(); } - mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); + mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); } -void CHARACTER::tick() +void CCharacter::Tick() { - if(player->force_balanced) + if(m_pPlayer->m_ForceBalanced) { - char buf[128]; - str_format(buf, sizeof(buf), "You were moved to %s due to team balancing", game.controller->get_team_name(team)); - game.send_broadcast(buf, player->client_id); + char Buf[128]; + str_format(Buf, sizeof(Buf), "You were moved to %s due to team balancing", GameServer()->m_pController->GetTeamName(m_pPlayer->GetTeam())); + GameServer()->SendBroadcast(Buf, m_pPlayer->GetCID()); - player->force_balanced = false; + m_pPlayer->m_ForceBalanced = false; } - //player_core core; - //core.pos = pos; - //core.jumped = jumped; - core.input = input; - core.tick(true); + m_Core.m_Input = m_Input; + m_Core.Tick(true); - float phys_size = 28.0f; // handle death-tiles - if(col_get((int)(pos.x+phys_size/3), (int)(pos.y-phys_size/3))&COLFLAG_DEATH || - col_get((int)(pos.x+phys_size/3), (int)(pos.y+phys_size/3))&COLFLAG_DEATH || - col_get((int)(pos.x-phys_size/3), (int)(pos.y-phys_size/3))&COLFLAG_DEATH || - col_get((int)(pos.x-phys_size/3), (int)(pos.y+phys_size/3))&COLFLAG_DEATH) + if(GameServer()->Collision()->GetCollisionAt(m_Pos.x+g_CharPhysSize/3.f, m_Pos.y-g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH || + GameServer()->Collision()->GetCollisionAt(m_Pos.x+g_CharPhysSize/3.f, m_Pos.y+g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH || + GameServer()->Collision()->GetCollisionAt(m_Pos.x-g_CharPhysSize/3.f, m_Pos.y-g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH || + GameServer()->Collision()->GetCollisionAt(m_Pos.x-g_CharPhysSize/3.f, m_Pos.y+g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH) { - die(player->client_id, WEAPON_WORLD); + Die(m_pPlayer->GetCID(), WEAPON_WORLD); } - // handle weapons - handle_weapons(); + // handle Weapons + HandleWeapons(); - player_state = input.player_state; + m_PlayerState = m_Input.m_PlayerState; // Previnput - previnput = input; + m_PrevInput = m_Input; return; } -void CHARACTER::tick_defered() +void CCharacter::TickDefered() { // advance the dummy { - WORLD_CORE tempworld; - reckoningcore.world = &tempworld; - reckoningcore.tick(false); - reckoningcore.move(); - reckoningcore.quantize(); + CWorldCore TempWorld; + m_ReckoningCore.Init(&TempWorld, GameServer()->Collision()); + m_ReckoningCore.Tick(false); + m_ReckoningCore.Move(); + m_ReckoningCore.Quantize(); } - //lastsentcore; - /*if(!dead) - {*/ - vec2 start_pos = core.pos; - vec2 start_vel = core.vel; - bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f)); - - core.move(); - bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f)); - core.quantize(); - bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f)); - pos = core.pos; - - if(!stuck_before && (stuck_after_move || stuck_after_quant)) - { - dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", - stuck_before, - stuck_after_move, - stuck_after_quant, - start_pos.x, start_pos.y, - start_vel.x, start_vel.y, - *((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y), - *((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y)); - } + //lastsentcore + vec2 StartPos = m_Core.m_Pos; + vec2 StartVel = m_Core.m_Vel; + bool StuckBefore = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + + m_Core.Move(); + bool StuckAfterMove = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + m_Core.Quantize(); + bool StuckAfterQuant = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + m_Pos = m_Core.m_Pos; + + if(!StuckBefore && (StuckAfterMove || StuckAfterQuant)) + { + dbg_msg("char_core", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", + StuckBefore, + StuckAfterMove, + StuckAfterQuant, + StartPos.x, StartPos.y, + StartVel.x, StartVel.y, + *((unsigned *)&StartPos.x), *((unsigned *)&StartPos.y), + *((unsigned *)&StartVel.x), *((unsigned *)&StartVel.y)); + } - int events = core.triggered_events; - int mask = cmask_all_except_one(player->client_id); - - if(events&COREEVENT_GROUND_JUMP) game.create_sound(pos, SOUND_PLAYER_JUMP, mask); - - //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); - if(events&COREEVENT_HOOK_ATTACH_PLAYER) game.create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all()); - if(events&COREEVENT_HOOK_ATTACH_GROUND) game.create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask); - if(events&COREEVENT_HOOK_HIT_NOHOOK) game.create_sound(pos, SOUND_HOOK_NOATTACH, mask); - //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - //} - - if(team == -1) + int Events = m_Core.m_TriggeredEvents; + int Mask = CmaskAllExceptOne(m_pPlayer->GetCID()); + + if(Events&COREEVENT_GROUND_JUMP) GameServer()->CreateSound(m_Pos, SOUND_PLAYER_JUMP, Mask); + + if(Events&COREEVENT_HOOK_ATTACH_PLAYER) GameServer()->CreateSound(m_Pos, SOUND_HOOK_ATTACH_PLAYER, CmaskAll()); + if(Events&COREEVENT_HOOK_ATTACH_GROUND) GameServer()->CreateSound(m_Pos, SOUND_HOOK_ATTACH_GROUND, Mask); + if(Events&COREEVENT_HOOK_HIT_NOHOOK) GameServer()->CreateSound(m_Pos, SOUND_HOOK_NOATTACH, Mask); + + + if(m_pPlayer->GetTeam() == -1) { - pos.x = input.target_x; - pos.y = input.target_y; + m_Pos.x = m_Input.m_TargetX; + m_Pos.y = m_Input.m_TargetY; } - // update the sendcore if needed + // update the m_SendCore if needed { - NETOBJ_CHARACTER predicted; - NETOBJ_CHARACTER current; - mem_zero(&predicted, sizeof(predicted)); - mem_zero(¤t, sizeof(current)); - reckoningcore.write(&predicted); - core.write(¤t); + CNetObj_Character Predicted; + CNetObj_Character Current; + mem_zero(&Predicted, sizeof(Predicted)); + mem_zero(&Current, sizeof(Current)); + m_ReckoningCore.Write(&Predicted); + m_Core.Write(&Current); // only allow dead reackoning for a top of 3 seconds - if(reckoning_tick+server_tickspeed()*3 < server_tick() || mem_comp(&predicted, ¤t, sizeof(NETOBJ_CHARACTER)) != 0) + if(m_ReckoningTick+Server()->TickSpeed()*3 < Server()->Tick() || mem_comp(&Predicted, &Current, sizeof(CNetObj_Character)) != 0) { - reckoning_tick = server_tick(); - sendcore = core; - reckoningcore = core; + m_ReckoningTick = Server()->Tick(); + m_SendCore = m_Core; + m_ReckoningCore = m_Core; } } } -bool CHARACTER::increase_health(int amount) +bool CCharacter::IncreaseHealth(int Amount) { - if(health >= 10) + if(m_Health >= 10) return false; - health = clamp(health+amount, 0, 10); + m_Health = clamp(m_Health+Amount, 0, 10); return true; } -bool CHARACTER::increase_armor(int amount) +bool CCharacter::IncreaseArmor(int Amount) { - if(armor >= 10) + if(m_Armor >= 10) return false; - armor = clamp(armor+amount, 0, 10); + m_Armor = clamp(m_Armor+Amount, 0, 10); return true; } -void CHARACTER::die(int killer, int weapon) +void CCharacter::Die(int Killer, int Weapon) { - /*if (dead || team == -1) - return;*/ - int mode_special = game.controller->on_character_death(this, game.players[killer], weapon); + int ModeSpecial = GameServer()->m_pController->OnCharacterDeath(this, GameServer()->m_apPlayers[Killer], Weapon); dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", - killer, server_clientname(killer), - player->client_id, server_clientname(player->client_id), weapon, mode_special); + Killer, Server()->ClientName(Killer), + m_pPlayer->GetCID(), Server()->ClientName(m_pPlayer->GetCID()), Weapon, ModeSpecial); // send the kill message - NETMSG_SV_KILLMSG msg; - msg.killer = killer; - msg.victim = player->client_id; - msg.weapon = weapon; - msg.mode_special = mode_special; - msg.pack(MSGFLAG_VITAL); - server_send_msg(-1); + CNetMsg_Sv_KillMsg Msg; + Msg.m_Killer = Killer; + Msg.m_Victim = m_pPlayer->GetCID(); + Msg.m_Weapon = Weapon; + Msg.m_ModeSpecial = ModeSpecial; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); // a nice sound - game.create_sound(pos, SOUND_PLAYER_DIE); - - // set dead state - // TODO: do stuff here - /* - die_pos = pos; - dead = true; - */ + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_DIE); // this is for auto respawn after 3 secs - player->die_tick = server_tick(); + m_pPlayer->m_DieTick = Server()->Tick(); - alive = false; - game.world.remove_entity(this); - game.world.core.characters[player->client_id] = 0; - game.create_death(pos, player->client_id); + m_Alive = false; + GameServer()->m_World.RemoveEntity(this); + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; + GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID()); // we got to wait 0.5 secs before respawning - player->respawn_tick = server_tick()+server_tickspeed()/2; + m_pPlayer->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; } -bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon) +bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) { - core.vel += force; + m_Core.m_Vel += Force; - if(game.controller->is_friendly_fire(player->client_id, from) && !config.sv_teamdamage) + if(GameServer()->m_pController->IsFriendlyFire(m_pPlayer->GetCID(), From) && !g_Config.m_SvTeamdamage) return false; - // player only inflicts half damage on self - if(from == player->client_id) - dmg = max(1, dmg/2); + // m_pPlayer only inflicts half damage on self + if(From == m_pPlayer->GetCID()) + Dmg = max(1, Dmg/2); - damage_taken++; + m_DamageTaken++; // create healthmod indicator - if(server_tick() < damage_taken_tick+25) + if(Server()->Tick() < m_DamageTakenTick+25) { // make sure that the damage indicators doesn't group together - game.create_damageind(pos, damage_taken*0.25f, dmg); + GameServer()->CreateDamageInd(m_Pos, m_DamageTaken*0.25f, Dmg); } else { - damage_taken = 0; - game.create_damageind(pos, 0, dmg); + m_DamageTaken = 0; + GameServer()->CreateDamageInd(m_Pos, 0, Dmg); } - if(dmg) + if(Dmg) { - if(armor) + if(m_Armor) { - if(dmg > 1) + if(Dmg > 1) { - health--; - dmg--; + m_Health--; + Dmg--; } - if(dmg > armor) + if(Dmg > m_Armor) { - dmg -= armor; - armor = 0; + Dmg -= m_Armor; + m_Armor = 0; } else { - armor -= dmg; - dmg = 0; + m_Armor -= Dmg; + Dmg = 0; } } - health -= dmg; + m_Health -= Dmg; } - damage_taken_tick = server_tick(); + m_DamageTakenTick = Server()->Tick(); - // do damage hit sound - if(from >= 0 && from != player->client_id && game.players[from]) - game.create_sound(game.players[from]->view_pos, SOUND_HIT, cmask_one(from)); + // do damage Hit sound + if(From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) + GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, CmaskOne(From)); // check for death - if(health <= 0) + if(m_Health <= 0) { - die(from, weapon); + Die(From, Weapon); // set attacker's face to happy (taunt!) - if (from >= 0 && from != player->client_id && game.players[from]) + if (From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) { - CHARACTER *chr = game.players[from]->get_character(); - if (chr) + CCharacter *pChr = GameServer()->m_apPlayers[From]->GetCharacter(); + if (pChr) { - chr->emote_type = EMOTE_HAPPY; - chr->emote_stop = server_tick() + server_tickspeed(); + pChr->m_EmoteType = EMOTE_HAPPY; + pChr->m_EmoteStop = Server()->Tick() + Server()->TickSpeed(); } } return false; } - if (dmg > 2) - game.create_sound(pos, SOUND_PLAYER_PAIN_LONG); + if (Dmg > 2) + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG); else - game.create_sound(pos, SOUND_PLAYER_PAIN_SHORT); + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_SHORT); - emote_type = EMOTE_PAIN; - emote_stop = server_tick() + 500 * server_tickspeed() / 1000; + m_EmoteType = EMOTE_PAIN; + m_EmoteStop = Server()->Tick() + 500 * Server()->TickSpeed() / 1000; - // spawn blood? return true; } -void CHARACTER::snap(int snapping_client) +void CCharacter::Snap(int SnappingClient) { - if(networkclipped(snapping_client)) + if(NetworkClipped(SnappingClient)) return; - NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER)); + CNetObj_Character *Character = static_cast<CNetObj_Character *>(Server()->SnapNewItem(NETOBJTYPE_CHARACTER, m_pPlayer->GetCID(), sizeof(CNetObj_Character))); - // write down the core - if(game.world.paused) + // write down the m_Core + if(GameServer()->m_World.m_Paused) { // no dead reckoning when paused because the client doesn't know // how far to perform the reckoning - character->tick = 0; - core.write(character); + Character->m_Tick = 0; + m_Core.Write(Character); } else { - character->tick = reckoning_tick; - sendcore.write(character); + Character->m_Tick = m_ReckoningTick; + m_SendCore.Write(Character); } // set emote - if (emote_stop < server_tick()) + if (m_EmoteStop < Server()->Tick()) { - emote_type = EMOTE_NORMAL; - emote_stop = -1; + m_EmoteType = EMOTE_NORMAL; + m_EmoteStop = -1; } - character->emote = emote_type; + Character->m_Emote = m_EmoteType; - character->ammocount = 0; - character->health = 0; - character->armor = 0; + Character->m_AmmoCount = 0; + Character->m_Health = 0; + Character->m_Armor = 0; - character->weapon = active_weapon; - character->attacktick = attack_tick; + Character->m_Weapon = m_ActiveWeapon; + Character->m_AttackTick = m_AttackTick; - character->direction = input.direction; + Character->m_Direction = m_Input.m_Direction; - if(player->client_id == snapping_client) + if(m_pPlayer->GetCID() == SnappingClient) { - character->health = health; - character->armor = armor; - if(weapons[active_weapon].ammo > 0) - character->ammocount = weapons[active_weapon].ammo; + Character->m_Health = m_Health; + Character->m_Armor = m_Armor; + if(m_aWeapons[m_ActiveWeapon].m_Ammo > 0) + Character->m_AmmoCount = m_aWeapons[m_ActiveWeapon].m_Ammo; } - if (character->emote == EMOTE_NORMAL) + if (Character->m_Emote == EMOTE_NORMAL) { - if(250 - ((server_tick() - last_action)%(250)) < 5) - character->emote = EMOTE_BLINK; + if(250 - ((Server()->Tick() - m_LastAction)%(250)) < 5) + Character->m_Emote = EMOTE_BLINK; } - character->player_state = player_state; + Character->m_PlayerState = m_PlayerState; } diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h new file mode 100644 index 00000000..30c6586f --- /dev/null +++ b/src/game/server/entities/character.h @@ -0,0 +1,133 @@ +#ifndef GAME_SERVER_ENTITIES_CHARACTER_H +#define GAME_SERVER_ENTITIES_CHARACTER_H + +#include <game/server/entity.h> +#include <game/generated/server_data.h> +#include <game/generated/protocol.h> + +#include <game/gamecore.h> + +//character's size +const int g_CharPhysSize = 28; + +enum +{ + WEAPON_GAME = -3, // team switching etc + WEAPON_SELF = -2, // console kill command + WEAPON_WORLD = -1, // death tiles etc +}; + +class CCharacter : public CEntity +{ + MACRO_ALLOC_POOL_ID() + +public: + CCharacter(CGameWorld *pWorld); + + virtual void Reset(); + virtual void Destroy(); + virtual void Tick(); + virtual void TickDefered(); + virtual void Snap(int SnappingClient); + + bool IsGrounded(); + + void SetWeapon(int W); + void HandleWeaponSwitch(); + void DoWeaponSwitch(); + + void HandleWeapons(); + void HandleNinja(); + + void OnPredictedInput(CNetObj_PlayerInput *pNewInput); + void OnDirectInput(CNetObj_PlayerInput *pNewInput); + void FireWeapon(); + + void Die(int Killer, int Weapon); + bool TakeDamage(vec2 Force, int Dmg, int From, int Weapon); + + bool Spawn(class CPlayer *pPlayer, vec2 Pos); + bool Remove(); + + bool IncreaseHealth(int Amount); + bool IncreaseArmor(int Amount); + + bool GiveWeapon(int Weapon, int Ammo); + void GiveNinja(); + + void SetEmote(int Emote, int Tick); + + const bool IsAlive() { return m_Alive; } + class CPlayer *GetPlayer() { return m_pPlayer; } + +private: + // player controlling this character + class CPlayer *m_pPlayer; + + bool m_Alive; + + // weapon info + CEntity *m_apHitObjects[10]; + int m_NumObjectsHit; + + struct WeaponStat + { + int m_AmmoRegenStart; + int m_Ammo; + int m_Ammocost; + bool m_Got; + + } m_aWeapons[NUM_WEAPONS]; + + int m_ActiveWeapon; + int m_LastWeapon; + int m_QueuedWeapon; + + int m_ReloadTimer; + int m_AttackTick; + + int m_DamageTaken; + + int m_EmoteType; + int m_EmoteStop; + + // last tick that the player took any action ie some input + int m_LastAction; + + // these are non-heldback inputs + CNetObj_PlayerInput m_LatestPrevInput; + CNetObj_PlayerInput m_LatestInput; + + // input + CNetObj_PlayerInput m_PrevInput; + CNetObj_PlayerInput m_Input; + int m_NumInputs; + int m_Jumped; + + int m_DamageTakenTick; + + int m_Health; + int m_Armor; + + // ninja + struct + { + vec2 m_ActivationDir; + int m_ActivationTick; + int m_CurrentMoveTime; + + } m_Ninja; + + int m_PlayerState;// if the client is chatting, accessing a menu or so + + // the player core for the physics + CCharacterCore m_Core; + + // info for dead reckoning + int m_ReckoningTick; // tick that we are performing dead reckoning From + CCharacterCore m_SendCore; // core that we should send + CCharacterCore m_ReckoningCore; // the dead reckoning core + +}; + +#endif diff --git a/src/game/server/entities/character.hpp b/src/game/server/entities/character.hpp deleted file mode 100644 index bd2f7afe..00000000 --- a/src/game/server/entities/character.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_CHARACTER_H -#define GAME_SERVER_ENTITY_CHARACTER_H - -#include <game/server/entity.hpp> -#include <game/generated/gs_data.hpp> -#include <game/generated/g_protocol.hpp> - -#include <game/gamecore.hpp> - -enum -{ - WEAPON_GAME = -3, // team switching etc - WEAPON_SELF = -2, // console kill command - WEAPON_WORLD = -1, // death tiles etc -}; - -class CHARACTER : public ENTITY -{ - MACRO_ALLOC_POOL_ID() -public: - // player controlling this character - class PLAYER *player; - - bool alive; - - // weapon info - ENTITY *hitobjects[10]; - int numobjectshit; - struct WEAPONSTAT - { - int ammoregenstart; - int ammo; - int ammocost; - bool got; - } weapons[NUM_WEAPONS]; - - int active_weapon; - int last_weapon; - int queued_weapon; - - int reload_timer; - int attack_tick; - - int damage_taken; - - int emote_type; - int emote_stop; - - // TODO: clean this up - char skin_name[64]; - int use_custom_color; - int color_body; - int color_feet; - - int last_action; // last tick that the player took any action ie some input - - // these are non-heldback inputs - NETOBJ_PLAYER_INPUT latest_previnput; - NETOBJ_PLAYER_INPUT latest_input; - - // input - NETOBJ_PLAYER_INPUT previnput; - NETOBJ_PLAYER_INPUT input; - int num_inputs; - int jumped; - - int damage_taken_tick; - - int health; - int armor; - - // ninja - struct - { - vec2 activationdir; - int activationtick; - int currentmovetime; - } ninja; - - // - //int score; - int team; - int player_state; // if the client is chatting, accessing a menu or so - - // the player core for the physics - CHARACTER_CORE core; - - // info for dead reckoning - int reckoning_tick; // tick that we are performing dead reckoning from - CHARACTER_CORE sendcore; // core that we should send - CHARACTER_CORE reckoningcore; // the dead reckoning core - - // - CHARACTER(); - - virtual void reset(); - virtual void destroy(); - - bool is_grounded(); - - void set_weapon(int w); - - void handle_weaponswitch(); - void do_weaponswitch(); - - int handle_weapons(); - int handle_ninja(); - - void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input); - void on_direct_input(NETOBJ_PLAYER_INPUT *new_input); - void fire_weapon(); - - void die(int killer, int weapon); - - bool take_damage(vec2 force, int dmg, int from, int weapon); - - - bool spawn(PLAYER *player, vec2 pos, int team); - //bool init_tryspawn(int team); - bool remove(); - - static const int phys_size = 28; - - virtual void tick(); - virtual void tick_defered(); - virtual void snap(int snapping_client); - - bool increase_health(int amount); - bool increase_armor(int amount); -}; - -#endif diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 2c6fa0ff..6bc26074 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -1,115 +1,105 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <engine/e_server_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/server/gamecontext.hpp> -#include "laser.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <game/generated/protocol.h> +#include <game/server/gamecontext.h> +#include "laser.h" -////////////////////////////////////////////////// -// laser -////////////////////////////////////////////////// -LASER::LASER(vec2 pos, vec2 direction, float start_energy, int owner) -: ENTITY(NETOBJTYPE_LASER) +CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEnergy, int Owner) +: CEntity(pGameWorld, NETOBJTYPE_LASER) { - this->pos = pos; - this->owner = owner; - energy = start_energy; - dir = direction; - bounces = 0; - do_bounce(); - - game.world.insert_entity(this); + m_Pos = Pos; + m_Owner = Owner; + m_Energy = StartEnergy; + m_Dir = Direction; + m_Bounces = 0; + m_EvalTick = 0; + GameWorld()->InsertEntity(this); + DoBounce(); } -bool LASER::hit_character(vec2 from, vec2 to) +bool CLaser::HitCharacter(vec2 From, vec2 To) { - vec2 at; - CHARACTER *owner_char = game.get_player_char(owner); - CHARACTER *hit = game.world.intersect_character(pos, to, 0.0f, at, owner_char); - if(!hit) + vec2 At; + CCharacter *OwnerChar = GameServer()->GetPlayerChar(m_Owner); + CCharacter *Hit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, OwnerChar); + if(!Hit) return false; - this->from = from; - pos = at; - energy = -1; - hit->take_damage(vec2(0,0), tuning.laser_damage, owner, WEAPON_RIFLE); + m_From = From; + m_Pos = At; + m_Energy = -1; + Hit->TakeDamage(vec2(0.f, 0.f), GameServer()->Tuning()->m_LaserDamage, m_Owner, WEAPON_RIFLE); return true; } -void LASER::do_bounce() +void CLaser::DoBounce() { - eval_tick = server_tick(); + m_EvalTick = Server()->Tick(); - if(energy < 0) + if(m_Energy < 0) { - //dbg_msg("laser", "%d removed", server_tick()); - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); return; } - vec2 to = pos + dir*energy; - vec2 org_to = to; + vec2 To = m_Pos + m_Dir * m_Energy; + vec2 OrgTo = To; - if(col_intersect_line(pos, to, 0x0, &to)) + if(GameServer()->Collision()->IntersectLine(m_Pos, To, 0x0, &To)) { - if(!hit_character(pos, to)) + if(!HitCharacter(m_Pos, To)) { // intersected - from = pos; - pos = to; + m_From = m_Pos; + m_Pos = To; - vec2 temp_pos = pos; - vec2 temp_dir = dir*4.0f; + vec2 TempPos = m_Pos; + vec2 TempDir = m_Dir * 4.0f; - move_point(&temp_pos, &temp_dir, 1.0f, 0); - pos = temp_pos; - dir = normalize(temp_dir); + GameServer()->Collision()->MovePoint(&TempPos, &TempDir, 1.0f, 0); + m_Pos = TempPos; + m_Dir = normalize(TempDir); - energy -= distance(from, pos) + tuning.laser_bounce_cost; - bounces++; + m_Energy -= distance(m_From, m_Pos) + GameServer()->Tuning()->m_LaserBounceCost; + m_Bounces++; - if(bounces > tuning.laser_bounce_num) - energy = -1; + if(m_Bounces > GameServer()->Tuning()->m_LaserBounceNum) + m_Energy = -1; - game.create_sound(pos, SOUND_RIFLE_BOUNCE); + GameServer()->CreateSound(m_Pos, SOUND_RIFLE_BOUNCE); } } else { - if(!hit_character(pos, to)) + if(!HitCharacter(m_Pos, To)) { - from = pos; - pos = to; - energy = -1; + m_From = m_Pos; + m_Pos = To; + m_Energy = -1; } } - - //dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y); } -void LASER::reset() +void CLaser::Reset() { - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); } -void LASER::tick() +void CLaser::Tick() { - if(server_tick() > eval_tick+(server_tickspeed()*tuning.laser_bounce_delay)/1000.0f) - { - do_bounce(); - } - + if(Server()->Tick() > m_EvalTick+(Server()->TickSpeed()*GameServer()->Tuning()->m_LaserBounceDelay)/1000.0f) + DoBounce(); } -void LASER::snap(int snapping_client) +void CLaser::Snap(int SnappingClient) { - if(networkclipped(snapping_client)) + if(NetworkClipped(SnappingClient)) return; - NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER)); - obj->x = (int)pos.x; - obj->y = (int)pos.y; - obj->from_x = (int)from.x; - obj->from_y = (int)from.y; - obj->start_tick = eval_tick; + CNetObj_Laser *pObj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(NETOBJTYPE_LASER, m_Id, sizeof(CNetObj_Laser))); + pObj->m_X = (int)m_Pos.x; + pObj->m_Y = (int)m_Pos.y; + pObj->m_FromX = (int)m_From.x; + pObj->m_FromY = (int)m_From.y; + pObj->m_StartTick = m_EvalTick; } diff --git a/src/game/server/entities/laser.h b/src/game/server/entities/laser.h new file mode 100644 index 00000000..040cfb4c --- /dev/null +++ b/src/game/server/entities/laser.h @@ -0,0 +1,28 @@ +#ifndef GAME_SERVER_ENTITIES_LASER_H +#define GAME_SERVER_ENTITIES_LASER_H + +#include <game/server/entity.h> + +class CLaser : public CEntity +{ +public: + CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEnergy, int Owner); + + virtual void Reset(); + virtual void Tick(); + virtual void Snap(int SnappingClient); + +protected: + bool HitCharacter(vec2 From, vec2 To); + void DoBounce(); + +private: + vec2 m_From; + vec2 m_Dir; + float m_Energy; + int m_Bounces; + int m_EvalTick; + int m_Owner; +}; + +#endif diff --git a/src/game/server/entities/laser.hpp b/src/game/server/entities/laser.hpp deleted file mode 100644 index aa4c2284..00000000 --- a/src/game/server/entities/laser.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_LASER_H -#define GAME_SERVER_ENTITY_LASER_H - -#include <game/server/entity.hpp> - -class CHARACTER; - -class LASER : public ENTITY -{ - vec2 from; - vec2 dir; - float energy; - int bounces; - int eval_tick; - int owner; - - bool hit_character(vec2 from, vec2 to); - void do_bounce(); - -public: - - LASER(vec2 pos, vec2 direction, float start_energy, int owner); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp index 436282cc..9798e2c3 100644 --- a/src/game/server/entities/pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -1,143 +1,129 @@ -#include <engine/e_server_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/server/gamecontext.hpp> -#include "pickup.hpp" +#include <game/generated/protocol.h> +#include <game/server/gamecontext.h> +#include "pickup.h" -////////////////////////////////////////////////// -// pickup -////////////////////////////////////////////////// -PICKUP::PICKUP(int _type, int _subtype) -: ENTITY(NETOBJTYPE_PICKUP) +CPickup::CPickup(CGameWorld *pGameWorld, int Type, int SubType) +: CEntity(pGameWorld, NETOBJTYPE_PICKUP) { - type = _type; - subtype = _subtype; - proximity_radius = phys_size; + m_Type = Type; + m_Subtype = SubType; + m_ProximityRadius = PickupPhysSize; - reset(); - - // TODO: should this be done here? - game.world.insert_entity(this); + Reset(); + + GameWorld()->InsertEntity(this); } -void PICKUP::reset() +void CPickup::Reset() { - if (data->pickups[type].spawndelay > 0) - spawntick = server_tick() + server_tickspeed() * data->pickups[type].spawndelay; + if (g_pData->m_aPickups[m_Type].m_Spawndelay > 0) + m_SpawnTick = Server()->Tick() + Server()->TickSpeed() * g_pData->m_aPickups[m_Type].m_Spawndelay; else - spawntick = -1; + m_SpawnTick = -1; } -void PICKUP::tick() +void CPickup::Tick() { // wait for respawn - if(spawntick > 0) + if(m_SpawnTick > 0) { - if(server_tick() > spawntick) + if(Server()->Tick() > m_SpawnTick) { // respawn - spawntick = -1; + m_SpawnTick = -1; - if(type == POWERUP_WEAPON) - game.create_sound(pos, SOUND_WEAPON_SPAWN); + if(m_Type == POWERUP_WEAPON) + GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SPAWN); } else return; } // Check if a player intersected us - CHARACTER *chr = game.world.closest_character(pos, 20.0f, 0); - if(chr) + CCharacter *pChr = GameServer()->m_World.ClosestCharacter(m_Pos, 20.0f, 0); + if(pChr && pChr->IsAlive()) { // player picked us up, is someone was hooking us, let them go - int respawntime = -1; - switch (type) + int RespawnTime = -1; + switch (m_Type) { - case POWERUP_HEALTH: - if(chr->increase_health(1)) - { - game.create_sound(pos, SOUND_PICKUP_HEALTH); - respawntime = data->pickups[type].respawntime; - } - break; - case POWERUP_ARMOR: - if(chr->increase_armor(1)) - { - game.create_sound(pos, SOUND_PICKUP_ARMOR); - respawntime = data->pickups[type].respawntime; - } - break; + case POWERUP_HEALTH: + if(pChr->IncreaseHealth(1)) + { + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_HEALTH); + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; + } + break; + + case POWERUP_ARMOR: + if(pChr->IncreaseArmor(1)) + { + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_ARMOR); + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; + } + break; - case POWERUP_WEAPON: - if(subtype >= 0 && subtype < NUM_WEAPONS) - { - if(chr->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !chr->weapons[subtype].got) + case POWERUP_WEAPON: + if(m_Subtype >= 0 && m_Subtype < NUM_WEAPONS) { - chr->weapons[subtype].got = true; - chr->weapons[subtype].ammo = min(data->weapons.id[subtype].maxammo, chr->weapons[subtype].ammo + 10); - respawntime = data->pickups[type].respawntime; + if(pChr->GiveWeapon(m_Subtype, 10)) + { + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; - // TODO: data compiler should take care of stuff like this - if(subtype == WEAPON_GRENADE) - game.create_sound(pos, SOUND_PICKUP_GRENADE); - else if(subtype == WEAPON_SHOTGUN) - game.create_sound(pos, SOUND_PICKUP_SHOTGUN); - else if(subtype == WEAPON_RIFLE) - game.create_sound(pos, SOUND_PICKUP_SHOTGUN); + if(m_Subtype == WEAPON_GRENADE) + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_GRENADE); + else if(m_Subtype == WEAPON_SHOTGUN) + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_SHOTGUN); + else if(m_Subtype == WEAPON_RIFLE) + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_SHOTGUN); - if(chr->player) - game.send_weapon_pickup(chr->player->client_id, subtype); + if(pChr->GetPlayer()) + GameServer()->SendWeaponPickup(pChr->GetPlayer()->GetCID(), m_Subtype); + } } - } - break; - case POWERUP_NINJA: - { - // activate ninja on target player - chr->ninja.activationtick = server_tick(); - chr->weapons[WEAPON_NINJA].got = true; - chr->weapons[WEAPON_NINJA].ammo = -1; - chr->last_weapon = chr->active_weapon; - chr->active_weapon = WEAPON_NINJA; - respawntime = data->pickups[type].respawntime; - game.create_sound(pos, SOUND_PICKUP_NINJA); - - // loop through all players, setting their emotes - ENTITY *ents[64]; - int num = game.world.find_entities(vec2(0, 0), 1000000, ents, 64, NETOBJTYPE_CHARACTER); - for (int i = 0; i < num; i++) + break; + + case POWERUP_NINJA: { - CHARACTER *c = (CHARACTER *)ents[i]; - if (c != chr) + // activate ninja on target player + pChr->GiveNinja(); + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; + + // loop through all players, setting their emotes + CEntity *apEnts[64]; + int Num = GameServer()->m_World.FindEntities(vec2(0, 0), 1000000, apEnts, 64, NETOBJTYPE_CHARACTER); + + for (int i = 0; i < Num; ++i) { - c->emote_type = EMOTE_SURPRISE; - c->emote_stop = server_tick() + server_tickspeed(); + CCharacter *pC = static_cast<CCharacter *>(apEnts[i]); + if (pC != pChr) + pC->SetEmote(EMOTE_SURPRISE, Server()->Tick() + Server()->TickSpeed()); } - } - chr->emote_type = EMOTE_ANGRY; - chr->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; + pChr->SetEmote(EMOTE_ANGRY, Server()->Tick() + 1200 * Server()->TickSpeed() / 1000); + break; + } + default: break; - } - default: - break; }; - if(respawntime >= 0) + if(RespawnTime >= 0) { dbg_msg("game", "pickup player='%d:%s' item=%d/%d", - chr->player->client_id, server_clientname(chr->player->client_id), type, subtype); - spawntick = server_tick() + server_tickspeed() * respawntime; + pChr->GetPlayer()->GetCID(), Server()->ClientName(pChr->GetPlayer()->GetCID()), m_Type, m_Subtype); + m_SpawnTick = Server()->Tick() + Server()->TickSpeed() * RespawnTime; } } } -void PICKUP::snap(int snapping_client) +void CPickup::Snap(int SnappingClient) { - if(spawntick != -1) + if(m_SpawnTick != -1 || NetworkClipped(SnappingClient)) return; - NETOBJ_PICKUP *up = (NETOBJ_PICKUP *)snap_new_item(NETOBJTYPE_PICKUP, id, sizeof(NETOBJ_PICKUP)); - up->x = (int)pos.x; - up->y = (int)pos.y; - up->type = type; // TODO: two diffrent types? what gives? - up->subtype = subtype; + CNetObj_Pickup *pP = static_cast<CNetObj_Pickup *>(Server()->SnapNewItem(NETOBJTYPE_PICKUP, m_Id, sizeof(CNetObj_Pickup))); + pP->m_X = (int)m_Pos.x; + pP->m_Y = (int)m_Pos.y; + pP->m_Type = m_Type; + pP->m_Subtype = m_Subtype; } diff --git a/src/game/server/entities/pickup.h b/src/game/server/entities/pickup.h new file mode 100644 index 00000000..c076464c --- /dev/null +++ b/src/game/server/entities/pickup.h @@ -0,0 +1,23 @@ +#ifndef GAME_SERVER_ENTITIES_PICKUP_H +#define GAME_SERVER_ENTITIES_PICKUP_H + +#include <game/server/entity.h> + +const int PickupPhysSize = 14; + +class CPickup : public CEntity +{ +public: + CPickup(CGameWorld *pGameWorld, int Type, int SubType = 0); + + virtual void Reset(); + virtual void Tick(); + virtual void Snap(int SnappingClient); + +private: + int m_Type; + int m_Subtype; + int m_SpawnTick; +}; + +#endif diff --git a/src/game/server/entities/pickup.hpp b/src/game/server/entities/pickup.hpp deleted file mode 100644 index cd480d92..00000000 --- a/src/game/server/entities/pickup.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_PICKUP_H -#define GAME_SERVER_ENTITY_PICKUP_H - -#include <game/server/entity.hpp> - -// TODO: move to seperate file -class PICKUP : public ENTITY -{ -public: - static const int phys_size = 14; - - int type; - int subtype; // weapon type for instance? - int spawntick; - PICKUP(int _type, int _subtype = 0); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index 2a8de766..18652ba1 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -1,105 +1,102 @@ -#include <engine/e_server_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/server/gamecontext.hpp> -#include "projectile.hpp" +#include <game/generated/protocol.h> +#include <game/server/gamecontext.h> +#include "projectile.h" - -////////////////////////////////////////////////// -// projectile -////////////////////////////////////////////////// -PROJECTILE::PROJECTILE(int type, int owner, vec2 pos, vec2 dir, int span, - int damage, int flags, float force, int sound_impact, int weapon) -: ENTITY(NETOBJTYPE_PROJECTILE) +CProjectile::CProjectile(CGameWorld *pGameWorld, int Type, int Owner, vec2 Pos, vec2 Dir, int Span, + int Damage, bool Explosive, float Force, int SoundImpact, int Weapon) +: CEntity(pGameWorld, NETOBJTYPE_PROJECTILE) { - this->type = type; - this->pos = pos; - this->direction = dir; - this->lifespan = span; - this->owner = owner; - this->flags = flags; - this->force = force; - this->damage = damage; - this->sound_impact = sound_impact; - this->weapon = weapon; - this->bounce = 0; - this->start_tick = server_tick(); - game.world.insert_entity(this); + m_Type = Type; + m_Pos = Pos; + m_Direction = Dir; + m_LifeSpan = Span; + m_Owner = Owner; + m_Force = Force; + m_Damage = Damage; + m_SoundImpact = SoundImpact; + m_Weapon = Weapon; + m_StartTick = Server()->Tick(); + m_Explosive = Explosive; + + GameWorld()->InsertEntity(this); } -void PROJECTILE::reset() +void CProjectile::Reset() { - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); } -vec2 PROJECTILE::get_pos(float time) +vec2 CProjectile::GetPos(float Time) { - float curvature = 0; - float speed = 0; - if(type == WEAPON_GRENADE) - { - curvature = tuning.grenade_curvature; - speed = tuning.grenade_speed; - } - else if(type == WEAPON_SHOTGUN) - { - curvature = tuning.shotgun_curvature; - speed = tuning.shotgun_speed; - } - else if(type == WEAPON_GUN) + float Curvature = 0; + float Speed = 0; + + switch(m_Type) { - curvature = tuning.gun_curvature; - speed = tuning.gun_speed; + case WEAPON_GRENADE: + Curvature = GameServer()->Tuning()->m_GrenadeCurvature; + Speed = GameServer()->Tuning()->m_GrenadeSpeed; + break; + + case WEAPON_SHOTGUN: + Curvature = GameServer()->Tuning()->m_ShotgunCurvature; + Speed = GameServer()->Tuning()->m_ShotgunSpeed; + break; + + case WEAPON_GUN: + Curvature = GameServer()->Tuning()->m_GunCurvature; + Speed = GameServer()->Tuning()->m_GunSpeed; + break; } - return calc_pos(pos, direction, curvature, speed, time); + return CalcPos(m_Pos, m_Direction, Curvature, Speed, Time); } -void PROJECTILE::tick() +void CProjectile::Tick() { - - float pt = (server_tick()-start_tick-1)/(float)server_tickspeed(); - float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - vec2 prevpos = get_pos(pt); - vec2 curpos = get_pos(ct); + float Pt = (Server()->Tick()-m_StartTick-1)/(float)Server()->TickSpeed(); + float Ct = (Server()->Tick()-m_StartTick)/(float)Server()->TickSpeed(); + vec2 PrevPos = GetPos(Pt); + vec2 CurPos = GetPos(Ct); + int Collide = GameServer()->Collision()->IntersectLine(PrevPos, CurPos, &CurPos, 0); + CCharacter *OwnerChar = GameServer()->GetPlayerChar(m_Owner); + CCharacter *TargetChr = GameServer()->m_World.IntersectCharacter(PrevPos, CurPos, 6.0f, CurPos, OwnerChar); - lifespan--; + m_LifeSpan--; - int collide = col_intersect_line(prevpos, curpos, &curpos, 0); - //int collide = col_check_point((int)curpos.x, (int)curpos.y); - CHARACTER *ownerchar = game.get_player_char(owner); - CHARACTER *targetchr = game.world.intersect_character(prevpos, curpos, 6.0f, curpos, ownerchar); - if(targetchr || collide || lifespan < 0) + if(TargetChr || Collide || m_LifeSpan < 0) { - if(lifespan >= 0 || weapon == WEAPON_GRENADE) - game.create_sound(curpos, sound_impact); + if(m_LifeSpan >= 0 || m_Weapon == WEAPON_GRENADE) + GameServer()->CreateSound(CurPos, m_SoundImpact); - if(flags & PROJECTILE_FLAGS_EXPLODE) - game.create_explosion(curpos, owner, weapon, false); - else if(targetchr) - targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon); + if(m_Explosive) + GameServer()->CreateExplosion(CurPos, m_Owner, m_Weapon, false); + + else if(TargetChr) + TargetChr->TakeDamage(m_Direction * max(0.001f, m_Force), m_Damage, m_Owner, m_Weapon); - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); } } -void PROJECTILE::fill_info(NETOBJ_PROJECTILE *proj) +void CProjectile::FillInfo(CNetObj_Projectile *pProj) { - proj->x = (int)pos.x; - proj->y = (int)pos.y; - proj->vx = (int)(direction.x*100.0f); - proj->vy = (int)(direction.y*100.0f); - proj->start_tick = start_tick; - proj->type = type; + pProj->m_X = (int)m_Pos.x; + pProj->m_Y = (int)m_Pos.y; + pProj->m_VelX = (int)(m_Direction.x*100.0f); + pProj->m_VelY = (int)(m_Direction.y*100.0f); + pProj->m_StartTick = m_StartTick; + pProj->m_Type = m_Type; } -void PROJECTILE::snap(int snapping_client) +void CProjectile::Snap(int SnappingClient) { - float ct = (server_tick()-start_tick)/(float)server_tickspeed(); + float Ct = (Server()->Tick()-m_StartTick)/(float)Server()->TickSpeed(); - if(networkclipped(snapping_client, get_pos(ct))) + if(NetworkClipped(SnappingClient, GetPos(Ct))) return; - NETOBJ_PROJECTILE *proj = (NETOBJ_PROJECTILE *)snap_new_item(NETOBJTYPE_PROJECTILE, id, sizeof(NETOBJ_PROJECTILE)); - fill_info(proj); + CNetObj_Projectile *pProj = static_cast<CNetObj_Projectile *>(Server()->SnapNewItem(NETOBJTYPE_PROJECTILE, m_Id, sizeof(CNetObj_Projectile))); + FillInfo(pProj); } diff --git a/src/game/server/entities/projectile.h b/src/game/server/entities/projectile.h new file mode 100644 index 00000000..87f4f6c3 --- /dev/null +++ b/src/game/server/entities/projectile.h @@ -0,0 +1,30 @@ +#ifndef GAME_SERVER_ENTITIES_PROJECTILE_H +#define GAME_SERVER_ENTITIES_PROJECTILE_H + +class CProjectile : public CEntity +{ +public: + CProjectile(CGameWorld *pGameWorld, int Type, int Owner, vec2 Pos, vec2 Dir, int Span, + int Damage, bool Explosive, float Force, int SoundImpact, int Weapon); + + vec2 GetPos(float Time); + void FillInfo(CNetObj_Projectile *pProj); + + virtual void Reset(); + virtual void Tick(); + virtual void Snap(int SnappingClient); + +private: + vec2 m_Direction; + int m_LifeSpan; + int m_Owner; + int m_Type; + int m_Damage; + int m_SoundImpact; + int m_Weapon; + float m_Force; + int m_StartTick; + bool m_Explosive; +}; + +#endif diff --git a/src/game/server/entities/projectile.hpp b/src/game/server/entities/projectile.hpp deleted file mode 100644 index a5c3b88f..00000000 --- a/src/game/server/entities/projectile.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_PROJECTILE_H -#define GAME_SERVER_ENTITY_PROJECTILE_H - -class PROJECTILE : public ENTITY -{ -public: - enum - { - PROJECTILE_FLAGS_EXPLODE = 1 << 0, - }; - - vec2 direction; - int lifespan; - int owner; - int type; - int flags; - int damage; - int sound_impact; - int weapon; - int bounce; - float force; - int start_tick; - - PROJECTILE(int type, int owner, vec2 pos, vec2 vel, int span, - int damage, int flags, float force, int sound_impact, int weapon); - - vec2 get_pos(float time); - void fill_info(NETOBJ_PROJECTILE *proj); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/entity.cpp b/src/game/server/entity.cpp index 8e3345ab..d17c3fab 100644 --- a/src/game/server/entity.cpp +++ b/src/game/server/entity.cpp @@ -1,49 +1,50 @@ -#include <engine/e_server_interface.h> -#include "entity.hpp" -#include "gamecontext.hpp" +#include "entity.h" +#include "gamecontext.h" ////////////////////////////////////////////////// // Entity ////////////////////////////////////////////////// -ENTITY::ENTITY(int objtype) +CEntity::CEntity(CGameWorld *pGameWorld, int ObjType) { - this->objtype = objtype; - pos = vec2(0,0); - proximity_radius = 0; + m_pGameWorld = pGameWorld; + + m_Objtype = ObjType; + m_Pos = vec2(0,0); + m_ProximityRadius = 0; - marked_for_destroy = false; - id = snap_new_id(); + m_MarkedForDestroy = false; + m_Id = Server()->SnapNewID(); - next_entity = 0; - prev_entity = 0; - prev_type_entity = 0; - next_type_entity = 0; + m_pNextEntity = 0; + m_pPrevEntity = 0; + m_pPrevTypeEntity = 0; + m_pNextTypeEntity = 0; } -ENTITY::~ENTITY() +CEntity::~CEntity() { - game.world.remove_entity(this); - snap_free_id(id); + GameWorld()->RemoveEntity(this); + Server()->SnapFreeID(m_Id); } -int ENTITY::networkclipped(int snapping_client) +int CEntity::NetworkClipped(int SnappingClient) { - return networkclipped(snapping_client, pos); + return NetworkClipped(SnappingClient, m_Pos); } -int ENTITY::networkclipped(int snapping_client, vec2 check_pos) +int CEntity::NetworkClipped(int SnappingClient, vec2 CheckPos) { - if(snapping_client == -1) + if(SnappingClient == -1) return 0; - float dx = game.players[snapping_client]->view_pos.x-check_pos.x; - float dy = game.players[snapping_client]->view_pos.y-check_pos.y; + float dx = GameServer()->m_apPlayers[SnappingClient]->m_ViewPos.x-CheckPos.x; + float dy = GameServer()->m_apPlayers[SnappingClient]->m_ViewPos.y-CheckPos.y; - if(fabs(dx) > 1000.0f || fabs(dy) > 800.0f) + if(absolute(dx) > 1000.0f || absolute(dy) > 800.0f) return 1; - if(distance(game.players[snapping_client]->view_pos, check_pos) > 1100.0f) + if(distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, CheckPos) > 1100.0f) return 1; return 0; } diff --git a/src/game/server/entity.hpp b/src/game/server/entity.h index debe57b6..b7fd3d94 100644 --- a/src/game/server/entity.hpp +++ b/src/game/server/entity.h @@ -2,101 +2,109 @@ #define GAME_SERVER_ENTITY_H #include <new> -#include <base/vmath.hpp> +#include <base/vmath.h> +#include <game/server/gameworld.h> #define MACRO_ALLOC_HEAP() \ public: \ - void *operator new(size_t size) \ + void *operator new(size_t Size) \ { \ - void *p = mem_alloc(size, 1); \ + void *p = mem_alloc(Size, 1); \ /*dbg_msg("", "++ %p %d", p, size);*/ \ - mem_zero(p, size); \ + mem_zero(p, Size); \ return p; \ } \ - void operator delete(void *p) \ + void operator delete(void *pPtr) \ { \ /*dbg_msg("", "-- %p", p);*/ \ - mem_free(p); \ + mem_free(pPtr); \ } \ private: #define MACRO_ALLOC_POOL_ID() \ public: \ - void *operator new(size_t size, int id); \ + void *operator new(size_t Size, int id); \ void operator delete(void *p); \ private: -#define MACRO_ALLOC_POOL_ID_IMPL(POOLTYPE, poolsize) \ - static char pool_data_##POOLTYPE[poolsize][sizeof(POOLTYPE)] = {{0}}; \ - static int pool_used_##POOLTYPE[poolsize] = {0}; \ - void *POOLTYPE::operator new(size_t size, int id) \ +#define MACRO_ALLOC_POOL_ID_IMPL(POOLTYPE, PoolSize) \ + static char ms_PoolData##POOLTYPE[PoolSize][sizeof(POOLTYPE)] = {{0}}; \ + static int ms_PoolUsed##POOLTYPE[PoolSize] = {0}; \ + void *POOLTYPE::operator new(size_t Size, int id) \ { \ - dbg_assert(sizeof(POOLTYPE) == size, "size error"); \ - dbg_assert(!pool_used_##POOLTYPE[id], "already used"); \ + dbg_assert(sizeof(POOLTYPE) == Size, "size error"); \ + dbg_assert(!ms_PoolUsed##POOLTYPE[id], "already used"); \ /*dbg_msg("pool", "++ %s %d", #POOLTYPE, id);*/ \ - pool_used_##POOLTYPE[id] = 1; \ - mem_zero(pool_data_##POOLTYPE[id], size); \ - return pool_data_##POOLTYPE[id]; \ + ms_PoolUsed##POOLTYPE[id] = 1; \ + mem_zero(ms_PoolData##POOLTYPE[id], Size); \ + return ms_PoolData##POOLTYPE[id]; \ } \ void POOLTYPE::operator delete(void *p) \ { \ - int id = (POOLTYPE*)p - (POOLTYPE*)pool_data_##POOLTYPE; \ - dbg_assert(pool_used_##POOLTYPE[id], "not used"); \ + int id = (POOLTYPE*)p - (POOLTYPE*)ms_PoolData##POOLTYPE; \ + dbg_assert(ms_PoolUsed##POOLTYPE[id], "not used"); \ /*dbg_msg("pool", "-- %s %d", #POOLTYPE, id);*/ \ - pool_used_##POOLTYPE[id] = 0; \ - mem_zero(pool_data_##POOLTYPE[id], sizeof(POOLTYPE)); \ + ms_PoolUsed##POOLTYPE[id] = 0; \ + mem_zero(ms_PoolData##POOLTYPE[id], sizeof(POOLTYPE)); \ } /* Class: Entity Basic entity class. */ -class ENTITY +class CEntity { MACRO_ALLOC_HEAP() private: - friend class GAMEWORLD; // thy these? - ENTITY *prev_entity; - ENTITY *next_entity; + friend class CGameWorld; // thy these? + CEntity *m_pPrevEntity; + CEntity *m_pNextEntity; - ENTITY *prev_type_entity; - ENTITY *next_type_entity; + CEntity *m_pPrevTypeEntity; + CEntity *m_pNextTypeEntity; + + class CGameWorld *m_pGameWorld; protected: - bool marked_for_destroy; - int id; - int objtype; + bool m_MarkedForDestroy; + int m_Id; + int m_Objtype; public: - ENTITY(int objtype); - virtual ~ENTITY(); + CEntity(CGameWorld *pGameWorld, int Objtype); + virtual ~CEntity(); + + class CGameWorld *GameWorld() { return m_pGameWorld; } + class CGameContext *GameServer() { return GameWorld()->GameServer(); } + class IServer *Server() { return GameWorld()->Server(); } + - ENTITY *typenext() { return next_type_entity; } - ENTITY *typeprev() { return prev_type_entity; } + CEntity *TypeNext() { return m_pNextTypeEntity; } + CEntity *TypePrev() { return m_pPrevTypeEntity; } /* Function: destroy Destorys the entity. */ - virtual void destroy() { delete this; } + virtual void Destroy() { delete this; } /* Function: reset Called when the game resets the map. Puts the entity back to it's starting state or perhaps destroys it. */ - virtual void reset() {} + virtual void Reset() {} /* Function: tick Called progress the entity to the next tick. Updates and moves the entity to it's new state and position. */ - virtual void tick() {} + virtual void Tick() {} /* Function: tick_defered Called after all entities tick() function has been called. */ - virtual void tick_defered() {} + virtual void TickDefered() {} /* Function: snap @@ -109,7 +117,7 @@ public: snapshot of everything in the game for demo recording. */ - virtual void snap(int snapping_client) {} + virtual void Snap(int SnappingClient) {} /* Function: networkclipped(int snapping_client) @@ -125,21 +133,21 @@ public: Returns: Non-zero if the entity doesn't have to be in the snapshot. */ - int networkclipped(int snapping_client); - int networkclipped(int snapping_client, vec2 check_pos); + int NetworkClipped(int SnappingClient); + int NetworkClipped(int SnappingClient, vec2 CheckPos); /* Variable: proximity_radius Contains the physical size of the entity. */ - float proximity_radius; + float m_ProximityRadius; /* Variable: pos Contains the current posititon of the entity. */ - vec2 pos; + vec2 m_Pos; }; #endif diff --git a/src/game/server/eventhandler.cpp b/src/game/server/eventhandler.cpp index 761eaf2c..48b6689e 100644 --- a/src/game/server/eventhandler.cpp +++ b/src/game/server/eventhandler.cpp @@ -1,48 +1,54 @@ -#include "eventhandler.hpp" -#include "gamecontext.hpp" +#include "eventhandler.h" +#include "gamecontext.h" ////////////////////////////////////////////////// // Event handler ////////////////////////////////////////////////// -EVENTHANDLER::EVENTHANDLER() +CEventHandler::CEventHandler() { - clear(); + m_pGameServer = 0; + Clear(); } -void *EVENTHANDLER::create(int type, int size, int mask) +void CEventHandler::SetGameServer(CGameContext *pGameServer) { - if(num_events == MAX_EVENTS) + m_pGameServer = pGameServer; +} + +void *CEventHandler::Create(int Type, int Size, int Mask) +{ + if(m_NumEvents == MAX_EVENTS) return 0; - if(current_offset+size >= MAX_DATASIZE) + if(m_CurrentOffset+Size >= MAX_DATASIZE) return 0; - void *p = &data[current_offset]; - offsets[num_events] = current_offset; - types[num_events] = type; - sizes[num_events] = size; - client_masks[num_events] = mask; - current_offset += size; - num_events++; + void *p = &m_aData[m_CurrentOffset]; + m_aOffsets[m_NumEvents] = m_CurrentOffset; + m_aTypes[m_NumEvents] = Type; + m_aSizes[m_NumEvents] = Size; + m_aClientMasks[m_NumEvents] = Mask; + m_CurrentOffset += Size; + m_NumEvents++; return p; } -void EVENTHANDLER::clear() +void CEventHandler::Clear() { - num_events = 0; - current_offset = 0; + m_NumEvents = 0; + m_CurrentOffset = 0; } -void EVENTHANDLER::snap(int snapping_client) +void CEventHandler::Snap(int SnappingClient) { - for(int i = 0; i < num_events; i++) + for(int i = 0; i < m_NumEvents; i++) { - if(snapping_client == -1 || cmask_is_set(client_masks[i], snapping_client)) + if(SnappingClient == -1 || CmaskIsSet(m_aClientMasks[i], SnappingClient)) { - NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&data[offsets[i]]; - if(snapping_client == -1 || distance(game.players[snapping_client]->view_pos, vec2(ev->x, ev->y)) < 1500.0f) + NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&m_aData[m_aOffsets[i]]; + if(SnappingClient == -1 || distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, vec2(ev->m_X, ev->m_Y)) < 1500.0f) { - void *d = snap_new_item(types[i], i, sizes[i]); - mem_copy(d, &data[offsets[i]], sizes[i]); + void *d = GameServer()->Server()->SnapNewItem(m_aTypes[i], i, m_aSizes[i]); + mem_copy(d, &m_aData[m_aOffsets[i]], m_aSizes[i]); } } } diff --git a/src/game/server/eventhandler.h b/src/game/server/eventhandler.h new file mode 100644 index 00000000..3833efe0 --- /dev/null +++ b/src/game/server/eventhandler.h @@ -0,0 +1,30 @@ +#ifndef GAME_SERVER_EVENTHANDLER_H +#define GAME_SERVER_EVENTHANDLER_H + +// +class CEventHandler +{ + static const int MAX_EVENTS = 128; + static const int MAX_DATASIZE = 128*64; + + int m_aTypes[MAX_EVENTS]; // TODO: remove some of these arrays + int m_aOffsets[MAX_EVENTS]; + int m_aSizes[MAX_EVENTS]; + int m_aClientMasks[MAX_EVENTS]; + char m_aData[MAX_DATASIZE]; + + class CGameContext *m_pGameServer; + + int m_CurrentOffset; + int m_NumEvents; +public: + CGameContext *GameServer() const { return m_pGameServer; } + void SetGameServer(CGameContext *pGameServer); + + CEventHandler(); + void *Create(int Type, int Size, int Mask = -1); + void Clear(); + void Snap(int SnappingClient); +}; + +#endif diff --git a/src/game/server/eventhandler.hpp b/src/game/server/eventhandler.hpp deleted file mode 100644 index 4d513154..00000000 --- a/src/game/server/eventhandler.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef GAME_SERVER_EVENTHANDLER_H -#define GAME_SERVER_EVENTHANDLER_H - -// -class EVENTHANDLER -{ - static const int MAX_EVENTS = 128; - static const int MAX_DATASIZE = 128*64; - - int types[MAX_EVENTS]; // TODO: remove some of these arrays - int offsets[MAX_EVENTS]; - int sizes[MAX_EVENTS]; - int client_masks[MAX_EVENTS]; - char data[MAX_DATASIZE]; - - int current_offset; - int num_events; -public: - EVENTHANDLER(); - void *create(int type, int size, int mask = -1); - void clear(); - void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 736d437f..795bb65f 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1,99 +1,143 @@ -#include <string.h> #include <new> -#include <engine/e_server_interface.h> -#include "gamecontext.hpp" +#include <base/math.h> +#include <engine/shared/config.h> +#include <engine/map.h> +#include <engine/console.h> +#include "gamecontext.h" +#include <game/version.h> +#include <game/collision.h> +#include <game/gamecore.h> +#include "gamemodes/dm.h" +#include "gamemodes/tdm.h" +#include "gamemodes/ctf.h" +#include "gamemodes/mod.h" -GAMECONTEXT game; +enum +{ + RESET, + NO_RESET +}; -GAMECONTEXT::GAMECONTEXT() +void CGameContext::Construct(int Resetting) { + m_Resetting = 0; + m_pServer = 0; + for(int i = 0; i < MAX_CLIENTS; i++) - players[i] = 0; + m_apPlayers[i] = 0; - controller = 0; - vote_closetime = 0; + m_pController = 0; + m_VoteCloseTime = 0; + m_pVoteOptionFirst = 0; + m_pVoteOptionLast = 0; + + if(Resetting==NO_RESET) + m_pVoteOptionHeap = new CHeap(); +} + +CGameContext::CGameContext(int Resetting) +{ + Construct(Resetting); +} + +CGameContext::CGameContext() +{ + Construct(NO_RESET); } -GAMECONTEXT::~GAMECONTEXT() +CGameContext::~CGameContext() { for(int i = 0; i < MAX_CLIENTS; i++) - delete players[i]; + delete m_apPlayers[i]; + if(!m_Resetting) + delete m_pVoteOptionHeap; } -void GAMECONTEXT::clear() +void CGameContext::Clear() { - this->~GAMECONTEXT(); + CHeap *pVoteOptionHeap = m_pVoteOptionHeap; + CVoteOption *pVoteOptionFirst = m_pVoteOptionFirst; + CVoteOption *pVoteOptionLast = m_pVoteOptionLast; + CTuningParams Tuning = m_Tuning; + + m_Resetting = true; + this->~CGameContext(); mem_zero(this, sizeof(*this)); - new (this) GAMECONTEXT(); + new (this) CGameContext(RESET); + + m_pVoteOptionHeap = pVoteOptionHeap; + m_pVoteOptionFirst = pVoteOptionFirst; + m_pVoteOptionLast = pVoteOptionLast; + m_Tuning = Tuning; } -class CHARACTER *GAMECONTEXT::get_player_char(int client_id) +class CCharacter *CGameContext::GetPlayerChar(int ClientId) { - if(client_id < 0 || client_id >= MAX_CLIENTS || !players[client_id]) + if(ClientId < 0 || ClientId >= MAX_CLIENTS || !m_apPlayers[ClientId]) return 0; - return players[client_id]->get_character(); + return m_apPlayers[ClientId]->GetCharacter(); } -void GAMECONTEXT::create_damageind(vec2 p, float angle, int amount) +void CGameContext::CreateDamageInd(vec2 p, float Angle, int Amount) { - float a = 3 * 3.14159f / 2 + angle; + float a = 3 * 3.14159f / 2 + Angle; //float a = get_angle(dir); float s = a-pi/3; float e = a+pi/3; - for(int i = 0; i < amount; i++) + for(int i = 0; i < Amount; i++) { - float f = mix(s, e, float(i+1)/float(amount+2)); - NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)events.create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND)); + float f = mix(s, e, float(i+1)/float(Amount+2)); + NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; - ev->angle = (int)(f*256.0f); + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; + ev->m_Angle = (int)(f*256.0f); } } } -void GAMECONTEXT::create_hammerhit(vec2 p) +void CGameContext::CreateHammerHit(vec2 p) { // create the event - NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)events.create(NETEVENTTYPE_HAMMERHIT, sizeof(NETEVENT_HAMMERHIT)); + NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(NETEVENT_HAMMERHIT)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; } } -void GAMECONTEXT::create_explosion(vec2 p, int owner, int weapon, bool bnodamage) +void CGameContext::CreateExplosion(vec2 p, int Owner, int Weapon, bool NoDamage) { // create the event - NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)events.create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); + NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; } - if (!bnodamage) + if (!NoDamage) { // deal damage - CHARACTER *ents[64]; - float radius = 135.0f; - float innerradius = 48.0f; - int num = game.world.find_entities(p, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); - for(int i = 0; i < num; i++) + CCharacter *apEnts[64]; + float Radius = 135.0f; + float InnerRadius = 48.0f; + int Num = m_World.FindEntities(p, Radius, (CEntity**)apEnts, 64, NETOBJTYPE_CHARACTER); + for(int i = 0; i < Num; i++) { - vec2 diff = ents[i]->pos - p; - vec2 forcedir(0,1); - float l = length(diff); + vec2 Diff = apEnts[i]->m_Pos - p; + vec2 ForceDir(0,1); + float l = length(Diff); if(l) - forcedir = normalize(diff); - l = 1-clamp((l-innerradius)/(radius-innerradius), 0.0f, 1.0f); - float dmg = 6 * l; - if((int)dmg) - ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner, weapon); + ForceDir = normalize(Diff); + l = 1-clamp((l-InnerRadius)/(Radius-InnerRadius), 0.0f, 1.0f); + float Dmg = 6 * l; + if((int)Dmg) + apEnts[i]->TakeDamage(ForceDir*Dmg*2, (int)Dmg, Owner, Weapon); } } } @@ -110,273 +154,893 @@ void create_smoke(vec2 p) } }*/ -void GAMECONTEXT::create_playerspawn(vec2 p) +void CGameContext::CreatePlayerSpawn(vec2 p) { // create the event - NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)events.create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN)); + NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; } } -void GAMECONTEXT::create_death(vec2 p, int cid) +void CGameContext::CreateDeath(vec2 p, int ClientId) { // create the event - NETEVENT_DEATH *ev = (NETEVENT_DEATH *)events.create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH)); + NETEVENT_DEATH *ev = (NETEVENT_DEATH *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; - ev->cid = cid; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; + ev->m_ClientId = ClientId; } } -void GAMECONTEXT::create_sound(vec2 pos, int sound, int mask) +void CGameContext::CreateSound(vec2 Pos, int Sound, int Mask) { - if (sound < 0) + if (Sound < 0) return; // create a sound - NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)events.create(NETEVENTTYPE_SOUNDWORLD, sizeof(NETEVENT_SOUNDWORLD), mask); + NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(NETEVENT_SOUNDWORLD), Mask); if(ev) { - ev->x = (int)pos.x; - ev->y = (int)pos.y; - ev->soundid = sound; + ev->m_X = (int)Pos.x; + ev->m_Y = (int)Pos.y; + ev->m_SoundId = Sound; } } -void GAMECONTEXT::create_sound_global(int sound, int target) +void CGameContext::CreateSoundGlobal(int Sound, int Target) { - if (sound < 0) + if (Sound < 0) return; - NETMSG_SV_SOUNDGLOBAL msg; - msg.soundid = sound; - msg.pack(MSGFLAG_VITAL); - server_send_msg(target); + CNetMsg_Sv_SoundGlobal Msg; + Msg.m_Soundid = Sound; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, Target); } -void GAMECONTEXT::send_chat_target(int to, const char *text) +void CGameContext::SendChatTarget(int To, const char *pText) { - NETMSG_SV_CHAT msg; - msg.team = 0; - msg.cid = -1; - msg.message = text; - msg.pack(MSGFLAG_VITAL); - server_send_msg(to); + CNetMsg_Sv_Chat Msg; + Msg.m_Team = 0; + Msg.m_Cid = -1; + Msg.m_pMessage = pText; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, To); } -void GAMECONTEXT::send_chat(int chatter_cid, int team, const char *text) +void CGameContext::SendChat(int ChatterClientId, int Team, const char *pText) { - if(chatter_cid >= 0 && chatter_cid < MAX_CLIENTS) - dbg_msg("chat", "%d:%d:%s: %s", chatter_cid, team, server_clientname(chatter_cid), text); + if(ChatterClientId >= 0 && ChatterClientId < MAX_CLIENTS) + dbg_msg("chat", "%d:%d:%s: %s", ChatterClientId, Team, Server()->ClientName(ChatterClientId), pText); else - dbg_msg("chat", "*** %s", text); + dbg_msg("chat", "*** %s", pText); - if(team == CHAT_ALL) + if(Team == CHAT_ALL) { - NETMSG_SV_CHAT msg; - msg.team = 0; - msg.cid = chatter_cid; - msg.message = text; - msg.pack(MSGFLAG_VITAL); - server_send_msg(-1); + CNetMsg_Sv_Chat Msg; + Msg.m_Team = 0; + Msg.m_Cid = ChatterClientId; + Msg.m_pMessage = pText; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); } else { - NETMSG_SV_CHAT msg; - msg.team = 1; - msg.cid = chatter_cid; - msg.message = text; + CNetMsg_Sv_Chat Msg; + Msg.m_Team = 1; + Msg.m_Cid = ChatterClientId; + Msg.m_pMessage = pText; // pack one for the recording only - msg.pack(MSGFLAG_VITAL|MSGFLAG_NOSEND); - server_send_msg(-1); + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NOSEND, -1); // send to the clients - msg.pack(MSGFLAG_VITAL|MSGFLAG_NORECORD); for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && game.players[i]->team == team) - server_send_msg(i); + if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() == Team) + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NORECORD, i); } } } -void GAMECONTEXT::send_emoticon(int cid, int emoticon) +void CGameContext::SendEmoticon(int ClientId, int Emoticon) { - NETMSG_SV_EMOTICON msg; - msg.cid = cid; - msg.emoticon = emoticon; - msg.pack(MSGFLAG_VITAL); - server_send_msg(-1); + CNetMsg_Sv_Emoticon Msg; + Msg.m_Cid = ClientId; + Msg.m_Emoticon = Emoticon; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); } -void GAMECONTEXT::send_weapon_pickup(int cid, int weapon) +void CGameContext::SendWeaponPickup(int ClientId, int Weapon) { - NETMSG_SV_WEAPONPICKUP msg; - msg.weapon = weapon; - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); + CNetMsg_Sv_WeaponPickup Msg; + Msg.m_Weapon = Weapon; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); } -void GAMECONTEXT::send_broadcast(const char *text, int cid) +void CGameContext::SendBroadcast(const char *pText, int ClientId) { - NETMSG_SV_BROADCAST msg; - msg.message = text; - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); + CNetMsg_Sv_Broadcast Msg; + Msg.m_pMessage = pText; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); } // -void GAMECONTEXT::start_vote(const char *desc, const char *command) +void CGameContext::StartVote(const char *pDesc, const char *pCommand) { // check if a vote is already running - if(vote_closetime) + if(m_VoteCloseTime) return; // reset votes - vote_enforce = VOTE_ENFORCE_UNKNOWN; + m_VoteEnforce = VOTE_ENFORCE_UNKNOWN; for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) - players[i]->vote = 0; + if(m_apPlayers[i]) + { + m_apPlayers[i]->m_Vote = 0; + m_apPlayers[i]->m_VotePos = 0; + } } // start vote - vote_closetime = time_get() + time_freq()*25; - str_copy(vote_description, desc, sizeof(vote_description)); - str_copy(vote_command, command, sizeof(vote_description)); - send_vote_set(-1); - send_vote_status(-1); + m_VoteCloseTime = time_get() + time_freq()*25; + str_copy(m_aVoteDescription, pDesc, sizeof(m_aVoteDescription)); + str_copy(m_aVoteCommand, pCommand, sizeof(m_aVoteCommand)); + SendVoteSet(-1); + m_VoteUpdate = true; } -void GAMECONTEXT::end_vote() +void CGameContext::EndVote() { - vote_closetime = 0; - send_vote_set(-1); + m_VoteCloseTime = 0; + SendVoteSet(-1); } -void GAMECONTEXT::send_vote_set(int cid) +void CGameContext::SendVoteSet(int ClientId) { - NETMSG_SV_VOTE_SET msg; - if(vote_closetime) + CNetMsg_Sv_VoteSet Msg; + if(m_VoteCloseTime) { - msg.timeout = (vote_closetime-time_get())/time_freq(); - msg.description = vote_description; - msg.command = vote_command; + Msg.m_Timeout = (m_VoteCloseTime-time_get())/time_freq(); + Msg.m_pDescription = m_aVoteDescription; + Msg.m_pCommand = ""; } else { - msg.timeout = 0; - msg.description = ""; - msg.command = ""; + Msg.m_Timeout = 0; + Msg.m_pDescription = ""; + Msg.m_pCommand = ""; } - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); } -void GAMECONTEXT::send_vote_status(int cid) +void CGameContext::SendVoteStatus(int ClientId, int Total, int Yes, int No) { - NETMSG_SV_VOTE_STATUS msg = {0}; - for(int i = 0; i < MAX_CLIENTS; i++) + CNetMsg_Sv_VoteStatus Msg = {0}; + Msg.m_Total = Total; + Msg.m_Yes = Yes; + Msg.m_No = No; + Msg.m_Pass = Total - (Yes+No); + + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); + +} + +void CGameContext::AbortVoteKickOnDisconnect(int ClientId) +{ + if(m_VoteCloseTime && !str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientId) + m_VoteCloseTime = -1; +} + + +void CGameContext::CheckPureTuning() +{ + // might not be created yet during start up + if(!m_pController) + return; + + if( str_comp(m_pController->m_pGameType, "DM")==0 || + str_comp(m_pController->m_pGameType, "TDM")==0 || + str_comp(m_pController->m_pGameType, "CTF")==0) { - if(players[i]) + CTuningParams p; + if(mem_comp(&p, &m_Tuning, sizeof(p)) != 0) { - msg.total++; - if(players[i]->vote > 0) - msg.yes++; - else if(players[i]->vote < 0) - msg.no++; - else - msg.pass++; + dbg_msg("server", "resetting tuning due to pure server"); + m_Tuning = p; } } - - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); - } -void GAMECONTEXT::abort_vote_kick_on_disconnect(int client_id) +void CGameContext::SendTuningParams(int Cid) { - if(vote_closetime && !strncmp(vote_command, "kick ", 5) && atoi(&vote_command[5]) == client_id) - vote_closetime = -1; + CheckPureTuning(); + + CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS); + int *pParams = (int *)&m_Tuning; + for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++) + Msg.AddInt(pParams[i]); + Server()->SendMsg(&Msg, MSGFLAG_VITAL, Cid); } -void GAMECONTEXT::tick() +void CGameContext::OnTick() { - world.core.tuning = tuning; - world.tick(); + // check tuning + CheckPureTuning(); + + // copy tuning + m_World.m_Core.m_Tuning = m_Tuning; + m_World.Tick(); //if(world.paused) // make sure that the game object always updates - controller->tick(); + m_pController->Tick(); for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) - players[i]->tick(); + if(m_apPlayers[i]) + m_apPlayers[i]->Tick(); } // update voting - if(vote_closetime) + if(m_VoteCloseTime) { // abort the kick-vote on player-leave - if(vote_closetime == -1) + if(m_VoteCloseTime == -1) { - send_chat(-1, GAMECONTEXT::CHAT_ALL, "Vote aborted"); - end_vote(); + SendChat(-1, CGameContext::CHAT_ALL, "Vote aborted"); + EndVote(); } else { - // count votes - int total = 0, yes = 0, no = 0; - for(int i = 0; i < MAX_CLIENTS; i++) + int Total = 0, Yes = 0, No = 0; + if(m_VoteUpdate) { - if(players[i]) + // count votes + char aaBuf[MAX_CLIENTS][64] = {{0}}; + for(int i = 0; i < MAX_CLIENTS; i++) + if(m_apPlayers[i]) + Server()->GetClientIP(i, aaBuf[i], 64); + bool aVoteChecked[MAX_CLIENTS] = {0}; + for(int i = 0; i < MAX_CLIENTS; i++) { - total++; - if(players[i]->vote > 0) - yes++; - else if(players[i]->vote < 0) - no++; + if(!m_apPlayers[i] || m_apPlayers[i]->GetTeam() == -1 || aVoteChecked[i]) // don't count in votes by spectators + continue; + + int ActVote = m_apPlayers[i]->m_Vote; + int ActVotePos = m_apPlayers[i]->m_VotePos; + + // check for more players with the same ip (only use the vote of the one who voted first) + for(int j = i+1; j < MAX_CLIENTS; ++j) + { + if(!m_apPlayers[j] || aVoteChecked[j] || str_comp(aaBuf[j], aaBuf[i])) + continue; + + aVoteChecked[j] = true; + if(m_apPlayers[j]->m_Vote && (!ActVote || ActVotePos > m_apPlayers[j]->m_VotePos)) + { + ActVote = m_apPlayers[j]->m_Vote; + ActVotePos = m_apPlayers[j]->m_VotePos; + } + } + + Total++; + if(ActVote > 0) + Yes++; + else if(ActVote < 0) + No++; } + + if(Yes >= Total/2+1) + m_VoteEnforce = VOTE_ENFORCE_YES; + else if(No >= Total/2+1 || Yes+No == Total) + m_VoteEnforce = VOTE_ENFORCE_NO; } + + if(m_VoteEnforce == VOTE_ENFORCE_YES) + { + Console()->ExecuteLine(m_aVoteCommand); + EndVote(); + SendChat(-1, CGameContext::CHAT_ALL, "Vote passed"); + + if(m_apPlayers[m_VoteCreator]) + m_apPlayers[m_VoteCreator]->m_Last_VoteCall = 0; + } + else if(m_VoteEnforce == VOTE_ENFORCE_NO || time_get() > m_VoteCloseTime) + { + EndVote(); + SendChat(-1, CGameContext::CHAT_ALL, "Vote failed"); + } + else if(m_VoteUpdate) + { + m_VoteUpdate = false; + SendVoteStatus(-1, Total, Yes, No); + } + } + } + + +#ifdef CONF_DEBUG + if(g_Config.m_DbgDummies) + { + for(int i = 0; i < g_Config.m_DbgDummies ; i++) + { + CNetObj_PlayerInput Input = {0}; + Input.m_Direction = (i&1)?-1:1; + m_apPlayers[MAX_CLIENTS-i-1]->OnPredictedInput(&Input); + } + } +#endif +} + +// Server hooks +void CGameContext::OnClientDirectInput(int ClientID, void *pInput) +{ + if(!m_World.m_Paused) + m_apPlayers[ClientID]->OnDirectInput((CNetObj_PlayerInput *)pInput); +} + +void CGameContext::OnClientPredictedInput(int ClientID, void *pInput) +{ + if(!m_World.m_Paused) + m_apPlayers[ClientID]->OnPredictedInput((CNetObj_PlayerInput *)pInput); +} + +void CGameContext::OnClientEnter(int ClientId) +{ + //world.insert_entity(&players[client_id]); + m_apPlayers[ClientId]->Respawn(); + dbg_msg("game", "join player='%d:%s'", ClientId, Server()->ClientName(ClientId)); + + + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "%s entered and joined the %s", Server()->ClientName(ClientId), m_pController->GetTeamName(m_apPlayers[ClientId]->GetTeam())); + SendChat(-1, CGameContext::CHAT_ALL, aBuf); + + dbg_msg("game", "team_join player='%d:%s' team=%d", ClientId, Server()->ClientName(ClientId), m_apPlayers[ClientId]->GetTeam()); + + m_VoteUpdate = true; +} + +void CGameContext::OnClientConnected(int ClientId) +{ + // Check which team the player should be on + const int StartTeam = g_Config.m_SvTournamentMode ? -1 : m_pController->GetAutoTeam(ClientId); + + m_apPlayers[ClientId] = new(ClientId) CPlayer(this, ClientId, StartTeam); + //players[client_id].init(client_id); + //players[client_id].client_id = client_id; + + (void)m_pController->CheckTeamBalance(); + +#ifdef CONF_DEBUG + if(g_Config.m_DbgDummies) + { + if(ClientId >= MAX_CLIENTS-g_Config.m_DbgDummies) + return; + } +#endif + + // send active vote + if(m_VoteCloseTime) + SendVoteSet(ClientId); + + // send motd + CNetMsg_Sv_Motd Msg; + Msg.m_pMessage = g_Config.m_SvMotd; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); +} + +void CGameContext::OnClientDrop(int ClientId) +{ + AbortVoteKickOnDisconnect(ClientId); + m_apPlayers[ClientId]->OnDisconnect(); + delete m_apPlayers[ClientId]; + m_apPlayers[ClientId] = 0; + + (void)m_pController->CheckTeamBalance(); + m_VoteUpdate = true; +} + +void CGameContext::OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId) +{ + void *pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgId, pUnpacker); + CPlayer *p = m_apPlayers[ClientId]; + + if(!pRawMsg) + { + dbg_msg("server", "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler.GetMsgName(MsgId), MsgId, m_NetObjHandler.FailedMsgOn()); + return; + } + + if(MsgId == NETMSGTYPE_CL_SAY) + { + CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg; + int Team = pMsg->m_Team; + if(Team) + Team = p->GetTeam(); + else + Team = CGameContext::CHAT_ALL; + + if(g_Config.m_SvSpamprotection && p->m_Last_Chat && p->m_Last_Chat+Server()->TickSpeed() > Server()->Tick()) + return; + + p->m_Last_Chat = Server()->Tick(); + + // check for invalid chars + unsigned char *pMessage = (unsigned char *)pMsg->m_pMessage; + while (*pMessage) + { + if(*pMessage < 32) + *pMessage = ' '; + pMessage++; + } + + SendChat(ClientId, Team, pMsg->m_pMessage); + } + else if(MsgId == NETMSGTYPE_CL_CALLVOTE) + { + if(g_Config.m_SvSpamprotection && p->m_Last_VoteTry && p->m_Last_VoteTry+Server()->TickSpeed()*3 > Server()->Tick()) + return; + + int64 Now = Server()->Tick(); + p->m_Last_VoteTry = Now; + if(m_VoteCloseTime) + { + SendChatTarget(ClientId, "Wait for current vote to end before calling a new one."); + return; + } - if(vote_enforce == VOTE_ENFORCE_YES || yes >= total/2+1) + int Timeleft = p->m_Last_VoteCall + Server()->TickSpeed()*60 - Now; + if(p->m_Last_VoteCall && Timeleft > 0) + { + char aChatmsg[512] = {0}; + str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote", (Timeleft/Server()->TickSpeed())+1); + SendChatTarget(ClientId, aChatmsg); + return; + } + + char aChatmsg[512] = {0}; + char aDesc[512] = {0}; + char aCmd[512] = {0}; + CNetMsg_Cl_CallVote *pMsg = (CNetMsg_Cl_CallVote *)pRawMsg; + if(str_comp_nocase(pMsg->m_Type, "option") == 0) + { + CVoteOption *pOption = m_pVoteOptionFirst; + while(pOption) { - console_execute_line(vote_command); - end_vote(); - send_chat(-1, GAMECONTEXT::CHAT_ALL, "Vote passed"); + if(str_comp_nocase(pMsg->m_Value, pOption->m_aCommand) == 0) + { + str_format(aChatmsg, sizeof(aChatmsg), "%s called vote to change server option '%s'", Server()->ClientName(ClientId), pOption->m_aCommand); + str_format(aDesc, sizeof(aDesc), "%s", pOption->m_aCommand); + str_format(aCmd, sizeof(aCmd), "%s", pOption->m_aCommand); + break; + } + + pOption = pOption->m_pNext; + } - if(players[vote_creator]) - players[vote_creator]->last_votecall = 0; + if(!pOption) + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' isn't an option on this server", pMsg->m_Value); + SendChatTarget(ClientId, aChatmsg); + return; } - else if(vote_enforce == VOTE_ENFORCE_NO || time_get() > vote_closetime || no >= total/2+1 || yes+no == total) + } + else if(str_comp_nocase(pMsg->m_Type, "kick") == 0) + { + if(!g_Config.m_SvVoteKick) { - end_vote(); - send_chat(-1, GAMECONTEXT::CHAT_ALL, "Vote failed"); + SendChatTarget(ClientId, "Server does not allow voting to kick players"); + return; } + + int KickId = str_toint(pMsg->m_Value); + if(KickId < 0 || KickId >= MAX_CLIENTS || !m_apPlayers[KickId]) + { + SendChatTarget(ClientId, "Invalid client id to kick"); + return; + } + + str_format(aChatmsg, sizeof(aChatmsg), "%s called for vote to kick '%s'", Server()->ClientName(ClientId), Server()->ClientName(KickId)); + str_format(aDesc, sizeof(aDesc), "Kick '%s'", Server()->ClientName(KickId)); + if (!g_Config.m_SvVoteKickBantime) + str_format(aCmd, sizeof(aCmd), "kick %d", KickId); + else + { + char aBuf[64] = {0}; + Server()->GetClientIP(KickId, aBuf, sizeof(aBuf)); + str_format(aCmd, sizeof(aCmd), "ban %s %d", aBuf, g_Config.m_SvVoteKickBantime); + } + } + + if(aCmd[0]) + { + SendChat(-1, CGameContext::CHAT_ALL, aChatmsg); + StartVote(aDesc, aCmd); + p->m_Vote = 1; + p->m_VotePos = m_VotePos = 1; + m_VoteCreator = ClientId; + p->m_Last_VoteCall = Now; + } + } + else if(MsgId == NETMSGTYPE_CL_VOTE) + { + if(!m_VoteCloseTime) + return; + + if(p->m_Vote == 0) + { + CNetMsg_Cl_Vote *pMsg = (CNetMsg_Cl_Vote *)pRawMsg; + if(!pMsg->m_Vote) + return; + + p->m_Vote = pMsg->m_Vote; + p->m_VotePos = ++m_VotePos; + m_VoteUpdate = true; } } + else if (MsgId == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused) + { + CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg; + + if(p->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && p->m_Last_SetTeam && p->m_Last_SetTeam+Server()->TickSpeed()*3 > Server()->Tick())) + return; + + // Switch team on given client and kill/respawn him + if(m_pController->CanJoinTeam(pMsg->m_Team, ClientId)) + { + if(m_pController->CanChangeTeam(p, pMsg->m_Team)) + { + p->m_Last_SetTeam = Server()->Tick(); + if(p->GetTeam() == -1 || pMsg->m_Team == -1) + m_VoteUpdate = true; + p->SetTeam(pMsg->m_Team); + (void)m_pController->CheckTeamBalance(); + } + else + SendBroadcast("Teams must be balanced, please join other team", ClientId); + } + else + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots); + SendBroadcast(aBuf, ClientId); + } + } + else if (MsgId == NETMSGTYPE_CL_CHANGEINFO || MsgId == NETMSGTYPE_CL_STARTINFO) + { + CNetMsg_Cl_ChangeInfo *pMsg = (CNetMsg_Cl_ChangeInfo *)pRawMsg; + + if(g_Config.m_SvSpamprotection && p->m_Last_ChangeInfo && p->m_Last_ChangeInfo+Server()->TickSpeed()*5 > Server()->Tick()) + return; + + p->m_Last_ChangeInfo = Server()->Tick(); + + p->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; + p->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; + p->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; + + // check for invalid chars + unsigned char *pName = (unsigned char *)pMsg->m_pName; + while (*pName) + { + if(*pName < 32) + *pName = ' '; + pName++; + } + + // copy old name + char aOldName[MAX_NAME_LENGTH]; + str_copy(aOldName, Server()->ClientName(ClientId), MAX_NAME_LENGTH); + + Server()->SetClientName(ClientId, pMsg->m_pName); + if(MsgId == NETMSGTYPE_CL_CHANGEINFO && str_comp(aOldName, Server()->ClientName(ClientId)) != 0) + { + char aChatText[256]; + str_format(aChatText, sizeof(aChatText), "%s changed name to %s", aOldName, Server()->ClientName(ClientId)); + SendChat(-1, CGameContext::CHAT_ALL, aChatText); + } + + // set skin + str_copy(p->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(p->m_TeeInfos.m_SkinName)); + + m_pController->OnPlayerInfoChange(p); + + if(MsgId == NETMSGTYPE_CL_STARTINFO) + { + // send vote options + CNetMsg_Sv_VoteClearOptions ClearMsg; + Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientId); + CVoteOption *pCurrent = m_pVoteOptionFirst; + while(pCurrent) + { + CNetMsg_Sv_VoteOption OptionMsg; + OptionMsg.m_pCommand = pCurrent->m_aCommand; + Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientId); + pCurrent = pCurrent->m_pNext; + } + + // send tuning parameters to client + SendTuningParams(ClientId); + + // + CNetMsg_Sv_ReadyToEnter m; + Server()->SendPackMsg(&m, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId); + } + } + else if (MsgId == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) + { + CNetMsg_Cl_Emoticon *pMsg = (CNetMsg_Cl_Emoticon *)pRawMsg; + + if(g_Config.m_SvSpamprotection && p->m_Last_Emote && p->m_Last_Emote+Server()->TickSpeed()*3 > Server()->Tick()) + return; + + p->m_Last_Emote = Server()->Tick(); + + SendEmoticon(ClientId, pMsg->m_Emoticon); + } + else if (MsgId == NETMSGTYPE_CL_KILL && !m_World.m_Paused) + { + if(p->m_Last_Kill && p->m_Last_Kill+Server()->TickSpeed()*3 > Server()->Tick()) + return; + + p->m_Last_Kill = Server()->Tick(); + p->KillCharacter(WEAPON_SELF); + p->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()*3; + } +} + +void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + const char *pParamName = pResult->GetString(0); + float NewValue = pResult->GetFloat(1); + + if(pSelf->Tuning()->Set(pParamName, NewValue)) + { + dbg_msg("tuning", "%s changed to %.2f", pParamName, NewValue); + pSelf->SendTuningParams(-1); + } + else + dbg_msg("tuning", "No such tuning parameter"); +} + +void CGameContext::ConTuneReset(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + CTuningParams p; + *pSelf->Tuning() = p; + pSelf->SendTuningParams(-1); + dbg_msg("tuning", "Tuning reset"); +} + +void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + for(int i = 0; i < pSelf->Tuning()->Num(); i++) + { + float v; + pSelf->Tuning()->Get(i, &v); + dbg_msg("tuning", "%s %.2f", pSelf->Tuning()->m_apNames[i], v); + } +} + +void CGameContext::ConChangeMap(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->m_pController->ChangeMap(pResult->GetString(0)); +} + +void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(pResult->NumArguments()) + pSelf->m_pController->DoWarmup(pResult->GetInteger(0)); + else + pSelf->m_pController->StartRound(); +} + +void CGameContext::ConBroadcast(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->SendBroadcast(pResult->GetString(0), -1); +} + +void CGameContext::ConSay(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->SendChat(-1, CGameContext::CHAT_ALL, pResult->GetString(0)); +} + +void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int ClientId = clamp(pResult->GetInteger(0), 0, (int)MAX_CLIENTS-1); + int Team = clamp(pResult->GetInteger(1), -1, 1); + + dbg_msg("", "%d %d", ClientId, Team); + + if(!pSelf->m_apPlayers[ClientId]) + return; + + pSelf->m_apPlayers[ClientId]->SetTeam(Team); + (void)pSelf->m_pController->CheckTeamBalance(); +} + +void CGameContext::ConAddVote(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int Len = str_length(pResult->GetString(0)); + + CGameContext::CVoteOption *pOption = (CGameContext::CVoteOption *)pSelf->m_pVoteOptionHeap->Allocate(sizeof(CGameContext::CVoteOption) + Len); + pOption->m_pNext = 0; + pOption->m_pPrev = pSelf->m_pVoteOptionLast; + if(pOption->m_pPrev) + pOption->m_pPrev->m_pNext = pOption; + pSelf->m_pVoteOptionLast = pOption; + if(!pSelf->m_pVoteOptionFirst) + pSelf->m_pVoteOptionFirst = pOption; + + mem_copy(pOption->m_aCommand, pResult->GetString(0), Len+1); + dbg_msg("server", "added option '%s'", pOption->m_aCommand); + + CNetMsg_Sv_VoteOption OptionMsg; + OptionMsg.m_pCommand = pOption->m_aCommand; + pSelf->Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, -1); +} + +void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(str_comp_nocase(pResult->GetString(0), "yes") == 0) + pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_YES; + else if(str_comp_nocase(pResult->GetString(0), "no") == 0) + pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO; + dbg_msg("server", "forcing vote %s", pResult->GetString(0)); +} + +void CGameContext::ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments()) + { + CNetMsg_Sv_Motd Msg; + Msg.m_pMessage = g_Config.m_SvMotd; + CGameContext *pSelf = (CGameContext *)pUserData; + for(int i = 0; i < MAX_CLIENTS; ++i) + if(pSelf->m_apPlayers[i]) + pSelf->Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, i); + } +} + +void CGameContext::OnConsoleInit() +{ + m_pServer = Kernel()->RequestInterface<IServer>(); + m_pConsole = Kernel()->RequestInterface<IConsole>(); + + Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, ""); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, ""); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, ""); + + Console()->Register("change_map", "r", CFGFLAG_SERVER, ConChangeMap, this, ""); + Console()->Register("restart", "?i", CFGFLAG_SERVER, ConRestart, this, ""); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, ""); + Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, ""); + Console()->Register("set_team", "ii", CFGFLAG_SERVER, ConSetTeam, this, ""); + + Console()->Register("addvote", "r", CFGFLAG_SERVER, ConAddVote, this, ""); + Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, ""); + + Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); +} + +void CGameContext::OnInit(/*class IKernel *pKernel*/) +{ + m_pServer = Kernel()->RequestInterface<IServer>(); + m_pConsole = Kernel()->RequestInterface<IConsole>(); + m_World.SetGameServer(this); + m_Events.SetGameServer(this); + + //if(!data) // only load once + //data = load_data_from_memory(internal_data); + + for(int i = 0; i < NUM_NETOBJTYPES; i++) + Server()->SnapSetStaticsize(i, m_NetObjHandler.GetObjSize(i)); + + m_Layers.Init(Kernel()); + m_Collision.Init(&m_Layers); + + // reset everything here + //world = new GAMEWORLD; + //players = new CPlayer[MAX_CLIENTS]; + + // select gametype + if(str_comp(g_Config.m_SvGametype, "mod") == 0) + m_pController = new CGameControllerMOD(this); + else if(str_comp(g_Config.m_SvGametype, "ctf") == 0) + m_pController = new CGameControllerCTF(this); + else if(str_comp(g_Config.m_SvGametype, "tdm") == 0) + m_pController = new CGameControllerTDM(this); + else + m_pController = new CGameControllerDM(this); + + // setup core world + //for(int i = 0; i < MAX_CLIENTS; i++) + // game.players[i].core.world = &game.world.core; + + // create all entities from the game layer + CMapItemLayerTilemap *pTileMap = m_Layers.GameLayer(); + CTile *pTiles = (CTile *)Kernel()->RequestInterface<IMap>()->GetData(pTileMap->m_Data); + + + + + /* + num_spawn_points[0] = 0; + num_spawn_points[1] = 0; + num_spawn_points[2] = 0; + */ + + for(int y = 0; y < pTileMap->m_Height; y++) + { + for(int x = 0; x < pTileMap->m_Width; x++) + { + int Index = pTiles[y*pTileMap->m_Width+x].m_Index; + + if(Index >= ENTITY_OFFSET) + { + vec2 Pos(x*32.0f+16.0f, y*32.0f+16.0f); + m_pController->OnEntity(Index-ENTITY_OFFSET, Pos); + } + } + } + + //game.world.insert_entity(game.Controller); + +#ifdef CONF_DEBUG + if(g_Config.m_DbgDummies) + { + for(int i = 0; i < g_Config.m_DbgDummies ; i++) + { + OnClientConnected(MAX_CLIENTS-i-1); + } + } +#endif +} + +void CGameContext::OnShutdown() +{ + delete m_pController; + m_pController = 0; + Clear(); } -void GAMECONTEXT::snap(int client_id) +void CGameContext::OnSnap(int ClientId) { - world.snap(client_id); - controller->snap(client_id); - events.snap(client_id); + m_World.Snap(ClientId); + m_pController->Snap(ClientId); + m_Events.Snap(ClientId); for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) - players[i]->snap(client_id); + if(m_apPlayers[i]) + m_apPlayers[i]->Snap(ClientId); } } +void CGameContext::OnPreSnap() {} +void CGameContext::OnPostSnap() +{ + m_Events.Clear(); +} + +const char *CGameContext::Version() { return GAME_VERSION; } +const char *CGameContext::NetVersion() { return GAME_NETVERSION; } + +IGameServer *CreateGameServer() { return new CGameContext; } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h new file mode 100644 index 00000000..d55203e7 --- /dev/null +++ b/src/game/server/gamecontext.h @@ -0,0 +1,168 @@ +#ifndef GAME_SERVER_GAMECONTEXT_H +#define GAME_SERVER_GAMECONTEXT_H + +#include <engine/server.h> +#include <engine/console.h> +#include <engine/shared/memheap.h> + +#include <game/layers.h> + +#include "eventhandler.h" +#include "gamecontroller.h" +#include "gameworld.h" +#include "player.h" + +/* + Tick + Game Context (CGameContext::tick) + Game World (GAMEWORLD::tick) + Reset world if requested (GAMEWORLD::reset) + All entities in the world (ENTITY::tick) + All entities in the world (ENTITY::tick_defered) + Remove entities marked for deletion (GAMEWORLD::remove_entities) + Game Controller (GAMECONTROLLER::tick) + All players (CPlayer::tick) + + + Snap + Game Context (CGameContext::snap) + Game World (GAMEWORLD::snap) + All entities in the world (ENTITY::snap) + Game Controller (GAMECONTROLLER::snap) + Events handler (EVENT_HANDLER::snap) + All players (CPlayer::snap) + +*/ +class CGameContext : public IGameServer +{ + IServer *m_pServer; + class IConsole *m_pConsole; + CLayers m_Layers; + CCollision m_Collision; + CNetObjHandler m_NetObjHandler; + CTuningParams m_Tuning; + + static void ConTuneParam(IConsole::IResult *pResult, void *pUserData); + static void ConTuneReset(IConsole::IResult *pResult, void *pUserData); + static void ConTuneDump(IConsole::IResult *pResult, void *pUserData); + static void ConChangeMap(IConsole::IResult *pResult, void *pUserData); + static void ConRestart(IConsole::IResult *pResult, void *pUserData); + static void ConBroadcast(IConsole::IResult *pResult, void *pUserData); + static void ConSay(IConsole::IResult *pResult, void *pUserData); + static void ConSetTeam(IConsole::IResult *pResult, void *pUserData); + static void ConAddVote(IConsole::IResult *pResult, void *pUserData); + static void ConVote(IConsole::IResult *pResult, void *pUserData); + static void ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + + CGameContext(int Resetting); + void Construct(int Resetting); + + bool m_Resetting; +public: + IServer *Server() const { return m_pServer; } + class IConsole *Console() { return m_pConsole; } + CCollision *Collision() { return &m_Collision; } + CTuningParams *Tuning() { return &m_Tuning; } + + CGameContext(); + ~CGameContext(); + + void Clear(); + + CEventHandler m_Events; + CPlayer *m_apPlayers[MAX_CLIENTS]; + + IGameController *m_pController; + CGameWorld m_World; + + // helper functions + class CCharacter *GetPlayerChar(int ClientId); + + // voting + void StartVote(const char *pDesc, const char *pCommand); + void EndVote(); + void SendVoteSet(int ClientId); + void SendVoteStatus(int ClientId, int Total, int Yes, int No); + void AbortVoteKickOnDisconnect(int ClientId); + + int m_VoteCreator; + int64 m_VoteCloseTime; + bool m_VoteUpdate; + int m_VotePos; + char m_aVoteDescription[512]; + char m_aVoteCommand[512]; + int m_VoteEnforce; + enum + { + VOTE_ENFORCE_UNKNOWN=0, + VOTE_ENFORCE_NO, + VOTE_ENFORCE_YES, + }; + struct CVoteOption + { + CVoteOption *m_pNext; + CVoteOption *m_pPrev; + char m_aCommand[1]; + }; + CHeap *m_pVoteOptionHeap; + CVoteOption *m_pVoteOptionFirst; + CVoteOption *m_pVoteOptionLast; + + // helper functions + void CreateDamageInd(vec2 Pos, float AngleMod, int Amount); + void CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage); + void CreateSmoke(vec2 Pos); + void CreateHammerHit(vec2 Pos); + void CreatePlayerSpawn(vec2 Pos); + void CreateDeath(vec2 Pos, int Who); + void CreateSound(vec2 Pos, int Sound, int Mask=-1); + void CreateSoundGlobal(int Sound, int Target=-1); + + + enum + { + CHAT_ALL=-2, + CHAT_SPEC=-1, + CHAT_RED=0, + CHAT_BLUE=1 + }; + + // network + void SendChatTarget(int To, const char *pText); + void SendChat(int ClientId, int Team, const char *pText); + void SendEmoticon(int ClientId, int Emoticon); + void SendWeaponPickup(int ClientId, int Weapon); + void SendBroadcast(const char *pText, int ClientId); + + + // + void CheckPureTuning(); + void SendTuningParams(int ClientId); + + // engine events + virtual void OnInit(); + virtual void OnConsoleInit(); + virtual void OnShutdown(); + + virtual void OnTick(); + virtual void OnPreSnap(); + virtual void OnSnap(int ClientId); + virtual void OnPostSnap(); + + virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId); + + virtual void OnClientConnected(int ClientId); + virtual void OnClientEnter(int ClientId); + virtual void OnClientDrop(int ClientId); + virtual void OnClientDirectInput(int ClientId, void *pInput); + virtual void OnClientPredictedInput(int ClientId, void *pInput); + + virtual const char *Version(); + virtual const char *NetVersion(); +}; + +inline int CmaskAll() { return -1; } +inline int CmaskOne(int ClientId) { return 1<<ClientId; } +inline int CmaskAllExceptOne(int ClientId) { return 0x7fffffff^CmaskOne(ClientId); } +inline bool CmaskIsSet(int Mask, int ClientId) { return (Mask&CmaskOne(ClientId)) != 0; } +#endif diff --git a/src/game/server/gamecontext.hpp b/src/game/server/gamecontext.hpp deleted file mode 100644 index bea087cb..00000000 --- a/src/game/server/gamecontext.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef GAME_SERVER_GAMECONTEXT_H -#define GAME_SERVER_GAMECONTEXT_H - -#include "eventhandler.hpp" -#include "gamecontroller.hpp" -#include "gameworld.hpp" -#include "player.hpp" - -/* - Tick - Game Context (GAMECONTEXT::tick) - Game World (GAMEWORLD::tick) - Reset world if requested (GAMEWORLD::reset) - All entities in the world (ENTITY::tick) - All entities in the world (ENTITY::tick_defered) - Remove entities marked for deletion (GAMEWORLD::remove_entities) - Game Controller (GAMECONTROLLER::tick) - All players (PLAYER::tick) - - - Snap - Game Context (GAMECONTEXT::snap) - Game World (GAMEWORLD::snap) - All entities in the world (ENTITY::snap) - Game Controller (GAMECONTROLLER::snap) - Events handler (EVENT_HANDLER::snap) - All players (PLAYER::snap) - -*/ -class GAMECONTEXT -{ -public: - GAMECONTEXT(); - ~GAMECONTEXT(); - - void clear(); - - EVENTHANDLER events; - PLAYER *players[MAX_CLIENTS]; - - GAMECONTROLLER *controller; - GAMEWORLD world; - - void tick(); - void snap(int client_id); - - // helper functions - class CHARACTER *get_player_char(int client_id); - - // voting - void start_vote(const char *desc, const char *command); - void end_vote(); - void send_vote_set(int cid); - void send_vote_status(int cid); - void abort_vote_kick_on_disconnect(int client_id); - int vote_creator; - int64 vote_closetime; - char vote_description[512]; - char vote_command[512]; - int vote_enforce; - enum - { - VOTE_ENFORCE_UNKNOWN=0, - VOTE_ENFORCE_NO, - VOTE_ENFORCE_YES, - }; - - // helper functions - void create_damageind(vec2 p, float angle_mod, int amount); - void create_explosion(vec2 p, int owner, int weapon, bool bnodamage); - void create_smoke(vec2 p); - void create_hammerhit(vec2 p); - void create_playerspawn(vec2 p); - void create_death(vec2 p, int who); - void create_sound(vec2 pos, int sound, int mask=-1); - void create_sound_global(int sound, int target=-1); - - - enum - { - CHAT_ALL=-2, - CHAT_SPEC=-1, - CHAT_RED=0, - CHAT_BLUE=1 - }; - - // network - void send_chat_target(int to, const char *text); - void send_chat(int cid, int team, const char *text); - void send_emoticon(int cid, int emoticon); - void send_weapon_pickup(int cid, int weapon); - void send_broadcast(const char *text, int cid); - -}; - -extern GAMECONTEXT game; - -// MISC stuff, move to a better place later on - -extern TUNING_PARAMS tuning; -inline int cmask_all() { return -1; } -inline int cmask_one(int cid) { return 1<<cid; } -inline int cmask_all_except_one(int cid) { return 0x7fffffff^cmask_one(cid); } -inline bool cmask_is_set(int mask, int cid) { return (mask&cmask_one(cid)) != 0; } -#endif diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index cdbace8e..a822f7ca 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -1,607 +1,614 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> -#include <engine/e_config.h> -#include <engine/e_server_interface.h> -#include <game/mapitems.hpp> +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <engine/shared/config.h> +#include <game/mapitems.h> -#include <game/generated/g_protocol.hpp> +#include <game/generated/protocol.h> -#include "entities/pickup.hpp" -#include "gamecontroller.hpp" -#include "gamecontext.hpp" +#include "entities/pickup.h" +#include "gamecontroller.h" +#include "gamecontext.h" - -GAMECONTROLLER::GAMECONTROLLER() +IGameController::IGameController(class CGameContext *pGameServer) { - gametype = "unknown"; + m_pGameServer = pGameServer; + m_pServer = m_pGameServer->Server(); + m_pGameType = "unknown"; // - do_warmup(config.sv_warmup); - game_over_tick = -1; - sudden_death = 0; - round_start_tick = server_tick(); - round_count = 0; - game_flags = 0; - teamscore[0] = 0; - teamscore[1] = 0; - map_wish[0] = 0; - - unbalanced_tick = -1; - force_balanced = false; - - num_spawn_points[0] = 0; - num_spawn_points[1] = 0; - num_spawn_points[2] = 0; + DoWarmup(g_Config.m_SvWarmup); + m_GameOverTick = -1; + m_SuddenDeath = 0; + m_RoundStartTick = Server()->Tick(); + m_RoundCount = 0; + m_GameFlags = 0; + m_aTeamscore[0] = 0; + m_aTeamscore[1] = 0; + m_aMapWish[0] = 0; + + m_UnbalancedTick = -1; + m_ForceBalanced = false; + + m_aNumSpawnPoints[0] = 0; + m_aNumSpawnPoints[1] = 0; + m_aNumSpawnPoints[2] = 0; } -GAMECONTROLLER::~GAMECONTROLLER() +IGameController::~IGameController() { } -float GAMECONTROLLER::evaluate_spawn_pos(SPAWNEVAL *eval, vec2 pos) +float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos) { - float score = 0.0f; - CHARACTER *c = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); - for(; c; c = (CHARACTER *)c->typenext()) + float Score = 0.0f; + CCharacter *pC = static_cast<CCharacter *>(GameServer()->m_World.FindFirst(NETOBJTYPE_CHARACTER)); + for(; pC; pC = (CCharacter *)pC->TypeNext()) { // team mates are not as dangerous as enemies - float scoremod = 1.0f; - if(eval->friendly_team != -1 && c->team == eval->friendly_team) - scoremod = 0.5f; + float Scoremod = 1.0f; + if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam) + Scoremod = 0.5f; - float d = distance(pos, c->pos); + float d = distance(Pos, pC->m_Pos); if(d == 0) - score += 1000000000.0f; + Score += 1000000000.0f; else - score += 1.0f/d; + Score += 1.0f/d; } - return score; + return Score; } -void GAMECONTROLLER::evaluate_spawn_type(SPAWNEVAL *eval, int t) +void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int T) { // get spawn point - for(int i = 0; i < num_spawn_points[t]; i++) + for(int i = 0; i < m_aNumSpawnPoints[T]; i++) { - vec2 p = spawn_points[t][i]; - float s = evaluate_spawn_pos(eval, p); - if(!eval->got || eval->score > s) + vec2 P = m_aaSpawnPoints[T][i]; + float S = EvaluateSpawnPos(pEval, P); + if(!pEval->m_Got || pEval->m_Score > S) { - eval->got = true; - eval->score = s; - eval->pos = p; + pEval->m_Got = true; + pEval->m_Score = S; + pEval->m_Pos = P; } } } -bool GAMECONTROLLER::can_spawn(PLAYER *player, vec2 *out_pos) +bool IGameController::CanSpawn(CPlayer *pPlayer, vec2 *pOutPos) { - SPAWNEVAL eval; + CSpawnEval Eval; // spectators can't spawn - if(player->team == -1) + if(pPlayer->GetTeam() == -1) return false; - if(is_teamplay()) + if(IsTeamplay()) { - eval.friendly_team = player->team; + Eval.m_FriendlyTeam = pPlayer->GetTeam(); // try first try own team spawn, then normal spawn and then enemy - evaluate_spawn_type(&eval, 1+(player->team&1)); - if(!eval.got) + EvaluateSpawnType(&Eval, 1+(pPlayer->GetTeam()&1)); + if(!Eval.m_Got) { - evaluate_spawn_type(&eval, 0); - if(!eval.got) - evaluate_spawn_type(&eval, 1+((player->team+1)&1)); + EvaluateSpawnType(&Eval, 0); + if(!Eval.m_Got) + EvaluateSpawnType(&Eval, 1+((pPlayer->GetTeam()+1)&1)); } } else { - evaluate_spawn_type(&eval, 0); - evaluate_spawn_type(&eval, 1); - evaluate_spawn_type(&eval, 2); + EvaluateSpawnType(&Eval, 0); + EvaluateSpawnType(&Eval, 1); + EvaluateSpawnType(&Eval, 2); } - *out_pos = eval.pos; - return eval.got; + *pOutPos = Eval.m_Pos; + return Eval.m_Got; } -bool GAMECONTROLLER::on_entity(int index, vec2 pos) +bool IGameController::OnEntity(int Index, vec2 Pos) { - int type = -1; - int subtype = 0; + int Type = -1; + int SubType = 0; - if(index == ENTITY_SPAWN) - spawn_points[0][num_spawn_points[0]++] = pos; - else if(index == ENTITY_SPAWN_RED) - spawn_points[1][num_spawn_points[1]++] = pos; - else if(index == ENTITY_SPAWN_BLUE) - spawn_points[2][num_spawn_points[2]++] = pos; - else if(index == ENTITY_ARMOR_1) - type = POWERUP_ARMOR; - else if(index == ENTITY_HEALTH_1) - type = POWERUP_HEALTH; - else if(index == ENTITY_WEAPON_SHOTGUN) + if(Index == ENTITY_SPAWN) + m_aaSpawnPoints[0][m_aNumSpawnPoints[0]++] = Pos; + else if(Index == ENTITY_SPAWN_RED) + m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos; + else if(Index == ENTITY_SPAWN_BLUE) + m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos; + else if(Index == ENTITY_ARMOR_1) + Type = POWERUP_ARMOR; + else if(Index == ENTITY_HEALTH_1) + Type = POWERUP_HEALTH; + else if(Index == ENTITY_WEAPON_SHOTGUN) { - type = POWERUP_WEAPON; - subtype = WEAPON_SHOTGUN; + Type = POWERUP_WEAPON; + SubType = WEAPON_SHOTGUN; } - else if(index == ENTITY_WEAPON_GRENADE) + else if(Index == ENTITY_WEAPON_GRENADE) { - type = POWERUP_WEAPON; - subtype = WEAPON_GRENADE; + Type = POWERUP_WEAPON; + SubType = WEAPON_GRENADE; } - else if(index == ENTITY_WEAPON_RIFLE) + else if(Index == ENTITY_WEAPON_RIFLE) { - type = POWERUP_WEAPON; - subtype = WEAPON_RIFLE; + Type = POWERUP_WEAPON; + SubType = WEAPON_RIFLE; } - else if(index == ENTITY_POWERUP_NINJA && config.sv_powerups) + else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups) { - type = POWERUP_NINJA; - subtype = WEAPON_NINJA; + Type = POWERUP_NINJA; + SubType = WEAPON_NINJA; } - if(type != -1) + if(Type != -1) { - PICKUP *pickup = new PICKUP(type, subtype); - pickup->pos = pos; + CPickup *pPickup = new CPickup(&GameServer()->m_World, Type, SubType); + pPickup->m_Pos = Pos; return true; } return false; } -void GAMECONTROLLER::endround() +void IGameController::EndRound() { - if(warmup) // game can't end when we are running warmup + if(m_Warmup) // game can't end when we are running warmup return; - game.world.paused = true; - game_over_tick = server_tick(); - sudden_death = 0; + GameServer()->m_World.m_Paused = true; + m_GameOverTick = Server()->Tick(); + m_SuddenDeath = 0; } -void GAMECONTROLLER::resetgame() +void IGameController::ResetGame() { - game.world.reset_requested = true; + GameServer()->m_World.m_ResetRequested = true; } -const char *GAMECONTROLLER::get_team_name(int team) +const char *IGameController::GetTeamName(int Team) { - if(is_teamplay()) + if(IsTeamplay()) { - if(team == 0) + if(Team == 0) return "red team"; - else if(team == 1) + else if(Team == 1) return "blue team"; } else { - if(team == 0) + if(Team == 0) return "game"; } return "spectators"; } -static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } +static bool IsSeparator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } -void GAMECONTROLLER::startround() +void IGameController::StartRound() { - resetgame(); - - round_start_tick = server_tick(); - sudden_death = 0; - game_over_tick = -1; - game.world.paused = false; - teamscore[0] = 0; - teamscore[1] = 0; - unbalanced_tick = -1; - force_balanced = false; - dbg_msg("game","start round type='%s' teamplay='%d'", gametype, game_flags&GAMEFLAG_TEAMS); + ResetGame(); + + m_RoundStartTick = Server()->Tick(); + m_SuddenDeath = 0; + m_GameOverTick = -1; + GameServer()->m_World.m_Paused = false; + m_aTeamscore[0] = 0; + m_aTeamscore[1] = 0; + m_ForceBalanced = false; + dbg_msg("game","start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS); } -void GAMECONTROLLER::change_map(const char *to_map) +void IGameController::ChangeMap(const char *pToMap) { - str_copy(map_wish, to_map, sizeof(map_wish)); - endround(); + str_copy(m_aMapWish, pToMap, sizeof(m_aMapWish)); + EndRound(); } -void GAMECONTROLLER::cyclemap() +void IGameController::CycleMap() { - if(map_wish[0] != 0) + if(m_aMapWish[0] != 0) { - dbg_msg("game", "rotating map to %s", map_wish); - str_copy(config.sv_map, map_wish, sizeof(config.sv_map)); - map_wish[0] = 0; - round_count = 0; + dbg_msg("game", "rotating map to %s", m_aMapWish); + str_copy(g_Config.m_SvMap, m_aMapWish, sizeof(g_Config.m_SvMap)); + m_aMapWish[0] = 0; + m_RoundCount = 0; return; } - if(!strlen(config.sv_maprotation)) + if(!str_length(g_Config.m_SvMaprotation)) return; - if(round_count < config.sv_rounds_per_map-1) + if(m_RoundCount < g_Config.m_SvRoundsPerMap-1) return; // handle maprotation - const char *map_rotation = config.sv_maprotation; - const char *current_map = config.sv_map; + const char *pMapRotation = g_Config.m_SvMaprotation; + const char *pCurrentMap = g_Config.m_SvMap; - int current_map_len = strlen(current_map); - const char *next_map = map_rotation; - while(*next_map) + int CurrentMapLen = str_length(pCurrentMap); + const char *pNextMap = pMapRotation; + while(*pNextMap) { - int wordlen = 0; - while(next_map[wordlen] && !is_separator(next_map[wordlen])) - wordlen++; + int WordLen = 0; + while(pNextMap[WordLen] && !IsSeparator(pNextMap[WordLen])) + WordLen++; - if(wordlen == current_map_len && strncmp(next_map, current_map, current_map_len) == 0) + if(WordLen == CurrentMapLen && str_comp_num(pNextMap, pCurrentMap, CurrentMapLen) == 0) { // map found - next_map += current_map_len; - while(*next_map && is_separator(*next_map)) - next_map++; + pNextMap += CurrentMapLen; + while(*pNextMap && IsSeparator(*pNextMap)) + pNextMap++; break; } - next_map++; + pNextMap++; } // restart rotation - if(next_map[0] == 0) - next_map = map_rotation; + if(pNextMap[0] == 0) + pNextMap = pMapRotation; // cut out the next map - char buf[512]; + char Buf[512]; for(int i = 0; i < 512; i++) { - buf[i] = next_map[i]; - if(is_separator(next_map[i]) || next_map[i] == 0) + Buf[i] = pNextMap[i]; + if(IsSeparator(pNextMap[i]) || pNextMap[i] == 0) { - buf[i] = 0; + Buf[i] = 0; break; } } // skip spaces int i = 0; - while(is_separator(buf[i])) + while(IsSeparator(Buf[i])) i++; - round_count = 0; + m_RoundCount = 0; - dbg_msg("game", "rotating map to %s", &buf[i]); - str_copy(config.sv_map, &buf[i], sizeof(config.sv_map)); + dbg_msg("game", "rotating map to %s", &Buf[i]); + str_copy(g_Config.m_SvMap, &Buf[i], sizeof(g_Config.m_SvMap)); } -void GAMECONTROLLER::post_reset() +void IGameController::PostReset() { for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i]) + if(GameServer()->m_apPlayers[i]) { - game.players[i]->respawn(); - game.players[i]->score = 0; + GameServer()->m_apPlayers[i]->Respawn(); + GameServer()->m_apPlayers[i]->m_Score = 0; } } } -void GAMECONTROLLER::on_player_info_change(class PLAYER *p) +void IGameController::OnPlayerInfoChange(class CPlayer *pP) { - const int team_colors[2] = {65387, 10223467}; - if(is_teamplay()) + const int aTeamColors[2] = {65387, 10223467}; + if(IsTeamplay()) { - if(p->team >= 0 || p->team <= 1) + if(pP->GetTeam() >= 0 || pP->GetTeam() <= 1) { - p->use_custom_color = 1; - p->color_body = team_colors[p->team]; - p->color_feet = team_colors[p->team]; + pP->m_TeeInfos.m_UseCustomColor = 1; + pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetTeam()]; + pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetTeam()]; } } } -int GAMECONTROLLER::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon) +int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon) { // do scoreing - if(!killer) + if(!pKiller) return 0; - if(killer == victim->player) - victim->player->score--; // suicide + if(pKiller == pVictim->GetPlayer()) + pVictim->GetPlayer()->m_Score--; // suicide else { - if(is_teamplay() && victim->team == killer->team) - killer->score--; // teamkill + if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam()) + pKiller->m_Score--; // teamkill else - killer->score++; // normal kill + pKiller->m_Score++; // normal kill } return 0; } -void GAMECONTROLLER::on_character_spawn(class CHARACTER *chr) +void IGameController::OnCharacterSpawn(class CCharacter *pChr) { // default health - chr->health = 10; + pChr->IncreaseHealth(10); // give default weapons - chr->weapons[WEAPON_HAMMER].got = 1; - chr->weapons[WEAPON_HAMMER].ammo = -1; - chr->weapons[WEAPON_GUN].got = 1; - chr->weapons[WEAPON_GUN].ammo = 10; + pChr->GiveWeapon(WEAPON_HAMMER, -1); + pChr->GiveWeapon(WEAPON_GUN, 10); } -void GAMECONTROLLER::do_warmup(int seconds) +void IGameController::DoWarmup(int Seconds) { - warmup = seconds*server_tickspeed(); + if(Seconds < 0) + m_Warmup = 0; + else + m_Warmup = Seconds*Server()->TickSpeed(); } -bool GAMECONTROLLER::is_friendly_fire(int cid1, int cid2) +bool IGameController::IsFriendlyFire(int Cid1, int Cid2) { - if(cid1 == cid2) + if(Cid1 == Cid2) return false; - if(is_teamplay()) + if(IsTeamplay()) { - if(!game.players[cid1] || !game.players[cid2]) + if(!GameServer()->m_apPlayers[Cid1] || !GameServer()->m_apPlayers[Cid2]) return false; - if(game.players[cid1]->team == game.players[cid2]->team) + if(GameServer()->m_apPlayers[Cid1]->GetTeam() == GameServer()->m_apPlayers[Cid2]->GetTeam()) return true; } return false; } -bool GAMECONTROLLER::is_force_balanced() +bool IGameController::IsForceBalanced() { - if(force_balanced) + if(m_ForceBalanced) { - force_balanced = false; + m_ForceBalanced = false; return true; } else return false; } -void GAMECONTROLLER::tick() +bool IGameController::CanBeMovedOnBalance(int Cid) +{ + return true; +} + +void IGameController::Tick() { // do warmup - if(warmup) + if(m_Warmup) { - warmup--; - if(!warmup) - startround(); + m_Warmup--; + if(!m_Warmup) + StartRound(); } - if(game_over_tick != -1) + if(m_GameOverTick != -1) { // game over.. wait for restart - if(server_tick() > game_over_tick+server_tickspeed()*10) + if(Server()->Tick() > m_GameOverTick+Server()->TickSpeed()*10) { - cyclemap(); - startround(); - round_count++; + CycleMap(); + StartRound(); + m_RoundCount++; } } // do team-balancing - if (is_teamplay() && unbalanced_tick != -1 && server_tick() > unbalanced_tick+config.sv_teambalance_time*server_tickspeed()*60) + if (IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) { dbg_msg("game", "Balancing teams"); - int t[2] = {0,0}; - int tscore[2] = {0,0}; + int aT[2] = {0,0}; + int aTScore[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && game.players[i]->team != -1) + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != -1) { - t[game.players[i]->team]++; - tscore[game.players[i]->team]+=game.players[i]->score; + aT[GameServer()->m_apPlayers[i]->GetTeam()]++; + aTScore[GameServer()->m_apPlayers[i]->GetTeam()] += GameServer()->m_apPlayers[i]->m_Score; } } // are teams unbalanced? - if(abs(t[0]-t[1]) >= 2) + if(absolute(aT[0]-aT[1]) >= 2) { - int m = (t[0] > t[1]) ? 0 : 1; - int num_balance = abs(t[0]-t[1]) / 2; + int M = (aT[0] > aT[1]) ? 0 : 1; + int NumBalance = absolute(aT[0]-aT[1]) / 2; do { - PLAYER *p = 0; - int pd = tscore[m]; + CPlayer *pP = 0; + int PD = aTScore[M]; for(int i = 0; i < MAX_CLIENTS; i++) { - if(!game.players[i]) + if(!GameServer()->m_apPlayers[i]) + continue; + if(!CanBeMovedOnBalance(i)) continue; - // remember the player who would cause lowest score-difference - if(game.players[i]->team == m && (!p || abs((tscore[m^1]+game.players[i]->score) - (tscore[m]-game.players[i]->score)) < pd)) + if(GameServer()->m_apPlayers[i]->GetTeam() == M && (!pP || absolute((aTScore[M^1]+GameServer()->m_apPlayers[i]->m_Score) - (aTScore[M]-GameServer()->m_apPlayers[i]->m_Score)) < PD)) { - p = game.players[i]; - pd = abs((tscore[m^1]+p->score) - (tscore[m]-p->score)); + pP = GameServer()->m_apPlayers[i]; + PD = absolute((aTScore[M^1]+pP->m_Score) - (aTScore[M]-pP->m_Score)); } } // move the player to other team without losing his score // TODO: change in player::set_team needed: player won't lose score on team-change - int score_before = p->score; - p->set_team(m^1); - p->score = score_before; + int ScoreBefore = pP->m_Score; + pP->SetTeam(M^1); + pP->m_Score = ScoreBefore; - p->respawn(); - p->force_balanced = true; - } while (--num_balance); + pP->Respawn(); + pP->m_ForceBalanced = true; + } while (--NumBalance); - force_balanced = true; + m_ForceBalanced = true; } - unbalanced_tick = -1; + m_UnbalancedTick = -1; } // update browse info - int prog = -1; - if(config.sv_timelimit > 0) - prog = max(prog, (server_tick()-round_start_tick) * 100 / (config.sv_timelimit*server_tickspeed()*60)); + int Prog = -1; + if(g_Config.m_SvTimelimit > 0) + Prog = max(Prog, (Server()->Tick()-m_RoundStartTick) * 100 / (g_Config.m_SvTimelimit*Server()->TickSpeed()*60)); - if(config.sv_scorelimit) + if(g_Config.m_SvScorelimit) { - if(is_teamplay()) + if(IsTeamplay()) { - prog = max(prog, (teamscore[0]*100)/config.sv_scorelimit); - prog = max(prog, (teamscore[1]*100)/config.sv_scorelimit); + Prog = max(Prog, (m_aTeamscore[0]*100)/g_Config.m_SvScorelimit); + Prog = max(Prog, (m_aTeamscore[1]*100)/g_Config.m_SvScorelimit); } else { for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i]) - prog = max(prog, (game.players[i]->score*100)/config.sv_scorelimit); + if(GameServer()->m_apPlayers[i]) + Prog = max(Prog, (GameServer()->m_apPlayers[i]->m_Score*100)/g_Config.m_SvScorelimit); } } } - if(warmup) - prog = -1; + if(m_Warmup) + Prog = -1; - server_setbrowseinfo(gametype, prog); + Server()->SetBrowseInfo(m_pGameType, Prog); } -bool GAMECONTROLLER::is_teamplay() const +bool IGameController::IsTeamplay() const { - return game_flags&GAMEFLAG_TEAMS; + return m_GameFlags&GAMEFLAG_TEAMS; } -void GAMECONTROLLER::snap(int snapping_client) +void IGameController::Snap(int SnappingClient) { - NETOBJ_GAME *gameobj = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME)); - gameobj->paused = game.world.paused; - gameobj->game_over = game_over_tick==-1?0:1; - gameobj->sudden_death = sudden_death; + CNetObj_Game *pGameObj = (CNetObj_Game *)Server()->SnapNewItem(NETOBJTYPE_GAME, 0, sizeof(CNetObj_Game)); + pGameObj->m_Paused = GameServer()->m_World.m_Paused; + pGameObj->m_GameOver = m_GameOverTick==-1?0:1; + pGameObj->m_SuddenDeath = m_SuddenDeath; - gameobj->score_limit = config.sv_scorelimit; - gameobj->time_limit = config.sv_timelimit; - gameobj->round_start_tick = round_start_tick; - gameobj->flags = game_flags; + pGameObj->m_ScoreLimit = g_Config.m_SvScorelimit; + pGameObj->m_TimeLimit = g_Config.m_SvTimelimit; + pGameObj->m_RoundStartTick = m_RoundStartTick; + pGameObj->m_Flags = m_GameFlags; - gameobj->warmup = warmup; + pGameObj->m_Warmup = m_Warmup; - gameobj->round_num = (strlen(config.sv_maprotation) && config.sv_rounds_per_map) ? config.sv_rounds_per_map : 0; - gameobj->round_current = round_count+1; + pGameObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0; + pGameObj->m_RoundCurrent = m_RoundCount+1; - if(snapping_client == -1) + if(SnappingClient == -1) { // we are recording a demo, just set the scores - gameobj->teamscore_red = teamscore[0]; - gameobj->teamscore_blue = teamscore[1]; + pGameObj->m_TeamscoreRed = m_aTeamscore[0]; + pGameObj->m_TeamscoreBlue = m_aTeamscore[1]; } else { // TODO: this little hack should be removed - gameobj->teamscore_red = is_teamplay() ? teamscore[0] : game.players[snapping_client]->score; - gameobj->teamscore_blue = teamscore[1]; + pGameObj->m_TeamscoreRed = IsTeamplay() ? m_aTeamscore[0] : GameServer()->m_apPlayers[SnappingClient]->m_Score; + pGameObj->m_TeamscoreBlue = m_aTeamscore[1]; } } -int GAMECONTROLLER::get_auto_team(int notthisid) +int IGameController::GetAutoTeam(int Notthisid) { // this will force the auto balancer to work overtime aswell - if(config.dbg_stress) + if(g_Config.m_DbgStress) return 0; - int numplayers[2] = {0,0}; + int aNumplayers[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && i != notthisid) + if(GameServer()->m_apPlayers[i] && i != Notthisid) { - if(game.players[i]->team == 0 || game.players[i]->team == 1) - numplayers[game.players[i]->team]++; + if(GameServer()->m_apPlayers[i]->GetTeam() == 0 || GameServer()->m_apPlayers[i]->GetTeam() == 1) + aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++; } } - int team = 0; - if(is_teamplay()) - team = numplayers[0] > numplayers[1] ? 1 : 0; + int Team = 0; + if(IsTeamplay()) + Team = aNumplayers[0] > aNumplayers[1] ? 1 : 0; - if(can_join_team(team, notthisid)) - return team; + if(CanJoinTeam(Team, Notthisid)) + return Team; return -1; } -bool GAMECONTROLLER::can_join_team(int team, int notthisid) +bool IGameController::CanJoinTeam(int Team, int Notthisid) { - (void)team; - int numplayers[2] = {0,0}; + if(Team == -1) + return true; + + int aNumplayers[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && i != notthisid) + if(GameServer()->m_apPlayers[i] && i != Notthisid) { - if(game.players[i]->team >= 0 || game.players[i]->team == 1) - numplayers[game.players[i]->team]++; + if(GameServer()->m_apPlayers[i]->GetTeam() >= 0 || GameServer()->m_apPlayers[i]->GetTeam() == 1) + aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++; } } - return (numplayers[0] + numplayers[1]) < config.sv_max_clients-config.sv_spectator_slots; + return (aNumplayers[0] + aNumplayers[1]) < g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots; } -bool GAMECONTROLLER::check_team_balance() +bool IGameController::CheckTeamBalance() { - if(!is_teamplay() || !config.sv_teambalance_time) + if(!IsTeamplay() || !g_Config.m_SvTeambalanceTime) return true; - int t[2] = {0, 0}; + int aT[2] = {0, 0}; for(int i = 0; i < MAX_CLIENTS; i++) { - PLAYER *p = game.players[i]; - if(p && p->team != -1) - t[p->team]++; + CPlayer *pP = GameServer()->m_apPlayers[i]; + if(pP && pP->GetTeam() != -1) + aT[pP->GetTeam()]++; } - if(abs(t[0]-t[1]) >= 2) + if(absolute(aT[0]-aT[1]) >= 2) { - dbg_msg("game", "Team is NOT balanced (red=%d blue=%d)", t[0], t[1]); - if (game.controller->unbalanced_tick == -1) - game.controller->unbalanced_tick = server_tick(); + dbg_msg("game", "Team is NOT balanced (red=%d blue=%d)", aT[0], aT[1]); + if(GameServer()->m_pController->m_UnbalancedTick == -1) + GameServer()->m_pController->m_UnbalancedTick = Server()->Tick(); return false; } else { - dbg_msg("game", "Team is balanced (red=%d blue=%d)", t[0], t[1]); - game.controller->unbalanced_tick = -1; + dbg_msg("game", "Team is balanced (red=%d blue=%d)", aT[0], aT[1]); + GameServer()->m_pController->m_UnbalancedTick = -1; return true; } } -bool GAMECONTROLLER::can_change_team(PLAYER *pplayer, int jointeam) +bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) { - int t[2] = {0, 0}; + int aT[2] = {0, 0}; - if (!is_teamplay() || jointeam == -1 || !config.sv_teambalance_time) + if (!IsTeamplay() || JoinTeam == -1 || !g_Config.m_SvTeambalanceTime) return true; for(int i = 0; i < MAX_CLIENTS; i++) { - PLAYER *p = game.players[i]; - if(p && p->team != -1) - t[p->team]++; + CPlayer *pP = GameServer()->m_apPlayers[i]; + if(pP && pP->GetTeam() != -1) + aT[pP->GetTeam()]++; } // simulate what would happen if changed team - t[jointeam]++; - if (pplayer->team != -1) - t[jointeam^1]--; + aT[JoinTeam]++; + if (pPlayer->GetTeam() != -1) + aT[JoinTeam^1]--; // there is a player-difference of at least 2 - if(abs(t[0]-t[1]) >= 2) + if(absolute(aT[0]-aT[1]) >= 2) { // player wants to join team with less players - if ((t[0] < t[1] && jointeam == 0) || (t[0] > t[1] && jointeam == 1)) + if ((aT[0] < aT[1] && JoinTeam == 0) || (aT[0] > aT[1] && JoinTeam == 1)) return true; else return false; @@ -610,62 +617,60 @@ bool GAMECONTROLLER::can_change_team(PLAYER *pplayer, int jointeam) return true; } -void GAMECONTROLLER::do_player_score_wincheck() +void IGameController::DoPlayerScoreWincheck() { - if(game_over_tick == -1 && !warmup) + if(m_GameOverTick == -1 && !m_Warmup) { // gather some stats - int topscore = 0; - int topscore_count = 0; + int Topscore = 0; + int TopscoreCount = 0; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i]) + if(GameServer()->m_apPlayers[i]) { - if(game.players[i]->score > topscore) + if(GameServer()->m_apPlayers[i]->m_Score > Topscore) { - topscore = game.players[i]->score; - topscore_count = 1; + Topscore = GameServer()->m_apPlayers[i]->m_Score; + TopscoreCount = 1; } - else if(game.players[i]->score == topscore) - topscore_count++; + else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) + TopscoreCount++; } } // check score win condition - if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) || - (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) + if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) { - if(topscore_count == 1) - endround(); + if(TopscoreCount == 1) + EndRound(); else - sudden_death = 1; + m_SuddenDeath = 1; } } } -void GAMECONTROLLER::do_team_score_wincheck() +void IGameController::DoTeamScoreWincheck() { - if(game_over_tick == -1 && !warmup) + if(m_GameOverTick == -1 && !m_Warmup) { // check score win condition - if((config.sv_scorelimit > 0 && (teamscore[0] >= config.sv_scorelimit || teamscore[1] >= config.sv_scorelimit)) || - (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[0] >= g_Config.m_SvScorelimit || m_aTeamscore[1] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) { - if(teamscore[0] != teamscore[1]) - endround(); + if(m_aTeamscore[0] != m_aTeamscore[1]) + EndRound(); else - sudden_death = 1; + m_SuddenDeath = 1; } } } -int GAMECONTROLLER::clampteam(int team) +int IGameController::ClampTeam(int Team) { - if(team < 0) // spectator + if(Team < 0) // spectator return -1; - if(is_teamplay()) - return team&1; + if(IsTeamplay()) + return Team&1; return 0; } - -GAMECONTROLLER *gamecontroller = 0; diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h new file mode 100644 index 00000000..0624dcab --- /dev/null +++ b/src/game/server/gamecontroller.h @@ -0,0 +1,145 @@ +#ifndef GAME_SERVER_GAMECONTROLLER_H +#define GAME_SERVER_GAMECONTROLLER_H + +#include <base/vmath.h> + +/* + Class: Game Controller + Controls the main game logic. Keeping track of team and player score, + winning conditions and specific game logic. +*/ +class IGameController +{ + vec2 m_aaSpawnPoints[3][64]; + int m_aNumSpawnPoints[3]; + + class CGameContext *m_pGameServer; + class IServer *m_pServer; + +protected: + CGameContext *GameServer() const { return m_pGameServer; } + IServer *Server() const { return m_pServer; } + + struct CSpawnEval + { + CSpawnEval() + { + m_Got = false; + m_FriendlyTeam = -1; + m_Pos = vec2(100,100); + } + + vec2 m_Pos; + bool m_Got; + int m_FriendlyTeam; + float m_Score; + }; + + float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos); + void EvaluateSpawnType(CSpawnEval *pEval, int Type); + bool EvaluateSpawn(class CPlayer *pP, vec2 *pPos); + + void CycleMap(); + void ResetGame(); + + char m_aMapWish[128]; + + + int m_RoundStartTick; + int m_GameOverTick; + int m_SuddenDeath; + + int m_aTeamscore[2]; + + int m_Warmup; + int m_RoundCount; + + int m_GameFlags; + int m_UnbalancedTick; + bool m_ForceBalanced; + +public: + const char *m_pGameType; + + bool IsTeamplay() const; + + IGameController(class CGameContext *pGameServer); + virtual ~IGameController(); + + void DoTeamScoreWincheck(); + void DoPlayerScoreWincheck(); + + void DoWarmup(int Seconds); + + void StartRound(); + void EndRound(); + void ChangeMap(const char *pToMap); + + bool IsFriendlyFire(int Cid1, int Cid2); + + bool IsForceBalanced(); + + /* + + */ + virtual bool CanBeMovedOnBalance(int Cid); + + virtual void Tick(); + + virtual void Snap(int SnappingClient); + + /* + Function: on_entity + Called when the map is loaded to process an entity + in the map. + + Arguments: + index - Entity index. + pos - Where the entity is located in the world. + + Returns: + bool? + */ + virtual bool OnEntity(int Index, vec2 Pos); + + /* + Function: on_CCharacter_spawn + Called when a CCharacter spawns into the game world. + + Arguments: + chr - The CCharacter that was spawned. + */ + virtual void OnCharacterSpawn(class CCharacter *pChr); + + /* + Function: on_CCharacter_death + Called when a CCharacter in the world dies. + + Arguments: + victim - The CCharacter that died. + killer - The player that killed it. + weapon - What weapon that killed it. Can be -1 for undefined + weapon when switching team or player suicides. + */ + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + + + virtual void OnPlayerInfoChange(class CPlayer *pP); + + // + virtual bool CanSpawn(class CPlayer *pP, vec2 *pPos); + + /* + + */ + virtual const char *GetTeamName(int Team); + virtual int GetAutoTeam(int NotThisId); + virtual bool CanJoinTeam(int Team, int NotThisId); + bool CheckTeamBalance(); + bool CanChangeTeam(CPlayer *pPplayer, int JoinTeam); + int ClampTeam(int Team); + + virtual void PostReset(); +}; + +#endif diff --git a/src/game/server/gamecontroller.hpp b/src/game/server/gamecontroller.hpp deleted file mode 100644 index 26b8a81f..00000000 --- a/src/game/server/gamecontroller.hpp +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef GAME_SERVER_GAMECONTROLLER_H -#define GAME_SERVER_GAMECONTROLLER_H - -#include <base/vmath.hpp> - -/* - Class: Game Controller - Controls the main game logic. Keeping track of team and player score, - winning conditions and specific game logic. -*/ -class GAMECONTROLLER -{ - vec2 spawn_points[3][64]; - int num_spawn_points[3]; -protected: - struct SPAWNEVAL - { - SPAWNEVAL() - { - got = false; - friendly_team = -1; - pos = vec2(100,100); - } - - vec2 pos; - bool got; - int friendly_team; - float score; - }; - - float evaluate_spawn_pos(SPAWNEVAL *eval, vec2 pos); - void evaluate_spawn_type(SPAWNEVAL *eval, int type); - bool evaluate_spawn(class PLAYER *p, vec2 *pos); - - void cyclemap(); - void resetgame(); - - char map_wish[128]; - - - int round_start_tick; - int game_over_tick; - int sudden_death; - - int teamscore[2]; - - int warmup; - int round_count; - - int game_flags; - int unbalanced_tick; - bool force_balanced; - -public: - const char *gametype; - - bool is_teamplay() const; - - GAMECONTROLLER(); - virtual ~GAMECONTROLLER(); - - void do_team_score_wincheck(); - void do_player_score_wincheck(); - - void do_warmup(int seconds); - - void startround(); - void endround(); - void change_map(const char *to_map); - - bool is_friendly_fire(int cid1, int cid2); - - bool is_force_balanced(); - - /* - - */ - virtual void tick(); - - virtual void snap(int snapping_client); - - /* - Function: on_entity - Called when the map is loaded to process an entity - in the map. - - Arguments: - index - Entity index. - pos - Where the entity is located in the world. - - Returns: - bool? - */ - virtual bool on_entity(int index, vec2 pos); - - /* - Function: on_character_spawn - Called when a character spawns into the game world. - - Arguments: - chr - The character that was spawned. - */ - virtual void on_character_spawn(class CHARACTER *chr); - - /* - Function: on_character_death - Called when a character in the world dies. - - Arguments: - victim - The character that died. - killer - The player that killed it. - weapon - What weapon that killed it. Can be -1 for undefined - weapon when switching team or player suicides. - */ - virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); - - - virtual void on_player_info_change(class PLAYER *p); - - // - virtual bool can_spawn(class PLAYER *p, vec2 *pos); - - /* - - */ - virtual const char *get_team_name(int team); - virtual int get_auto_team(int notthisid); - virtual bool can_join_team(int team, int notthisid); - bool check_team_balance(); - bool can_change_team(PLAYER *pplayer, int jointeam); - int clampteam(int team); - - virtual void post_reset(); -}; - -#endif diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index b2146b51..3da88342 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -1,188 +1,205 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <engine/e_server_interface.h> -#include <game/mapitems.hpp> -#include <game/server/entities/character.hpp> -#include <game/server/player.hpp> -#include <game/server/gamecontext.hpp> -#include "ctf.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <game/mapitems.h> +#include <game/server/entities/character.h> +#include <game/server/player.h> +#include <game/server/gamecontext.h> +#include "ctf.h" -GAMECONTROLLER_CTF::GAMECONTROLLER_CTF() +CGameControllerCTF::CGameControllerCTF(class CGameContext *pGameServer) +: IGameController(pGameServer) { - flags[0] = 0; - flags[1] = 0; - gametype = "CTF"; - game_flags = GAMEFLAG_TEAMS|GAMEFLAG_FLAGS; + m_apFlags[0] = 0; + m_apFlags[1] = 0; + m_pGameType = "CTF"; + m_GameFlags = GAMEFLAG_TEAMS|GAMEFLAG_FLAGS; } -bool GAMECONTROLLER_CTF::on_entity(int index, vec2 pos) +bool CGameControllerCTF::OnEntity(int Index, vec2 Pos) { - if(GAMECONTROLLER::on_entity(index, pos)) + if(IGameController::OnEntity(Index, Pos)) return true; - int team = -1; - if(index == ENTITY_FLAGSTAND_RED) team = 0; - if(index == ENTITY_FLAGSTAND_BLUE) team = 1; - if(team == -1) + int Team = -1; + if(Index == ENTITY_FLAGSTAND_RED) Team = 0; + if(Index == ENTITY_FLAGSTAND_BLUE) Team = 1; + if(Team == -1) return false; - FLAG *f = new FLAG(team); - f->stand_pos = pos; - f->pos = pos; - flags[team] = f; + CFlag *F = new CFlag(&GameServer()->m_World, Team); + F->m_StandPos = Pos; + F->m_Pos = Pos; + m_apFlags[Team] = F; + GameServer()->m_World.InsertEntity(F); return true; } -int GAMECONTROLLER_CTF::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weaponid) +int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int WeaponID) { - GAMECONTROLLER::on_character_death(victim, killer, weaponid); - int had_flag = 0; + IGameController::OnCharacterDeath(pVictim, pKiller, WeaponID); + int HadFlag = 0; // drop flags - for(int fi = 0; fi < 2; fi++) + for(int i = 0; i < 2; i++) { - FLAG *f = flags[fi]; - if(f && killer && f->carrying_character == killer->get_character()) - had_flag |= 2; - if(f && f->carrying_character == victim) + CFlag *F = m_apFlags[i]; + if(F && pKiller && pKiller->GetCharacter() && F->m_pCarryingCCharacter == pKiller->GetCharacter()) + HadFlag |= 2; + if(F && F->m_pCarryingCCharacter == pVictim) { - game.create_sound_global(SOUND_CTF_DROP); - f->drop_tick = server_tick(); - f->carrying_character = 0; - f->vel = vec2(0,0); + GameServer()->CreateSoundGlobal(SOUND_CTF_DROP); + F->m_DropTick = Server()->Tick(); + F->m_pCarryingCCharacter = 0; + F->m_Vel = vec2(0,0); - if(killer && killer->team != victim->team) - killer->score++; + if(pKiller && pKiller->GetTeam() != pVictim->GetPlayer()->GetTeam()) + pKiller->m_Score++; - had_flag |= 1; + HadFlag |= 1; } } - return had_flag; + return HadFlag; +} + +bool CGameControllerCTF::CanBeMovedOnBalance(int Cid) +{ + CCharacter* Character = GameServer()->m_apPlayers[Cid]->GetCharacter(); + if(Character) + { + for(int fi = 0; fi < 2; fi++) + { + CFlag *F = m_apFlags[fi]; + if(F->m_pCarryingCCharacter == Character) + return false; + } + } + return true; } -void GAMECONTROLLER_CTF::tick() +void CGameControllerCTF::Tick() { - GAMECONTROLLER::tick(); + IGameController::Tick(); - do_team_score_wincheck(); + DoTeamScoreWincheck(); for(int fi = 0; fi < 2; fi++) { - FLAG *f = flags[fi]; + CFlag *F = m_apFlags[fi]; - if(!f) + if(!F) continue; // flag hits death-tile, reset it - if(col_get((int)f->pos.x, (int)f->pos.y)&COLFLAG_DEATH) + if(GameServer()->Collision()->GetCollisionAt(F->m_Pos.x, F->m_Pos.y)&CCollision::COLFLAG_DEATH) { - game.create_sound_global(SOUND_CTF_RETURN); - f->reset(); + GameServer()->CreateSoundGlobal(SOUND_CTF_RETURN); + F->Reset(); continue; } // - if(f->carrying_character) + if(F->m_pCarryingCCharacter) { // update flag position - f->pos = f->carrying_character->pos; + F->m_Pos = F->m_pCarryingCCharacter->m_Pos; - if(flags[fi^1] && flags[fi^1]->at_stand) + if(m_apFlags[fi^1] && m_apFlags[fi^1]->m_AtStand) { - if(distance(f->pos, flags[fi^1]->pos) < 32) + if(distance(F->m_Pos, m_apFlags[fi^1]->m_Pos) < 32) { // CAPTURE! \o/ - teamscore[fi^1] += 100; - f->carrying_character->player->score += 5; + m_aTeamscore[fi^1] += 100; + F->m_pCarryingCCharacter->GetPlayer()->m_Score += 5; dbg_msg("game", "flag_capture player='%d:%s'", - f->carrying_character->player->client_id, - server_clientname(f->carrying_character->player->client_id)); + F->m_pCarryingCCharacter->GetPlayer()->GetCID(), + Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID())); - char buf[512]; - float capture_time = (server_tick() - f->grab_tick)/(float)server_tickspeed(); - if(capture_time <= 60) + char Buf[512]; + float CaptureTime = (Server()->Tick() - F->m_GrabTick)/(float)Server()->TickSpeed(); + if(CaptureTime <= 60) { - str_format(buf, sizeof(buf), "the %s flag was captured by %s (%d.%s%d seconds)", fi ? "blue" : "red", server_clientname(f->carrying_character->player->client_id), (int)capture_time%60, ((int)(capture_time*100)%100)<10?"0":"", (int)(capture_time*100)%100); + str_format(Buf, sizeof(Buf), "The %s flag was captured by %s (%d.%s%d seconds)", fi ? "blue" : "red", Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID()), (int)CaptureTime%60, ((int)(CaptureTime*100)%100)<10?"0":"", (int)(CaptureTime*100)%100); } else { - str_format(buf, sizeof(buf), "the %s flag was captured by %s", fi ? "blue" : "red", server_clientname(f->carrying_character->player->client_id)); + str_format(Buf, sizeof(Buf), "The %s flag was captured by %s", fi ? "blue" : "red", Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID())); } - game.send_chat(-1, -2, buf); + GameServer()->SendChat(-1, -2, Buf); for(int i = 0; i < 2; i++) - flags[i]->reset(); + m_apFlags[i]->Reset(); - game.create_sound_global(SOUND_CTF_CAPTURE); + GameServer()->CreateSoundGlobal(SOUND_CTF_CAPTURE); } - } + } } else { - CHARACTER *close_characters[MAX_CLIENTS]; - int num = game.world.find_entities(f->pos, 32.0f, (ENTITY**)close_characters, MAX_CLIENTS, NETOBJTYPE_CHARACTER); - for(int i = 0; i < num; i++) + CCharacter *apCloseCCharacters[MAX_CLIENTS]; + int Num = GameServer()->m_World.FindEntities(F->m_Pos, 32.0f, (CEntity**)apCloseCCharacters, MAX_CLIENTS, NETOBJTYPE_CHARACTER); + for(int i = 0; i < Num; i++) { - if(!close_characters[i]->alive || close_characters[i]->player->team == -1 || col_intersect_line(f->pos, close_characters[i]->pos, NULL, NULL)) + if(!apCloseCCharacters[i]->IsAlive() || apCloseCCharacters[i]->GetPlayer()->GetTeam() == -1 || GameServer()->Collision()->IntersectLine(F->m_Pos, apCloseCCharacters[i]->m_Pos, NULL, NULL)) continue; - if(close_characters[i]->team == f->team) + if(apCloseCCharacters[i]->GetPlayer()->GetTeam() == F->m_Team) { // return the flag - if(!f->at_stand) + if(!F->m_AtStand) { - CHARACTER *chr = close_characters[i]; - chr->player->score += 1; + CCharacter *pChr = apCloseCCharacters[i]; + pChr->GetPlayer()->m_Score += 1; dbg_msg("game", "flag_return player='%d:%s'", - chr->player->client_id, - server_clientname(chr->player->client_id)); + pChr->GetPlayer()->GetCID(), + Server()->ClientName(pChr->GetPlayer()->GetCID())); - game.create_sound_global(SOUND_CTF_RETURN); - f->reset(); + GameServer()->CreateSoundGlobal(SOUND_CTF_RETURN); + F->Reset(); } } else { // take the flag - if(f->at_stand) + if(F->m_AtStand) { - teamscore[fi^1]++; - f->grab_tick = server_tick(); + m_aTeamscore[fi^1]++; + F->m_GrabTick = Server()->Tick(); } - f->at_stand = 0; - f->carrying_character = close_characters[i]; - f->carrying_character->player->score += 1; + + F->m_AtStand = 0; + F->m_pCarryingCCharacter = apCloseCCharacters[i]; + F->m_pCarryingCCharacter->GetPlayer()->m_Score += 1; dbg_msg("game", "flag_grab player='%d:%s'", - f->carrying_character->player->client_id, - server_clientname(f->carrying_character->player->client_id)); + F->m_pCarryingCCharacter->GetPlayer()->GetCID(), + Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID())); for(int c = 0; c < MAX_CLIENTS; c++) { - if(!game.players[c]) + if(!GameServer()->m_apPlayers[c]) continue; - if(game.players[c]->team == fi) - game.create_sound_global(SOUND_CTF_GRAB_EN, game.players[c]->client_id); + if(GameServer()->m_apPlayers[c]->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, GameServer()->m_apPlayers[c]->GetCID()); else - game.create_sound_global(SOUND_CTF_GRAB_PL, game.players[c]->client_id); + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, GameServer()->m_apPlayers[c]->GetCID()); } break; } } - if(!f->carrying_character && !f->at_stand) + if(!F->m_pCarryingCCharacter && !F->m_AtStand) { - if(server_tick() > f->drop_tick + server_tickspeed()*30) + if(Server()->Tick() > F->m_DropTick + Server()->TickSpeed()*30) { - game.create_sound_global(SOUND_CTF_RETURN); - f->reset(); + GameServer()->CreateSoundGlobal(SOUND_CTF_RETURN); + F->Reset(); } else { - f->vel.y += game.world.core.tuning.gravity; - move_box(&f->pos, &f->vel, vec2(f->phys_size, f->phys_size), 0.5f); + F->m_Vel.y += GameServer()->m_World.m_Core.m_Tuning.m_Gravity; + GameServer()->Collision()->MoveBox(&F->m_Pos, &F->m_Vel, vec2(F->m_PhysSize, F->m_PhysSize), 0.5f); } } } @@ -190,39 +207,36 @@ void GAMECONTROLLER_CTF::tick() } // Flag -FLAG::FLAG(int _team) -: ENTITY(NETOBJTYPE_FLAG) +CFlag::CFlag(CGameWorld *pGameWorld, int Team) +: CEntity(pGameWorld, NETOBJTYPE_FLAG) { - team = _team; - proximity_radius = phys_size; - carrying_character = 0x0; - grab_tick = 0; - - reset(); + m_Team = Team; + m_ProximityRadius = m_PhysSize; + m_pCarryingCCharacter = 0x0; + m_GrabTick = 0; - // TODO: should this be done here? - game.world.insert_entity(this); + Reset(); } -void FLAG::reset() +void CFlag::Reset() { - carrying_character = 0x0; - at_stand = 1; - pos = stand_pos; - vel = vec2(0,0); - grab_tick = 0; + m_pCarryingCCharacter = 0x0; + m_AtStand = 1; + m_Pos = m_StandPos; + m_Vel = vec2(0,0); + m_GrabTick = 0; } -void FLAG::snap(int snapping_client) +void CFlag::Snap(int SnappingClient) { - NETOBJ_FLAG *flag = (NETOBJ_FLAG *)snap_new_item(NETOBJTYPE_FLAG, team, sizeof(NETOBJ_FLAG)); - flag->x = (int)pos.x; - flag->y = (int)pos.y; - flag->team = team; - flag->carried_by = -1; + CNetObj_Flag *pFlag = (CNetObj_Flag *)Server()->SnapNewItem(NETOBJTYPE_FLAG, m_Team, sizeof(CNetObj_Flag)); + pFlag->m_X = (int)m_Pos.x; + pFlag->m_Y = (int)m_Pos.y; + pFlag->m_Team = m_Team; + pFlag->m_CarriedBy = -1; - if(at_stand) - flag->carried_by = -2; - else if(carrying_character && carrying_character->player) - flag->carried_by = carrying_character->player->client_id; + if(m_AtStand) + pFlag->m_CarriedBy = -2; + else if(m_pCarryingCCharacter && m_pCarryingCCharacter->GetPlayer()) + pFlag->m_CarriedBy = m_pCarryingCCharacter->GetPlayer()->GetCID(); } diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h new file mode 100644 index 00000000..b86ad688 --- /dev/null +++ b/src/game/server/gamemodes/ctf.h @@ -0,0 +1,38 @@ +#ifndef GAME_SERVER_GAMEMODES_CTF_H +#define GAME_SERVER_GAMEMODES_CTF_H +#include <game/server/gamecontroller.h> +#include <game/server/entity.h> + +class CGameControllerCTF : public IGameController +{ +public: + class CFlag *m_apFlags[2]; + + CGameControllerCTF(class CGameContext *pGameServer); + virtual bool CanBeMovedOnBalance(int Cid); + virtual void Tick(); + + virtual bool OnEntity(int Index, vec2 Pos); + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); +}; + +// TODO: move to seperate file +class CFlag : public CEntity +{ +public: + static const int m_PhysSize = 14; + CCharacter *m_pCarryingCCharacter; + vec2 m_Vel; + vec2 m_StandPos; + + int m_Team; + int m_AtStand; + int m_DropTick; + int m_GrabTick; + + CFlag(CGameWorld *pGameWorld, int Team); + + virtual void Reset(); + virtual void Snap(int SnappingClient); +}; +#endif diff --git a/src/game/server/gamemodes/ctf.hpp b/src/game/server/gamemodes/ctf.hpp deleted file mode 100644 index 67a098a4..00000000 --- a/src/game/server/gamemodes/ctf.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> -#include <game/server/entity.hpp> - -class GAMECONTROLLER_CTF : public GAMECONTROLLER -{ -public: - class FLAG *flags[2]; - - GAMECONTROLLER_CTF(); - virtual void tick(); - - virtual bool on_entity(int index, vec2 pos); - virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); -}; - -// TODO: move to seperate file -class FLAG : public ENTITY -{ -public: - static const int phys_size = 14; - CHARACTER *carrying_character; - vec2 vel; - vec2 stand_pos; - - int team; - int at_stand; - int drop_tick; - int grab_tick; - - FLAG(int _team); - - virtual void reset(); - virtual void snap(int snapping_client); -}; diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp index 15c0b987..173ef5fd 100644 --- a/src/game/server/gamemodes/dm.cpp +++ b/src/game/server/gamemodes/dm.cpp @@ -1,14 +1,15 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "dm.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include "dm.h" -GAMECONTROLLER_DM::GAMECONTROLLER_DM() +CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer) +: IGameController(pGameServer) { - gametype = "DM"; + m_pGameType = "DM"; } -void GAMECONTROLLER_DM::tick() +void CGameControllerDM::Tick() { - do_player_score_wincheck(); - GAMECONTROLLER::tick(); + DoPlayerScoreWincheck(); + IGameController::Tick(); } diff --git a/src/game/server/gamemodes/dm.h b/src/game/server/gamemodes/dm.h new file mode 100644 index 00000000..f2854680 --- /dev/null +++ b/src/game/server/gamemodes/dm.h @@ -0,0 +1,11 @@ +#ifndef GAME_SERVER_GAMEMODES_DM_H +#define GAME_SERVER_GAMEMODES_DM_H +#include <game/server/gamecontroller.h> + +class CGameControllerDM : public IGameController +{ +public: + CGameControllerDM(class CGameContext *pGameServer); + virtual void Tick(); +}; +#endif diff --git a/src/game/server/gamemodes/dm.hpp b/src/game/server/gamemodes/dm.hpp deleted file mode 100644 index 6fb25f61..00000000 --- a/src/game/server/gamemodes/dm.hpp +++ /dev/null @@ -1,10 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> - -class GAMECONTROLLER_DM : public GAMECONTROLLER -{ -public: - GAMECONTROLLER_DM(); - virtual void tick(); -}; diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp index 87b37411..44d0effc 100644 --- a/src/game/server/gamemodes/mod.cpp +++ b/src/game/server/gamemodes/mod.cpp @@ -1,20 +1,21 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "mod.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include "mod.h" -GAMECONTROLLER_MOD::GAMECONTROLLER_MOD() +CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer) +: IGameController(pGameServer) { // Exchange this to a string that identifies your game mode. // DM, TDM and CTF are reserved for teeworlds original modes. - gametype = "MOD"; + m_pGameType = "MOD"; - //game_flags = GAMEFLAG_TEAMS; // GAMEFLAG_TEAMS makes it a two-team gamemode + //m_GameFlags = GAMEFLAG_TEAMS; // GAMEFLAG_TEAMS makes it a two-team gamemode } -void GAMECONTROLLER_MOD::tick() +void CGameControllerMOD::Tick() { // this is the main part of the gamemode, this function is run every tick - do_player_score_wincheck(); // checks for winners, no teams version - //do_team_score_wincheck(); // checks for winners, two teams version + DoPlayerScoreWincheck(); // checks for winners, no teams version + //DoTeamScoreWincheck(); // checks for winners, two teams version - GAMECONTROLLER::tick(); + IGameController::Tick(); } diff --git a/src/game/server/gamemodes/mod.h b/src/game/server/gamemodes/mod.h new file mode 100644 index 00000000..86dff78b --- /dev/null +++ b/src/game/server/gamemodes/mod.h @@ -0,0 +1,14 @@ +#ifndef GAME_SERVER_GAMEMODES_MOD_H +#define GAME_SERVER_GAMEMODES_MOD_H +#include <game/server/gamecontroller.h> + +// you can subclass GAMECONTROLLER_CTF, GAMECONTROLLER_TDM etc if you want +// todo a modification with their base as well. +class CGameControllerMOD : public IGameController +{ +public: + CGameControllerMOD(class CGameContext *pGameServer); + virtual void Tick(); + // add more virtual functions here if you wish +}; +#endif diff --git a/src/game/server/gamemodes/mod.hpp b/src/game/server/gamemodes/mod.hpp deleted file mode 100644 index 9915a615..00000000 --- a/src/game/server/gamemodes/mod.hpp +++ /dev/null @@ -1,13 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> - -// you can subclass GAMECONTROLLER_CTF, GAMECONTROLLER_TDM etc if you want -// todo a modification with their base as well. -class GAMECONTROLLER_MOD : public GAMECONTROLLER -{ -public: - GAMECONTROLLER_MOD(); - virtual void tick(); - // add more virtual functions here if you wish -}; diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp index 72605000..b54e3ac0 100644 --- a/src/game/server/gamemodes/tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -1,34 +1,33 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <engine/e_server_interface.h> -#include <game/server/entities/character.hpp> -#include <game/server/player.hpp> -#include "tdm.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <game/server/entities/character.h> +#include <game/server/player.h> +#include "tdm.h" -GAMECONTROLLER_TDM::GAMECONTROLLER_TDM() +CGameControllerTDM::CGameControllerTDM(class CGameContext *pGameServer) : IGameController(pGameServer) { - gametype = "TDM"; - game_flags = GAMEFLAG_TEAMS; + m_pGameType = "TDM"; + m_GameFlags = GAMEFLAG_TEAMS; } -int GAMECONTROLLER_TDM::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon) +int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon) { - GAMECONTROLLER::on_character_death(victim, killer, weapon); + IGameController::OnCharacterDeath(pVictim, pKiller, Weapon); - if(weapon != WEAPON_GAME) + if(Weapon != WEAPON_GAME) { // do team scoring - if(killer == victim->player || killer->team == victim->player->team) - teamscore[killer->team&1]--; // klant arschel + if(pKiller == pVictim->GetPlayer() || pKiller->GetTeam() == pVictim->GetPlayer()->GetTeam()) + m_aTeamscore[pKiller->GetTeam()&1]--; // klant arschel else - teamscore[killer->team&1]++; // good shit + m_aTeamscore[pKiller->GetTeam()&1]++; // good shit } return 0; } -void GAMECONTROLLER_TDM::tick() +void CGameControllerTDM::Tick() { - do_team_score_wincheck(); - GAMECONTROLLER::tick(); + DoTeamScoreWincheck(); + IGameController::Tick(); } diff --git a/src/game/server/gamemodes/tdm.h b/src/game/server/gamemodes/tdm.h new file mode 100644 index 00000000..2d149456 --- /dev/null +++ b/src/game/server/gamemodes/tdm.h @@ -0,0 +1,13 @@ +#ifndef GAME_SERVER_GAMEMODES_TDM_H +#define GAME_SERVER_GAMEMODES_TDM_H +#include <game/server/gamecontroller.h> + +class CGameControllerTDM : public IGameController +{ +public: + CGameControllerTDM(class CGameContext *pGameServer); + + int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + virtual void Tick(); +}; +#endif diff --git a/src/game/server/gamemodes/tdm.hpp b/src/game/server/gamemodes/tdm.hpp deleted file mode 100644 index 51c47ca5..00000000 --- a/src/game/server/gamemodes/tdm.hpp +++ /dev/null @@ -1,12 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> - -class GAMECONTROLLER_TDM : public GAMECONTROLLER -{ -public: - GAMECONTROLLER_TDM(); - - int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); - virtual void tick(); -}; diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 9e76f14b..42c19487 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -1,217 +1,226 @@ -#include "gameworld.hpp" -#include "entity.hpp" -#include "gamecontext.hpp" +#include "gameworld.h" +#include "entity.h" +#include "gamecontext.h" ////////////////////////////////////////////////// // game world ////////////////////////////////////////////////// -GAMEWORLD::GAMEWORLD() +CGameWorld::CGameWorld() { - paused = false; - reset_requested = false; - first_entity = 0x0; + m_pGameServer = 0x0; + m_pServer = 0x0; + + m_Paused = false; + m_ResetRequested = false; + m_pFirstEntity = 0x0; for(int i = 0; i < NUM_ENT_TYPES; i++) - first_entity_types[i] = 0; + m_apFirstEntityTypes[i] = 0; } -GAMEWORLD::~GAMEWORLD() +CGameWorld::~CGameWorld() { // delete all entities - while(first_entity) - delete first_entity; + while(m_pFirstEntity) + delete m_pFirstEntity; +} + +void CGameWorld::SetGameServer(CGameContext *pGameServer) +{ + m_pGameServer = pGameServer; + m_pServer = m_pGameServer->Server(); } -ENTITY *GAMEWORLD::find_first(int type) +CEntity *CGameWorld::FindFirst(int Type) { - return first_entity_types[type]; + return m_apFirstEntityTypes[Type]; } -int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type) +int CGameWorld::FindEntities(vec2 Pos, float Radius, CEntity **ppEnts, int Max, int Type) { - int num = 0; - for(ENTITY *ent = (type<0) ? first_entity : first_entity_types[type]; - ent; ent = (type<0) ? ent->next_entity : ent->next_type_entity) + int Num = 0; + for(CEntity *pEnt = (Type<0) ? m_pFirstEntity : m_apFirstEntityTypes[Type]; + pEnt; pEnt = (Type<0) ? pEnt->m_pNextEntity : pEnt->m_pNextTypeEntity) { - if(distance(ent->pos, pos) < radius+ent->proximity_radius) + if(distance(pEnt->m_Pos, Pos) < Radius+pEnt->m_ProximityRadius) { - ents[num] = ent; - num++; - if(num == max) + ppEnts[Num] = pEnt; + Num++; + if(Num == Max) break; } } - return num; + return Num; } -void GAMEWORLD::insert_entity(ENTITY *ent) +void CGameWorld::InsertEntity(CEntity *pEnt) { - ENTITY *cur = first_entity; - while(cur) + CEntity *pCur = m_pFirstEntity; + while(pCur) { - dbg_assert(cur != ent, "err"); - cur = cur->next_entity; + dbg_assert(pCur != pEnt, "err"); + pCur = pCur->m_pNextEntity; } // insert it - if(first_entity) - first_entity->prev_entity = ent; - ent->next_entity = first_entity; - ent->prev_entity = 0x0; - first_entity = ent; + if(m_pFirstEntity) + m_pFirstEntity->m_pPrevEntity = pEnt; + pEnt->m_pNextEntity = m_pFirstEntity; + pEnt->m_pPrevEntity = 0x0; + m_pFirstEntity = pEnt; // into typelist aswell - if(first_entity_types[ent->objtype]) - first_entity_types[ent->objtype]->prev_type_entity = ent; - ent->next_type_entity = first_entity_types[ent->objtype]; - ent->prev_type_entity = 0x0; - first_entity_types[ent->objtype] = ent; + if(m_apFirstEntityTypes[pEnt->m_Objtype]) + m_apFirstEntityTypes[pEnt->m_Objtype]->m_pPrevTypeEntity = pEnt; + pEnt->m_pNextTypeEntity = m_apFirstEntityTypes[pEnt->m_Objtype]; + pEnt->m_pPrevTypeEntity = 0x0; + m_apFirstEntityTypes[pEnt->m_Objtype] = pEnt; } -void GAMEWORLD::destroy_entity(ENTITY *ent) +void CGameWorld::DestroyEntity(CEntity *pEnt) { - ent->marked_for_destroy = true; + pEnt->m_MarkedForDestroy = true; } -void GAMEWORLD::remove_entity(ENTITY *ent) +void CGameWorld::RemoveEntity(CEntity *pEnt) { // not in the list - if(!ent->next_entity && !ent->prev_entity && first_entity != ent) + if(!pEnt->m_pNextEntity && !pEnt->m_pPrevEntity && m_pFirstEntity != pEnt) return; // remove - if(ent->prev_entity) - ent->prev_entity->next_entity = ent->next_entity; + if(pEnt->m_pPrevEntity) + pEnt->m_pPrevEntity->m_pNextEntity = pEnt->m_pNextEntity; else - first_entity = ent->next_entity; - if(ent->next_entity) - ent->next_entity->prev_entity = ent->prev_entity; + m_pFirstEntity = pEnt->m_pNextEntity; + if(pEnt->m_pNextEntity) + pEnt->m_pNextEntity->m_pPrevEntity = pEnt->m_pPrevEntity; - if(ent->prev_type_entity) - ent->prev_type_entity->next_type_entity = ent->next_type_entity; + if(pEnt->m_pPrevTypeEntity) + pEnt->m_pPrevTypeEntity->m_pNextTypeEntity = pEnt->m_pNextTypeEntity; else - first_entity_types[ent->objtype] = ent->next_type_entity; - if(ent->next_type_entity) - ent->next_type_entity->prev_type_entity = ent->prev_type_entity; - - ent->next_entity = 0; - ent->prev_entity = 0; - ent->next_type_entity = 0; - ent->prev_type_entity = 0; + m_apFirstEntityTypes[pEnt->m_Objtype] = pEnt->m_pNextTypeEntity; + if(pEnt->m_pNextTypeEntity) + pEnt->m_pNextTypeEntity->m_pPrevTypeEntity = pEnt->m_pPrevTypeEntity; + + pEnt->m_pNextEntity = 0; + pEnt->m_pPrevEntity = 0; + pEnt->m_pNextTypeEntity = 0; + pEnt->m_pPrevTypeEntity = 0; } // -void GAMEWORLD::snap(int snapping_client) +void CGameWorld::Snap(int SnappingClient) { - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->snap(snapping_client); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->Snap(SnappingClient); } -void GAMEWORLD::reset() +void CGameWorld::Reset() { // reset all entities - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->reset(); - remove_entities(); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->Reset(); + RemoveEntities(); - game.controller->post_reset(); - remove_entities(); + GameServer()->m_pController->PostReset(); + RemoveEntities(); - reset_requested = false; + m_ResetRequested = false; } -void GAMEWORLD::remove_entities() +void CGameWorld::RemoveEntities() { // destroy objects marked for destruction - ENTITY *ent = first_entity; - while(ent) + CEntity *pEnt = m_pFirstEntity; + while(pEnt) { - ENTITY *next = ent->next_entity; - if(ent->marked_for_destroy) + CEntity *pNext = pEnt->m_pNextEntity; + if(pEnt->m_MarkedForDestroy) { - remove_entity(ent); - ent->destroy(); + RemoveEntity(pEnt); + pEnt->Destroy(); } - ent = next; + pEnt = pNext; } } -void GAMEWORLD::tick() +void CGameWorld::Tick() { - if(reset_requested) - reset(); + if(m_ResetRequested) + Reset(); - if(!paused) + if(!m_Paused) { - if(game.controller->is_force_balanced()) - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, "Teams have been balanced"); + if(GameServer()->m_pController->IsForceBalanced()) + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, "Teams have been balanced"); // update all objects - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->tick(); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->Tick(); - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->tick_defered(); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->TickDefered(); } - remove_entities(); + RemoveEntities(); } // TODO: should be more general -CHARACTER *GAMEWORLD::intersect_character(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTITY *notthis) +CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2& NewPos, CEntity *pNotThis) { // Find other players - float closest_len = distance(pos0, pos1) * 100.0f; - vec2 line_dir = normalize(pos1-pos0); - CHARACTER *closest = 0; + float ClosestLen = distance(Pos0, Pos1) * 100.0f; + vec2 LineDir = normalize(Pos1-Pos0); + CCharacter *pClosest = 0; - CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); - for(; p; p = (CHARACTER *)p->typenext()) + CCharacter *p = (CCharacter *)FindFirst(NETOBJTYPE_CHARACTER); + for(; p; p = (CCharacter *)p->TypeNext()) { - if(p == notthis) + if(p == pNotThis) continue; - vec2 intersect_pos = closest_point_on_line(pos0, pos1, p->pos); - float len = distance(p->pos, intersect_pos); - if(len < CHARACTER::phys_size+radius) + vec2 IntersectPos = closest_point_on_line(Pos0, Pos1, p->m_Pos); + float Len = distance(p->m_Pos, IntersectPos); + if(Len < p->m_ProximityRadius+Radius) { - if(len < closest_len) + if(Len < ClosestLen) { - new_pos = intersect_pos; - closest_len = len; - closest = p; + NewPos = IntersectPos; + ClosestLen = Len; + pClosest = p; } } } - return closest; + return pClosest; } -CHARACTER *GAMEWORLD::closest_character(vec2 pos, float radius, ENTITY *notthis) +CCharacter *CGameWorld::ClosestCharacter(vec2 Pos, float Radius, CEntity *pNotThis) { // Find other players - float closest_range = radius*2; - CHARACTER *closest = 0; + float ClosestRange = Radius*2; + CCharacter *pClosest = 0; - CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); - for(; p; p = (CHARACTER *)p->typenext()) + CCharacter *p = (CCharacter *)GameServer()->m_World.FindFirst(NETOBJTYPE_CHARACTER); + for(; p; p = (CCharacter *)p->TypeNext()) { - if(p == notthis) + if(p == pNotThis) continue; - float len = distance(pos, p->pos); - if(len < CHARACTER::phys_size+radius) + float Len = distance(Pos, p->m_Pos); + if(Len < p->m_ProximityRadius+Radius) { - if(len < closest_range) + if(Len < ClosestRange) { - closest_range = len; - closest = p; + ClosestRange = Len; + pClosest = p; } } } - return closest; + return pClosest; } diff --git a/src/game/server/gameworld.hpp b/src/game/server/gameworld.h index 4757ad67..2d1cc4be 100644 --- a/src/game/server/gameworld.hpp +++ b/src/game/server/gameworld.h @@ -1,20 +1,20 @@ #ifndef GAME_SERVER_GAMEWORLD_H #define GAME_SERVER_GAMEWORLD_H -#include <game/gamecore.hpp> +#include <game/gamecore.h> -class ENTITY; -class CHARACTER; +class CEntity; +class CCharacter; /* Class: Game World Tracks all entities in the game. Propagates tick and snap calls to all entities. */ -class GAMEWORLD +class CGameWorld { - void reset(); - void remove_entities(); + void Reset(); + void RemoveEntities(); enum { @@ -22,19 +22,27 @@ class GAMEWORLD }; // TODO: two lists seams kinda not good, shouldn't be needed - ENTITY *first_entity; - ENTITY *first_entity_types[NUM_ENT_TYPES]; + CEntity *m_pFirstEntity; + CEntity *m_apFirstEntityTypes[NUM_ENT_TYPES]; + + class CGameContext *m_pGameServer; + class IServer *m_pServer; public: - bool reset_requested; - bool paused; - WORLD_CORE core; + class CGameContext *GameServer() { return m_pGameServer; } + class IServer *Server() { return m_pServer; } + + bool m_ResetRequested; + bool m_Paused; + CWorldCore m_Core; + + CGameWorld(); + ~CGameWorld(); - GAMEWORLD(); - ~GAMEWORLD(); + void SetGameServer(CGameContext *pGameServer); - ENTITY *find_first() { return first_entity; } - ENTITY *find_first(int type); + CEntity *FindFirst() { return m_pFirstEntity; } + CEntity *FindFirst(int Type); /* Function: find_entities @@ -51,37 +59,37 @@ public: Returns: Number of entities found and added to the ents array. */ - int find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type = -1); + int FindEntities(vec2 Pos, float Radius, CEntity **ppEnts, int Max, int Type = -1); /* - Function: interserct_character - Finds the closest character that intersects the line. + Function: interserct_CCharacter + Finds the closest CCharacter that intersects the line. Arguments: pos0 - Start position pos2 - End position - radius - How for from the line the character is allowed to be. + radius - How for from the line the CCharacter is allowed to be. new_pos - Intersection position notthis - Entity to ignore intersecting with Returns: Returns a pointer to the closest hit or NULL of there is no intersection. */ - class CHARACTER *intersect_character(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class ENTITY *notthis = 0); + class CCharacter *IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2 &NewPos, class CEntity *pNotThis = 0); /* - Function: closest_character - Finds the closest character to a specific point. + Function: closest_CCharacter + Finds the closest CCharacter to a specific point. Arguments: pos - The center position. - radius - How far off the character is allowed to be + radius - How far off the CCharacter is allowed to be notthis - Entity to ignore Returns: - Returns a pointer to the closest character or NULL if no character is close enough. + Returns a pointer to the closest CCharacter or NULL if no CCharacter is close enough. */ - class CHARACTER *closest_character(vec2 pos, float radius, ENTITY *notthis); + class CCharacter *ClosestCharacter(vec2 Pos, float Radius, CEntity *ppNotThis); /* Function: insert_entity @@ -90,7 +98,7 @@ public: Arguments: entity - Entity to add */ - void insert_entity(ENTITY *entity); + void InsertEntity(CEntity *pEntity); /* Function: remove_entity @@ -99,7 +107,7 @@ public: Arguments: entity - Entity to remove */ - void remove_entity(ENTITY *entity); + void RemoveEntity(CEntity *pEntity); /* Function: destroy_entity @@ -108,7 +116,7 @@ public: Arguments: entity - Entity to destroy */ - void destroy_entity(ENTITY *entity); + void DestroyEntity(CEntity *pEntity); /* Function: snap @@ -119,7 +127,7 @@ public: snapping_client - ID of the client which snapshot is being created. */ - void snap(int snapping_client); + void Snap(int SnappingClient); /* Function: tick @@ -127,7 +135,7 @@ public: the world to the next tick. */ - void tick(); + void Tick(); }; #endif diff --git a/src/game/server/hooks.cpp b/src/game/server/hooks.cpp deleted file mode 100644 index 89c95f79..00000000 --- a/src/game/server/hooks.cpp +++ /dev/null @@ -1,599 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <base/math.hpp> - -#include <engine/e_config.h> -#include <engine/e_server_interface.h> -#include <engine/e_memheap.h> - -#include <game/version.hpp> -#include <game/collision.hpp> -#include <game/layers.hpp> - -#include <game/gamecore.hpp> - -#include "gamecontext.hpp" -#include "gamemodes/dm.hpp" -#include "gamemodes/tdm.hpp" -#include "gamemodes/ctf.hpp" -#include "gamemodes/mod.hpp" - -TUNING_PARAMS tuning; - -static void check_pure_tuning() -{ - // might not be created yet during start up - if(!game.controller) - return; - - if( strcmp(game.controller->gametype, "DM")==0 || - strcmp(game.controller->gametype, "TDM")==0 || - strcmp(game.controller->gametype, "CTF")==0) - { - TUNING_PARAMS p; - if(memcmp(&p, &tuning, sizeof(TUNING_PARAMS)) != 0) - { - dbg_msg("server", "resetting tuning due to pure server"); - tuning = p; - } - } -} - -struct VOTEOPTION -{ - VOTEOPTION *next; - VOTEOPTION *prev; - char command[1]; -}; - -static HEAP *voteoption_heap = 0; -static VOTEOPTION *voteoption_first = 0; -static VOTEOPTION *voteoption_last = 0; - -void send_tuning_params(int cid) -{ - check_pure_tuning(); - - msg_pack_start(NETMSGTYPE_SV_TUNEPARAMS, MSGFLAG_VITAL); - int *params = (int *)&tuning; - for(unsigned i = 0; i < sizeof(tuning)/sizeof(int); i++) - msg_pack_int(params[i]); - msg_pack_end(); - server_send_msg(cid); -} - -// Server hooks -void mods_client_direct_input(int client_id, void *input) -{ - if(!game.world.paused) - game.players[client_id]->on_direct_input((NETOBJ_PLAYER_INPUT *)input); -} - -void mods_client_predicted_input(int client_id, void *input) -{ - if(!game.world.paused) - game.players[client_id]->on_predicted_input((NETOBJ_PLAYER_INPUT *)input); -} - -// Server hooks -void mods_tick() -{ - check_pure_tuning(); - - game.tick(); - -#ifdef CONF_DEBUG - if(config.dbg_dummies) - { - for(int i = 0; i < config.dbg_dummies ; i++) - { - NETOBJ_PLAYER_INPUT input = {0}; - input.direction = (i&1)?-1:1; - game.players[MAX_CLIENTS-i-1]->on_predicted_input(&input); - } - } -#endif -} - -void mods_snap(int client_id) -{ - game.snap(client_id); -} - -void mods_client_enter(int client_id) -{ - //game.world.insert_entity(&game.players[client_id]); - game.players[client_id]->respawn(); - dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id)); - - - char buf[512]; - str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), game.controller->get_team_name(game.players[client_id]->team)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, buf); - - dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), game.players[client_id]->team); -} - -void mods_connected(int client_id) -{ - game.players[client_id] = new(client_id) PLAYER(client_id); - //game.players[client_id].init(client_id); - //game.players[client_id].client_id = client_id; - - // Check which team the player should be on - if(config.sv_tournament_mode) - game.players[client_id]->team = -1; - else - game.players[client_id]->team = game.controller->get_auto_team(client_id); - - (void) game.controller->check_team_balance(); - - // send motd - NETMSG_SV_MOTD msg; - msg.message = config.sv_motd; - msg.pack(MSGFLAG_VITAL); - server_send_msg(client_id); -} - -void mods_client_drop(int client_id) -{ - game.abort_vote_kick_on_disconnect(client_id); - game.players[client_id]->on_disconnect(); - delete game.players[client_id]; - game.players[client_id] = 0; - - (void) game.controller->check_team_balance(); -} - -/*static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } - -static const char *liststr_find(const char *str, const char *needle) -{ - int needle_len = strlen(needle); - while(*str) - { - int wordlen = 0; - while(str[wordlen] && !is_separator(str[wordlen])) - wordlen++; - - if(wordlen == needle_len && strncmp(str, needle, needle_len) == 0) - return str; - - str += wordlen+1; - } - - return 0; -}*/ - -void mods_message(int msgtype, int client_id) -{ - void *rawmsg = netmsg_secure_unpack(msgtype); - PLAYER *p = game.players[client_id]; - - if(!rawmsg) - { - dbg_msg("server", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on()); - return; - } - - if(msgtype == NETMSGTYPE_CL_SAY) - { - NETMSG_CL_SAY *msg = (NETMSG_CL_SAY *)rawmsg; - int team = msg->team; - if(team) - team = p->team; - else - team = GAMECONTEXT::CHAT_ALL; - - if(config.sv_spamprotection && p->last_chat+time_freq() > time_get()) - return; - - p->last_chat = time_get(); - - game.send_chat(client_id, team, msg->message); - } - else if(msgtype == NETMSGTYPE_CL_CALLVOTE) - { - int64 now = time_get(); - if(game.vote_closetime) - { - game.send_chat_target(client_id, "Wait for current vote to end before calling a new one."); - return; - } - - int64 timeleft = p->last_votecall + time_freq()*60 - now; - if(timeleft > 0) - { - char chatmsg[512] = {0}; - str_format(chatmsg, sizeof(chatmsg), "You must wait %d seconds before making another vote", (timeleft/time_freq())+1); - game.send_chat_target(client_id, chatmsg); - return; - } - - char chatmsg[512] = {0}; - char desc[512] = {0}; - char cmd[512] = {0}; - NETMSG_CL_CALLVOTE *msg = (NETMSG_CL_CALLVOTE *)rawmsg; - if(str_comp_nocase(msg->type, "option") == 0) - { - VOTEOPTION *option = voteoption_first; - while(option) - { - if(str_comp_nocase(msg->value, option->command) == 0) - { - str_format(chatmsg, sizeof(chatmsg), "%s called vote to change server option '%s'", server_clientname(client_id), option->command); - str_format(desc, sizeof(desc), "%s", option->command); - str_format(cmd, sizeof(cmd), "%s", option->command); - break; - } - - option = option->next; - } - - if(!option) - { - str_format(chatmsg, sizeof(chatmsg), "'%s' isn't an option on this server", msg->value); - game.send_chat_target(client_id, chatmsg); - return; - } - } - else if(str_comp_nocase(msg->type, "kick") == 0) - { - if(!config.sv_vote_kick) - { - game.send_chat_target(client_id, "Server does not allow voting to kick players"); - return; - } - - int kick_id = atoi(msg->value); - if(kick_id < 0 || kick_id >= MAX_CLIENTS || !game.players[kick_id]) - { - game.send_chat_target(client_id, "Invalid client id to kick"); - return; - } - - str_format(chatmsg, sizeof(chatmsg), "%s called for vote to kick '%s'", server_clientname(client_id), server_clientname(kick_id)); - str_format(desc, sizeof(desc), "Kick '%s'", server_clientname(kick_id)); - str_format(cmd, sizeof(cmd), "kick %d", kick_id); - if (!config.sv_vote_kick_bantime) - str_format(cmd, sizeof(cmd), "kick %d", kick_id); - else - str_format(cmd, sizeof(cmd), "ban %d %d", kick_id, config.sv_vote_kick_bantime); - } - - if(cmd[0]) - { - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, chatmsg); - game.start_vote(desc, cmd); - p->vote = 1; - game.vote_creator = client_id; - p->last_votecall = now; - game.send_vote_status(-1); - } - } - else if(msgtype == NETMSGTYPE_CL_VOTE) - { - if(!game.vote_closetime) - return; - - if(p->vote == 0) - { - NETMSG_CL_VOTE *msg = (NETMSG_CL_VOTE *)rawmsg; - p->vote = msg->vote; - game.send_vote_status(-1); - } - } - else if (msgtype == NETMSGTYPE_CL_SETTEAM && !game.world.paused) - { - NETMSG_CL_SETTEAM *msg = (NETMSG_CL_SETTEAM *)rawmsg; - - if(config.sv_spamprotection && p->last_setteam+time_freq()*3 > time_get()) - return; - - // Switch team on given client and kill/respawn him - if(game.controller->can_join_team(msg->team, client_id)) - { - if(game.controller->can_change_team(p, msg->team)) - { - p->last_setteam = time_get(); - p->set_team(msg->team); - (void) game.controller->check_team_balance(); - } - else - game.send_broadcast("Teams must be balanced, please join other team", client_id); - } - else - { - char buf[128]; - str_format(buf, sizeof(buf), "Only %d active players are allowed", config.sv_max_clients-config.sv_spectator_slots); - game.send_broadcast(buf, client_id); - } - } - else if (msgtype == NETMSGTYPE_CL_CHANGEINFO || msgtype == NETMSGTYPE_CL_STARTINFO) - { - NETMSG_CL_CHANGEINFO *msg = (NETMSG_CL_CHANGEINFO *)rawmsg; - - if(config.sv_spamprotection && p->last_changeinfo+time_freq()*5 > time_get()) - return; - - p->last_changeinfo = time_get(); - - p->use_custom_color = msg->use_custom_color; - p->color_body = msg->color_body; - p->color_feet = msg->color_feet; - - // check for invalid chars - unsigned char *name = (unsigned char *)msg->name; - while (*name) - { - if(*name < 32) - *name = ' '; - name++; - } - - // copy old name - char oldname[MAX_NAME_LENGTH]; - str_copy(oldname, server_clientname(client_id), MAX_NAME_LENGTH); - - server_setclientname(client_id, msg->name); - if(msgtype == NETMSGTYPE_CL_CHANGEINFO && strcmp(oldname, server_clientname(client_id)) != 0) - { - char chattext[256]; - str_format(chattext, sizeof(chattext), "%s changed name to %s", oldname, server_clientname(client_id)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, chattext); - } - - // set skin - str_copy(p->skin_name, msg->skin, sizeof(p->skin_name)); - - game.controller->on_player_info_change(p); - - if(msgtype == NETMSGTYPE_CL_STARTINFO) - { - // send vote options - NETMSG_SV_VOTE_CLEAROPTIONS clearmsg; - clearmsg.pack(MSGFLAG_VITAL); - server_send_msg(client_id); - VOTEOPTION *current = voteoption_first; - while(current) - { - NETMSG_SV_VOTE_OPTION optionmsg; - optionmsg.command = current->command; - optionmsg.pack(MSGFLAG_VITAL); - server_send_msg(client_id); - current = current->next; - } - - // send tuning parameters to client - send_tuning_params(client_id); - - // - NETMSG_SV_READYTOENTER m; - m.pack(MSGFLAG_VITAL|MSGFLAG_FLUSH); - server_send_msg(client_id); - } - } - else if (msgtype == NETMSGTYPE_CL_EMOTICON && !game.world.paused) - { - NETMSG_CL_EMOTICON *msg = (NETMSG_CL_EMOTICON *)rawmsg; - - if(config.sv_spamprotection && p->last_emote+time_freq()*3 > time_get()) - return; - - p->last_emote = time_get(); - - game.send_emoticon(client_id, msg->emoticon); - } - else if (msgtype == NETMSGTYPE_CL_KILL && !game.world.paused) - { - if(p->last_kill+time_freq()*3 > time_get()) - return; - - p->last_kill = time_get(); - p->kill_character(WEAPON_SELF); - p->respawn_tick = server_tick()+server_tickspeed()*3; - } -} - -static void con_tune_param(void *result, void *user_data) -{ - const char *param_name = console_arg_string(result, 0); - float new_value = console_arg_float(result, 1); - - if(tuning.set(param_name, new_value)) - { - dbg_msg("tuning", "%s changed to %.2f", param_name, new_value); - send_tuning_params(-1); - } - else - console_print("No such tuning parameter"); -} - -static void con_tune_reset(void *result, void *user_data) -{ - TUNING_PARAMS p; - tuning = p; - send_tuning_params(-1); - console_print("tuning reset"); -} - -static void con_tune_dump(void *result, void *user_data) -{ - for(int i = 0; i < tuning.num(); i++) - { - float v; - tuning.get(i, &v); - dbg_msg("tuning", "%s %.2f", tuning.names[i], v); - } -} - - -static void con_change_map(void *result, void *user_data) -{ - game.controller->change_map(console_arg_string(result, 0)); -} - -static void con_restart(void *result, void *user_data) -{ - if(console_arg_num(result)) - game.controller->do_warmup(console_arg_int(result, 0)); - else - game.controller->startround(); -} - -static void con_broadcast(void *result, void *user_data) -{ - game.send_broadcast(console_arg_string(result, 0), -1); -} - -static void con_say(void *result, void *user_data) -{ - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, console_arg_string(result, 0)); -} - -static void con_set_team(void *result, void *user_data) -{ - int client_id = clamp(console_arg_int(result, 0), 0, (int)MAX_CLIENTS); - int team = clamp(console_arg_int(result, 1), -1, 1); - - dbg_msg("", "%d %d", client_id, team); - - if(!game.players[client_id]) - return; - - game.players[client_id]->set_team(team); - (void) game.controller->check_team_balance(); -} - -static void con_addvote(void *result, void *user_data) -{ - int len = strlen(console_arg_string(result, 0)); - - if(!voteoption_heap) - voteoption_heap = memheap_create(); - - VOTEOPTION *option = (VOTEOPTION *)memheap_allocate(voteoption_heap, sizeof(VOTEOPTION) + len); - option->next = 0; - option->prev = voteoption_last; - if(option->prev) - option->prev->next = option; - voteoption_last = option; - if(!voteoption_first) - voteoption_first = option; - - mem_copy(option->command, console_arg_string(result, 0), len+1); - dbg_msg("server", "added option '%s'", option->command); -} - -static void con_vote(void *result, void *user_data) -{ - if(str_comp_nocase(console_arg_string(result, 0), "yes") == 0) - game.vote_enforce = GAMECONTEXT::VOTE_ENFORCE_YES; - else if(str_comp_nocase(console_arg_string(result, 0), "no") == 0) - game.vote_enforce = GAMECONTEXT::VOTE_ENFORCE_NO; - dbg_msg("server", "forcing vote %s", console_arg_string(result, 0)); -} - -void mods_console_init() -{ - MACRO_REGISTER_COMMAND("tune", "si", CFGFLAG_SERVER, con_tune_param, 0, ""); - MACRO_REGISTER_COMMAND("tune_reset", "", CFGFLAG_SERVER, con_tune_reset, 0, ""); - MACRO_REGISTER_COMMAND("tune_dump", "", CFGFLAG_SERVER, con_tune_dump, 0, ""); - - MACRO_REGISTER_COMMAND("change_map", "r", CFGFLAG_SERVER, con_change_map, 0, ""); - MACRO_REGISTER_COMMAND("restart", "?i", CFGFLAG_SERVER, con_restart, 0, ""); - MACRO_REGISTER_COMMAND("broadcast", "r", CFGFLAG_SERVER, con_broadcast, 0, ""); - MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_SERVER, con_say, 0, ""); - MACRO_REGISTER_COMMAND("set_team", "ii", CFGFLAG_SERVER, con_set_team, 0, ""); - - MACRO_REGISTER_COMMAND("addvote", "r", CFGFLAG_SERVER, con_addvote, 0, ""); - MACRO_REGISTER_COMMAND("vote", "r", CFGFLAG_SERVER, con_vote, 0, ""); -} - -void mods_init() -{ - //if(!data) /* only load once */ - //data = load_data_from_memory(internal_data); - - for(int i = 0; i < NUM_NETOBJTYPES; i++) - snap_set_staticsize(i, netobj_get_size(i)); - - layers_init(); - col_init(); - - // reset everything here - //world = new GAMEWORLD; - //players = new PLAYER[MAX_CLIENTS]; - - // select gametype - if(strcmp(config.sv_gametype, "mod") == 0) - game.controller = new GAMECONTROLLER_MOD; - else if(strcmp(config.sv_gametype, "ctf") == 0) - game.controller = new GAMECONTROLLER_CTF; - else if(strcmp(config.sv_gametype, "tdm") == 0) - game.controller = new GAMECONTROLLER_TDM; - else - game.controller = new GAMECONTROLLER_DM; - - // setup core world - //for(int i = 0; i < MAX_CLIENTS; i++) - // game.players[i].core.world = &game.world.core; - - // create all entities from the game layer - MAPITEM_LAYER_TILEMAP *tmap = layers_game_layer(); - TILE *tiles = (TILE *)map_get_data(tmap->data); - - /* - num_spawn_points[0] = 0; - num_spawn_points[1] = 0; - num_spawn_points[2] = 0; - */ - - for(int y = 0; y < tmap->height; y++) - { - for(int x = 0; x < tmap->width; x++) - { - int index = tiles[y*tmap->width+x].index; - - if(index >= ENTITY_OFFSET) - { - vec2 pos(x*32.0f+16.0f, y*32.0f+16.0f); - game.controller->on_entity(index-ENTITY_OFFSET, pos); - } - } - } - - //game.world.insert_entity(game.controller); - -#ifdef CONF_DEBUG - if(config.dbg_dummies) - { - for(int i = 0; i < config.dbg_dummies ; i++) - { - mods_connected(MAX_CLIENTS-i-1); - mods_client_enter(MAX_CLIENTS-i-1); - if(game.controller->is_teamplay()) - game.players[MAX_CLIENTS-i-1]->team = i&1; - } - } -#endif -} - -void mods_shutdown() -{ - delete game.controller; - game.controller = 0; - game.clear(); -} - -void mods_presnap() {} -void mods_postsnap() -{ - game.events.clear(); -} - -const char *mods_net_version() { return GAME_NETVERSION; } -const char *mods_version() { return GAME_VERSION; } diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index a0a2b051..0eb61cac 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -1,183 +1,185 @@ #include <new> +#include "player.h" -#include <engine/e_server_interface.h> -#include "player.hpp" -#include "gamecontext.hpp" +MACRO_ALLOC_POOL_ID_IMPL(CPlayer, MAX_CLIENTS) -MACRO_ALLOC_POOL_ID_IMPL(PLAYER, MAX_CLIENTS) - -PLAYER::PLAYER(int client_id) +IServer *CPlayer::Server() const { return m_pGameServer->Server(); } + +CPlayer::CPlayer(CGameContext *pGameServer, int CID, int Team) { - respawn_tick = server_tick(); - character = 0; - this->client_id = client_id; + m_pGameServer = pGameServer; + m_RespawnTick = Server()->Tick(); + m_DieTick = Server()->Tick(); + Character = 0; + this->m_ClientID = CID; + m_Team = GameServer()->m_pController->ClampTeam(Team); } -PLAYER::~PLAYER() +CPlayer::~CPlayer() { - delete character; - character = 0; + delete Character; + Character = 0; } -void PLAYER::tick() +void CPlayer::Tick() { - server_setclientscore(client_id, score); + Server()->SetClientScore(m_ClientID, m_Score); // do latency stuff { - CLIENT_INFO info; - if(server_getclientinfo(client_id, &info)) + IServer::CClientInfo Info; + if(Server()->GetClientInfo(m_ClientID, &Info)) { - latency.accum += info.latency; - latency.accum_max = max(latency.accum_max, info.latency); - latency.accum_min = min(latency.accum_min, info.latency); + m_Latency.m_Accum += Info.m_Latency; + m_Latency.m_AccumMax = max(m_Latency.m_AccumMax, Info.m_Latency); + m_Latency.m_AccumMin = min(m_Latency.m_AccumMin, Info.m_Latency); } - - if(server_tick()%server_tickspeed() == 0) + // each second + if(Server()->Tick()%Server()->TickSpeed() == 0) { - latency.avg = latency.accum/server_tickspeed(); - latency.max = latency.accum_max; - latency.min = latency.accum_min; - latency.accum = 0; - latency.accum_min = 1000; - latency.accum_max = 0; + m_Latency.m_Avg = m_Latency.m_Accum/Server()->TickSpeed(); + m_Latency.m_Max = m_Latency.m_AccumMax; + m_Latency.m_Min = m_Latency.m_AccumMin; + m_Latency.m_Accum = 0; + m_Latency.m_AccumMin = 1000; + m_Latency.m_AccumMax = 0; } } - if(!character && die_tick+server_tickspeed()*3 <= server_tick()) - spawning = true; + if(!Character && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) + m_Spawning = true; - if(character) + if(Character) { - if(character->alive) + if(Character->IsAlive()) { - view_pos = character->pos; + m_ViewPos = Character->m_Pos; } else { - delete character; - character = 0; + delete Character; + Character = 0; } } - else if(spawning && respawn_tick <= server_tick()) - try_respawn(); + else if(m_Spawning && m_RespawnTick <= Server()->Tick()) + TryRespawn(); } -void PLAYER::snap(int snapping_client) +void CPlayer::Snap(int SnappingClient) { - NETOBJ_CLIENT_INFO *client_info = (NETOBJ_CLIENT_INFO *)snap_new_item(NETOBJTYPE_CLIENT_INFO, client_id, sizeof(NETOBJ_CLIENT_INFO)); - str_to_ints(&client_info->name0, 6, server_clientname(client_id)); - str_to_ints(&client_info->skin0, 6, skin_name); - client_info->use_custom_color = use_custom_color; - client_info->color_body = color_body; - client_info->color_feet = color_feet; - - NETOBJ_PLAYER_INFO *info = (NETOBJ_PLAYER_INFO *)snap_new_item(NETOBJTYPE_PLAYER_INFO, client_id, sizeof(NETOBJ_PLAYER_INFO)); - - info->latency = latency.min; - info->latency_flux = latency.max-latency.min; - info->local = 0; - info->cid = client_id; - info->score = score; - info->team = team; - - if(client_id == snapping_client) - info->local = 1; + CNetObj_ClientInfo *ClientInfo = static_cast<CNetObj_ClientInfo *>(Server()->SnapNewItem(NETOBJTYPE_CLIENTINFO, m_ClientID, sizeof(CNetObj_ClientInfo))); + StrToInts(&ClientInfo->m_Name0, 6, Server()->ClientName(m_ClientID)); + StrToInts(&ClientInfo->m_Skin0, 6, m_TeeInfos.m_SkinName); + ClientInfo->m_UseCustomColor = m_TeeInfos.m_UseCustomColor; + ClientInfo->m_ColorBody = m_TeeInfos.m_ColorBody; + ClientInfo->m_ColorFeet = m_TeeInfos.m_ColorFeet; + + CNetObj_PlayerInfo *Info = static_cast<CNetObj_PlayerInfo *>(Server()->SnapNewItem(NETOBJTYPE_PLAYERINFO, m_ClientID, sizeof(CNetObj_PlayerInfo))); + + Info->m_Latency = m_Latency.m_Min; + Info->m_LatencyFlux = m_Latency.m_Max-m_Latency.m_Min; + Info->m_Local = 0; + Info->m_ClientId = m_ClientID; + Info->m_Score = m_Score; + Info->m_Team = m_Team; + + if(m_ClientID == SnappingClient) + Info->m_Local = 1; } -void PLAYER::on_disconnect() +void CPlayer::OnDisconnect() { - kill_character(WEAPON_GAME); - - //game.controller->on_player_death(&game.players[client_id], 0, -1); - - char buf[512]; - str_format(buf, sizeof(buf), "%s has left the game", server_clientname(client_id)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, buf); + KillCharacter(); + + if(Server()->ClientIngame(m_ClientID)) + { + char Buf[512]; + str_format(Buf, sizeof(Buf), "%s has left the game", Server()->ClientName(m_ClientID)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, Buf); - dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); + dbg_msg("game", "leave player='%d:%s'", m_ClientID, Server()->ClientName(m_ClientID)); + } } -void PLAYER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) +void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) { - CHARACTER *chr = get_character(); - if(chr) - chr->on_predicted_input(new_input); + if(Character) + Character->OnPredictedInput(NewInput); } -void PLAYER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) +void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) { - CHARACTER *chr = get_character(); - if(chr) - chr->on_direct_input(new_input); + if(Character) + Character->OnDirectInput(NewInput); - if(!chr && team >= 0 && (new_input->fire&1)) - spawning = true; + if(!Character && m_Team >= 0 && (NewInput->m_Fire&1)) + m_Spawning = true; - if(!chr && team == -1) - view_pos = vec2(new_input->target_x, new_input->target_y); + if(!Character && m_Team == -1) + m_ViewPos = vec2(NewInput->m_TargetX, NewInput->m_TargetY); } -CHARACTER *PLAYER::get_character() +CCharacter *CPlayer::GetCharacter() { - if(character && character->alive) - return character; + if(Character && Character->IsAlive()) + return Character; return 0; } -void PLAYER::kill_character(int weapon) +void CPlayer::KillCharacter(int Weapon) { - //CHARACTER *chr = get_character(); - if(character) + if(Character) { - character->die(client_id, weapon); - delete character; - character = 0; + Character->Die(m_ClientID, Weapon); + delete Character; + Character = 0; } } -void PLAYER::respawn() +void CPlayer::Respawn() { - if(team > -1) - spawning = true; + if(m_Team > -1) + m_Spawning = true; } -void PLAYER::set_team(int new_team) +void CPlayer::SetTeam(int Team) { // clamp the team - new_team = game.controller->clampteam(new_team); - if(team == new_team) + Team = GameServer()->m_pController->ClampTeam(Team); + if(m_Team == Team) return; - char buf[512]; - str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), game.controller->get_team_name(new_team)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, buf); + char Buf[512]; + str_format(Buf, sizeof(Buf), "%s joined the %s", Server()->ClientName(m_ClientID), GameServer()->m_pController->GetTeamName(Team)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, Buf); - kill_character(WEAPON_GAME); - team = new_team; - score = 0; - dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); + KillCharacter(); + m_Team = Team; + m_Score = 0; + // we got to wait 0.5 secs before respawning + m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; + dbg_msg("game", "team_join player='%d:%s' m_Team=%d", m_ClientID, Server()->ClientName(m_ClientID), m_Team); - game.controller->on_player_info_change(game.players[client_id]); + GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]); } -void PLAYER::try_respawn() +void CPlayer::TryRespawn() { - vec2 spawnpos = vec2(100.0f, -60.0f); + vec2 SpawnPos = vec2(100.0f, -60.0f); - if(!game.controller->can_spawn(this, &spawnpos)) + if(!GameServer()->m_pController->CanSpawn(this, &SpawnPos)) return; // check if the position is occupado - ENTITY *ents[2] = {0}; - int num_ents = game.world.find_entities(spawnpos, 64, ents, 2, NETOBJTYPE_CHARACTER); + CEntity *apEnts[2] = {0}; + int NumEnts = GameServer()->m_World.FindEntities(SpawnPos, 64, apEnts, 2, NETOBJTYPE_CHARACTER); - if(num_ents == 0) + if(NumEnts == 0) { - spawning = false; - character = new(client_id) CHARACTER(); - character->spawn(this, spawnpos, team); - game.create_playerspawn(spawnpos); + m_Spawning = false; + Character = new(m_ClientID) CCharacter(&GameServer()->m_World); + Character->Spawn(this, SpawnPos); + GameServer()->CreatePlayerSpawn(SpawnPos); } } diff --git a/src/game/server/player.h b/src/game/server/player.h new file mode 100644 index 00000000..1b631d45 --- /dev/null +++ b/src/game/server/player.h @@ -0,0 +1,89 @@ +#ifndef GAME_SERVER_PLAYER_H +#define GAME_SERVER_PLAYER_H + +// this include should perhaps be removed +#include "entities/character.h" +#include "gamecontext.h" + +// player object +class CPlayer +{ + MACRO_ALLOC_POOL_ID() + +public: + CPlayer(CGameContext *pGameServer, int CID, int Team); + ~CPlayer(); + + void Init(int CID); + + void TryRespawn(); + void Respawn(); + void SetTeam(int Team); + int GetTeam() const { return m_Team; }; + int GetCID() const { return m_ClientID; }; + + void Tick(); + void Snap(int SnappingClient); + + void OnDirectInput(CNetObj_PlayerInput *NewInput); + void OnPredictedInput(CNetObj_PlayerInput *NewInput); + void OnDisconnect(); + + void KillCharacter(int Weapon = WEAPON_GAME); + CCharacter *GetCharacter(); + + //--------------------------------------------------------- + // this is used for snapping so we know how we can clip the view for the player + vec2 m_ViewPos; + + // + int m_Vote; + int m_VotePos; + // + int m_Last_VoteCall; + int m_Last_VoteTry; + int m_Last_Chat; + int m_Last_SetTeam; + int m_Last_ChangeInfo; + int m_Last_Emote; + int m_Last_Kill; + + // TODO: clean this up + struct + { + char m_SkinName[64]; + int m_UseCustomColor; + int m_ColorBody; + int m_ColorFeet; + } m_TeeInfos; + + int m_RespawnTick; + int m_DieTick; + int m_Score; + bool m_ForceBalanced; + +private: + CCharacter *Character; + CGameContext *m_pGameServer; + + CGameContext *GameServer() const { return m_pGameServer; } + IServer *Server() const; + + // + bool m_Spawning; + int m_ClientID; + int m_Team; + + // network latency calculations + struct + { + int m_Accum; + int m_AccumMin; + int m_AccumMax; + int m_Avg; + int m_Min; + int m_Max; + } m_Latency; +}; + +#endif diff --git a/src/game/server/player.hpp b/src/game/server/player.hpp deleted file mode 100644 index e93aee01..00000000 --- a/src/game/server/player.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef GAME_SERVER_PLAYER_H -#define GAME_SERVER_PLAYER_H - -// this include should perhaps be removed -#include "entities/character.hpp" - -// player object -class PLAYER -{ - MACRO_ALLOC_POOL_ID() -private: - CHARACTER *character; -public: - PLAYER(int client_id); - ~PLAYER(); - - // TODO: clean this up - char skin_name[64]; - int use_custom_color; - int color_body; - int color_feet; - - int respawn_tick; - int die_tick; - // - bool spawning; - int client_id; - int team; - int score; - bool force_balanced; - - // - int vote; - int64 last_votecall; - - // - int64 last_chat; - int64 last_setteam; - int64 last_changeinfo; - int64 last_emote; - int64 last_kill; - - // network latency calculations - struct - { - int accum; - int accum_min; - int accum_max; - int avg; - int min; - int max; - } latency; - - // this is used for snapping so we know how we can clip the view for the player - vec2 view_pos; - - void init(int client_id); - - CHARACTER *get_character(); - - void kill_character(int weapon); - - void try_respawn(); - void respawn(); - void set_team(int team); - - void tick(); - void snap(int snapping_client); - - void on_direct_input(NETOBJ_PLAYER_INPUT *new_input); - void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input); - void on_disconnect(); -}; - -#endif diff --git a/src/game/tuning.h b/src/game/tuning.h new file mode 100644 index 00000000..b336fcb3 --- /dev/null +++ b/src/game/tuning.h @@ -0,0 +1,46 @@ +#ifndef GAME_TUNING_H +#define GAME_TUNING_H +#undef GAME_TUNING_H // this file will be included several times + +// physics tuning +MACRO_TUNING_PARAM(GroundControlSpeed, ground_control_speed, 10.0f) +MACRO_TUNING_PARAM(GroundControlAccel, ground_control_accel, 100.0f / TicksPerSecond) +MACRO_TUNING_PARAM(GroundFriction, ground_friction, 0.5f) +MACRO_TUNING_PARAM(GroundJumpImpulse, ground_jump_impulse, 13.2f) +MACRO_TUNING_PARAM(AirJumpImpulse, air_jump_impulse, 12.0f) +MACRO_TUNING_PARAM(AirControlSpeed, air_control_speed, 250.0f / TicksPerSecond) +MACRO_TUNING_PARAM(AirControlAccel, air_control_accel, 1.5f) +MACRO_TUNING_PARAM(AirFriction, air_friction, 0.95f) +MACRO_TUNING_PARAM(HookLength, hook_length, 380.0f) +MACRO_TUNING_PARAM(HookFireSpeed, hook_fire_speed, 80.0f) +MACRO_TUNING_PARAM(HookDragAccel, hook_drag_accel, 3.0f) +MACRO_TUNING_PARAM(HookDragSpeed, hook_drag_speed, 15.0f) +MACRO_TUNING_PARAM(Gravity, gravity, 0.5f) + +MACRO_TUNING_PARAM(VelrampStart, velramp_start, 550) +MACRO_TUNING_PARAM(VelrampRange, velramp_range, 2000) +MACRO_TUNING_PARAM(VelrampCurvature, velramp_curvature, 1.4f) + +// weapon tuning +MACRO_TUNING_PARAM(GunCurvature, gun_curvature, 1.25f) +MACRO_TUNING_PARAM(GunSpeed, gun_speed, 2200.0f) +MACRO_TUNING_PARAM(GunLifetime, gun_lifetime, 2.0f) + +MACRO_TUNING_PARAM(ShotgunCurvature, shotgun_curvature, 1.25f) +MACRO_TUNING_PARAM(ShotgunSpeed, shotgun_speed, 2750.0f) +MACRO_TUNING_PARAM(ShotgunSpeeddiff, shotgun_speeddiff, 0.8f) +MACRO_TUNING_PARAM(ShotgunLifetime, shotgun_lifetime, 0.20f) + +MACRO_TUNING_PARAM(GrenadeCurvature, grenade_curvature, 7.0f) +MACRO_TUNING_PARAM(GrenadeSpeed, grenade_speed, 1000.0f) +MACRO_TUNING_PARAM(GrenadeLifetime, grenade_lifetime, 2.0f) + +MACRO_TUNING_PARAM(LaserReach, laser_reach, 800.0f) +MACRO_TUNING_PARAM(LaserBounceDelay, laser_bounce_delay, 150) +MACRO_TUNING_PARAM(LaserBounceNum, laser_bounce_num, 1) +MACRO_TUNING_PARAM(LaserBounceCost, laser_bounce_cost, 0) +MACRO_TUNING_PARAM(LaserDamage, laser_damage, 5) + +MACRO_TUNING_PARAM(PlayerCollision, player_collision, 1) +MACRO_TUNING_PARAM(PlayerHooking, player_hooking, 1) +#endif diff --git a/src/game/tuning.hpp b/src/game/tuning.hpp deleted file mode 100644 index 4554a8eb..00000000 --- a/src/game/tuning.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* physics tuning */ -MACRO_TUNING_PARAM(ground_control_speed, 10.0f) -MACRO_TUNING_PARAM(ground_control_accel, 100.0f / ticks_per_second) -MACRO_TUNING_PARAM(ground_friction, 0.5f) -MACRO_TUNING_PARAM(ground_jump_impulse, 13.2f) -MACRO_TUNING_PARAM(air_jump_impulse, 12.0f) -MACRO_TUNING_PARAM(air_control_speed, 250.0f / ticks_per_second) -MACRO_TUNING_PARAM(air_control_accel, 1.5f) -MACRO_TUNING_PARAM(air_friction, 0.95f) -MACRO_TUNING_PARAM(hook_length, 380.0f) -MACRO_TUNING_PARAM(hook_fire_speed, 80.0f) -MACRO_TUNING_PARAM(hook_drag_accel, 3.0f) -MACRO_TUNING_PARAM(hook_drag_speed, 15.0f) -MACRO_TUNING_PARAM(gravity, 0.5f) - -MACRO_TUNING_PARAM(velramp_start, 550) -MACRO_TUNING_PARAM(velramp_range, 2000) -MACRO_TUNING_PARAM(velramp_curvature, 1.4f) - -/* weapon tuning */ -MACRO_TUNING_PARAM(gun_curvature, 1.25f) -MACRO_TUNING_PARAM(gun_speed, 2200.0f) -MACRO_TUNING_PARAM(gun_lifetime, 2.0f) - -MACRO_TUNING_PARAM(shotgun_curvature, 1.25f) -MACRO_TUNING_PARAM(shotgun_speed, 2750.0f) -MACRO_TUNING_PARAM(shotgun_speeddiff, 0.8f) -MACRO_TUNING_PARAM(shotgun_lifetime, 0.20f) - -MACRO_TUNING_PARAM(grenade_curvature, 7.0f) -MACRO_TUNING_PARAM(grenade_speed, 1000.0f) -MACRO_TUNING_PARAM(grenade_lifetime, 2.0f) - -MACRO_TUNING_PARAM(laser_reach, 800.0f) -MACRO_TUNING_PARAM(laser_bounce_delay, 150) -MACRO_TUNING_PARAM(laser_bounce_num, 1) -MACRO_TUNING_PARAM(laser_bounce_cost, 0) -MACRO_TUNING_PARAM(laser_damage, 5) - -MACRO_TUNING_PARAM(player_collision, 1) -MACRO_TUNING_PARAM(player_hooking, 1) diff --git a/src/game/variables.h b/src/game/variables.h new file mode 100644 index 00000000..6a247671 --- /dev/null +++ b/src/game/variables.h @@ -0,0 +1,79 @@ +#ifndef GAME_VARIABLES_H +#define GAME_VARIABLES_H +#undef GAME_VARIABLES_H // this file will be included several times + + +// client +MACRO_CONFIG_INT(ClPredict, cl_predict, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict client movements") +MACRO_CONFIG_INT(ClNameplates, cl_nameplates, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show nameplates") +MACRO_CONFIG_INT(ClNameplatesAlways, cl_nameplates_always, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Always show nameplats disregarding of distance") +MACRO_CONFIG_INT(ClAutoswitchWeapons, cl_autoswitch_weapons, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Auto switch weapon on pickup") + +MACRO_CONFIG_INT(ClShowfps, cl_showfps, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame FPS counter") + +MACRO_CONFIG_INT(ClAirjumpindicator, cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") +MACRO_CONFIG_INT(ClThreadsoundloading, cl_threadsoundloading, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") + +MACRO_CONFIG_INT(ClWarningTeambalance, cl_warning_teambalance, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Warn about team balance") + +MACRO_CONFIG_INT(ClMouseDeadzone, cl_mouse_deadzone, 300, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") +MACRO_CONFIG_INT(ClMouseFollowfactor, cl_mouse_followfactor, 60, 0, 200, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") +MACRO_CONFIG_INT(ClMouseMaxDistance, cl_mouse_max_distance, 800, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") + +MACRO_CONFIG_INT(EdShowkeys, ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") + +MACRO_CONFIG_INT(ClFlow, cl_flow, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") + +MACRO_CONFIG_INT(ClShowWelcome, cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") +MACRO_CONFIG_INT(ClMotdTime, cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT|CFGFLAG_SAVE, "How long to show the server message of the day") + +MACRO_CONFIG_STR(ClVersionServer, cl_version_server, 100, "version.teeworlds.com", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Server to use to check for new versions") + +MACRO_CONFIG_STR(ClLanguagefile, cl_languagefile, 255, "", CFGFLAG_CLIENT|CFGFLAG_SAVE, "What language file to use") + +MACRO_CONFIG_INT(PlayerUseCustomColor, player_use_custom_color, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toggles usage of custom colors") +MACRO_CONFIG_INT(PlayerColorBody, player_color_body, 65408, 0, 0xFFFFFF, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player body color") +MACRO_CONFIG_INT(PlayerColorFeet, player_color_feet, 65408, 0, 0xFFFFFF, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player feet color") +MACRO_CONFIG_STR(PlayerSkin, player_skin, 64, "default", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player skin") + +MACRO_CONFIG_INT(UiPage, ui_page, 5, 0, 9, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page") +MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toolbox page") +MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 25, "localhost:8303", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface server address") +MACRO_CONFIG_INT(UiScale, ui_scale, 100, 1, 100000, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface scale") + +MACRO_CONFIG_INT(UiColorHue, ui_color_hue, 160, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color hue") +MACRO_CONFIG_INT(UiColorSat, ui_color_sat, 70, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color saturation") +MACRO_CONFIG_INT(UiColorLht, ui_color_lht, 175, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color lightness") +MACRO_CONFIG_INT(UiColorAlpha, ui_color_alpha, 228, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface alpha") + +MACRO_CONFIG_INT(GfxNoclip, gfx_noclip, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Disable clipping") + +// server +MACRO_CONFIG_INT(SvWarmup, sv_warmup, 0, 0, 0, CFGFLAG_SERVER, "Number of seconds to do warpup before round starts") +MACRO_CONFIG_STR(SvMotd, sv_motd, 900, "", CFGFLAG_SERVER, "Message of the day to display for the clients") +MACRO_CONFIG_INT(SvTeamdamage, sv_teamdamage, 0, 0, 1, CFGFLAG_SERVER, "Team damage") +MACRO_CONFIG_STR(SvMaprotation, sv_maprotation, 768, "", CFGFLAG_SERVER, "Maps to rotate between") +MACRO_CONFIG_INT(SvRoundsPerMap, sv_rounds_per_map, 1, 1, 100, CFGFLAG_SERVER, "Number of rounds on each map before rotating") +MACRO_CONFIG_INT(SvPowerups, sv_powerups, 1, 0, 1, CFGFLAG_SERVER, "Allow powerups like ninja") +MACRO_CONFIG_INT(SvScorelimit, sv_scorelimit, 20, 0, 1000, CFGFLAG_SERVER, "Score limit (0 disables)") +MACRO_CONFIG_INT(SvTimelimit, sv_timelimit, 0, 0, 1000, CFGFLAG_SERVER, "Time limit in minutes (0 disables)") +MACRO_CONFIG_STR(SvGametype, sv_gametype, 32, "dm", CFGFLAG_SERVER, "Game type (dm, tdm, ctf)") +MACRO_CONFIG_INT(SvTournamentMode, sv_tournament_mode, 0, 0, 1, CFGFLAG_SERVER, "Tournament mode. When enabled, players joins the server as spectator") +MACRO_CONFIG_INT(SvSpamprotection, sv_spamprotection, 1, 0, 1, CFGFLAG_SERVER, "Spam protection") + +MACRO_CONFIG_INT(SvSpectatorSlots, sv_spectator_slots, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Number of slots to reserve for spectators") +MACRO_CONFIG_INT(SvTeambalanceTime, sv_teambalance_time, 1, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before autobalancing teams") + +MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players") +MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick") +MACRO_CONFIG_INT(SvVoteScorelimit, sv_vote_scorelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change score limit") +MACRO_CONFIG_INT(SvVoteTimelimit, sv_vote_timelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change time limit") + +// debug +#ifdef CONF_DEBUG // this one can crash the server if not used correctly + MACRO_CONFIG_INT(DbgDummies, dbg_dummies, 0, 0, 15, CFGFLAG_SERVER, "") +#endif + +MACRO_CONFIG_INT(DbgFocus, dbg_focus, 0, 0, 1, CFGFLAG_CLIENT, "") +MACRO_CONFIG_INT(DbgTuning, dbg_tuning, 0, 0, 1, CFGFLAG_CLIENT, "") +#endif diff --git a/src/game/variables.hpp b/src/game/variables.hpp deleted file mode 100644 index e0d9fe31..00000000 --- a/src/game/variables.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - - -/* client */ -MACRO_CONFIG_INT(cl_predict, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict client movements") -MACRO_CONFIG_INT(cl_nameplates, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show nameplates") -MACRO_CONFIG_INT(cl_nameplates_always, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Always show nameplats disregarding of distance") -MACRO_CONFIG_INT(cl_autoswitch_weapons, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Auto switch weapon on pickup") - -MACRO_CONFIG_INT(cl_showfps, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ingame FPS counter") - -MACRO_CONFIG_INT(cl_airjumpindicator, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") -MACRO_CONFIG_INT(cl_threadsoundloading, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") - -MACRO_CONFIG_INT(cl_warning_teambalance, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Warn about team balance") - -MACRO_CONFIG_INT(cl_mouse_deadzone, 300, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") -MACRO_CONFIG_INT(cl_mouse_followfactor, 60, 0, 200, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") -MACRO_CONFIG_INT(cl_mouse_max_distance, 800, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") - -MACRO_CONFIG_INT(ed_showkeys, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") - -MACRO_CONFIG_INT(cl_flow, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") - -MACRO_CONFIG_INT(cl_show_welcome, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "") -MACRO_CONFIG_INT(cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT|CFGFLAG_SAVE, "How long to show the server message of the day") - -MACRO_CONFIG_STR(cl_version_server, 100, "version.teeworlds.com", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Server to use to check for new versions") - -MACRO_CONFIG_STR(cl_languagefile, 255, "", CFGFLAG_CLIENT|CFGFLAG_SAVE, "What language file to use") - -MACRO_CONFIG_INT(player_use_custom_color, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toggles usage of custom colors") -MACRO_CONFIG_INT(player_color_body, 65408, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player body color") -MACRO_CONFIG_INT(player_color_feet, 65408, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player feet color") -MACRO_CONFIG_STR(player_skin, 64, "default", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Player skin") - -MACRO_CONFIG_INT(ui_page, 5, 0, 9, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page") -MACRO_CONFIG_STR(ui_server_address, 128, "localhost:8303", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface server address") -MACRO_CONFIG_INT(ui_scale, 100, 1, 100000, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface scale") - -MACRO_CONFIG_INT(ui_color_hue, 160, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color hue") -MACRO_CONFIG_INT(ui_color_sat, 70, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color saturation") -MACRO_CONFIG_INT(ui_color_lht, 175, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface color lightness") -MACRO_CONFIG_INT(ui_color_alpha, 228, 0, 255, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface alpha") - -MACRO_CONFIG_INT(gfx_noclip, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Disable clipping") - -/* server */ -MACRO_CONFIG_INT(sv_warmup, 0, 0, 0, CFGFLAG_SERVER, "Number of seconds to do warpup before round starts") -MACRO_CONFIG_STR(sv_motd, 900, "", CFGFLAG_SERVER, "Message of the day to display for the clients") -MACRO_CONFIG_INT(sv_teamdamage, 0, 0, 1, CFGFLAG_SERVER, "Team damage") -MACRO_CONFIG_STR(sv_maprotation, 768, "", CFGFLAG_SERVER, "Maps to rotate between") -MACRO_CONFIG_INT(sv_rounds_per_map, 1, 1, 100, CFGFLAG_SERVER, "Number of rounds on each map before rotating") -MACRO_CONFIG_INT(sv_powerups, 1, 0, 1, CFGFLAG_SERVER, "Allow powerups like ninja") -MACRO_CONFIG_INT(sv_scorelimit, 20, 0, 1000, CFGFLAG_SERVER, "Score limit (0 disables)") -MACRO_CONFIG_INT(sv_timelimit, 0, 0, 1000, CFGFLAG_SERVER, "Time limit in minutes (0 disables)") -MACRO_CONFIG_STR(sv_gametype, 32, "dm", CFGFLAG_SERVER, "Game type (dm, tdm, ctf)") -MACRO_CONFIG_INT(sv_tournament_mode, 0, 0, 1, CFGFLAG_SERVER, "Tournament mode. When enabled, players joins the server as spectator") -MACRO_CONFIG_INT(sv_spamprotection, 1, 0, 1, CFGFLAG_SERVER, "Spam protection") - -MACRO_CONFIG_INT(sv_spectator_slots, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Number of slots to reserve for spectators") -MACRO_CONFIG_INT(sv_teambalance_time, 1, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before autobalancing teams") - -MACRO_CONFIG_INT(sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players") -MACRO_CONFIG_INT(sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick") -MACRO_CONFIG_INT(sv_vote_scorelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change score limit") -MACRO_CONFIG_INT(sv_vote_timelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change time limit") - -/* debug */ -#ifdef CONF_DEBUG /* this one can crash the server if not used correctly */ - MACRO_CONFIG_INT(dbg_dummies, 0, 0, 15, CFGFLAG_SERVER, "") -#endif - -MACRO_CONFIG_INT(dbg_focus, 0, 0, 1, CFGFLAG_CLIENT, "") -MACRO_CONFIG_INT(dbg_tuning, 0, 0, 1, CFGFLAG_CLIENT, "") diff --git a/src/game/version.h b/src/game/version.h new file mode 100644 index 00000000..0fe22c95 --- /dev/null +++ b/src/game/version.h @@ -0,0 +1,8 @@ +#ifndef GAME_VERSION_H +#define GAME_VERSION_H +#include "generated/nethash.c" +#define GAME_VERSION "trunk" +//#define GAME_NETVERSION "0.5 " GAME_NETVERSION_HASH +// TODO: hash forced during refactoring. Remove later on +#define GAME_NETVERSION "0.5 b67d1f1a1eea234e" +#endif diff --git a/src/game/version.hpp b/src/game/version.hpp deleted file mode 100644 index 2692752c..00000000 --- a/src/game/version.hpp +++ /dev/null @@ -1,4 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "generated/nethash.c" -#define GAME_VERSION "trunk" -#define GAME_NETVERSION "0.5 " GAME_NETVERSION_HASH diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp index 34acf448..8e838086 100644 --- a/src/mastersrv/mastersrv.cpp +++ b/src/mastersrv/mastersrv.cpp @@ -1,10 +1,6 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> - -extern "C" { - #include <engine/e_network.h> -} +#include <engine/shared/network.h> #include "mastersrv.h" @@ -16,333 +12,338 @@ enum { EXPIRE_TIME = 90 }; -static struct CHECK_SERVER +struct CCheckServer { - NETADDR address; - NETADDR alt_address; - int try_count; - int64 try_time; -} check_servers[MAX_SERVERS]; -static int num_checkservers = 0; + NETADDR m_Address; + NETADDR m_AltAddress; + int m_TryCount; + int64 m_TryTime; +}; +static CCheckServer m_aCheckServers[MAX_SERVERS]; +static int m_NumCheckServers = 0; -typedef struct NETADDR_IPv4 +struct NETADDR_IPv4 { - unsigned char ip[4]; - unsigned short port; -} NETADDR_IPv4; + unsigned char m_aIp[4]; + unsigned short m_Port; +}; -static struct SERVER_ENTRY +struct CServerEntry { - NETADDR address; - int64 expire; -} servers[MAX_SERVERS]; -static int num_servers = 0; + NETADDR m_Address; + int64 m_Expire; +}; + +static CServerEntry m_aServers[MAX_SERVERS]; +static int m_NumServers = 0; -static struct PACKET_DATA +struct CPacketData { - int size; + int m_Size; struct { - unsigned char header[sizeof(SERVERBROWSE_LIST)]; - NETADDR_IPv4 servers[MAX_SERVERS_PER_PACKET]; - } data; -} packets[MAX_PACKETS]; -static int num_packets = 0; + unsigned char m_aHeader[sizeof(SERVERBROWSE_LIST)]; + NETADDR_IPv4 m_aServers[MAX_SERVERS_PER_PACKET]; + } m_Data; +}; -static struct COUNT_PACKET_DATA +CPacketData m_aPackets[MAX_PACKETS]; +static int m_NumPackets = 0; + +struct CCountPacketData { - unsigned char header[sizeof(SERVERBROWSE_COUNT)]; - unsigned char high; - unsigned char low; -} count_data; + unsigned char m_Header[sizeof(SERVERBROWSE_COUNT)]; + unsigned char m_High; + unsigned char m_Low; +}; + +static CCountPacketData m_CountData; //static int64 server_expire[MAX_SERVERS]; -static net_client net_checker; // NAT/FW checker -static net_client net_op; // main +static CNetClient m_NetChecker; // NAT/FW checker +static CNetClient m_NetOp; // main -void build_packets() +void BuildPackets() { - SERVER_ENTRY *current = &servers[0]; - int servers_left = num_servers; - int i; - num_packets = 0; - while(servers_left && num_packets < MAX_PACKETS) + CServerEntry *pCurrent = &m_aServers[0]; + int ServersLeft = m_NumServers; + m_NumPackets = 0; + while(ServersLeft && m_NumPackets < MAX_PACKETS) { - int chunk = servers_left; - if(chunk > MAX_SERVERS_PER_PACKET) - chunk = MAX_SERVERS_PER_PACKET; - servers_left -= chunk; + int Chunk = ServersLeft; + if(Chunk > MAX_SERVERS_PER_PACKET) + Chunk = MAX_SERVERS_PER_PACKET; + ServersLeft -= Chunk; // copy header - mem_copy(packets[num_packets].data.header, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)); + mem_copy(m_aPackets[m_NumPackets].m_Data.m_aHeader, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)); // copy server addresses - for(i = 0; i < chunk; i++) + for(int i = 0; i < Chunk; i++) { // TODO: ipv6 support - packets[num_packets].data.servers[i].ip[0] = current->address.ip[0]; - packets[num_packets].data.servers[i].ip[1] = current->address.ip[1]; - packets[num_packets].data.servers[i].ip[2] = current->address.ip[2]; - packets[num_packets].data.servers[i].ip[3] = current->address.ip[3]; - packets[num_packets].data.servers[i].port = current->address.port; - current++; + m_aPackets[m_NumPackets].m_Data.m_aServers[i].m_aIp[0] = pCurrent->m_Address.ip[0]; + m_aPackets[m_NumPackets].m_Data.m_aServers[i].m_aIp[1] = pCurrent->m_Address.ip[1]; + m_aPackets[m_NumPackets].m_Data.m_aServers[i].m_aIp[2] = pCurrent->m_Address.ip[2]; + m_aPackets[m_NumPackets].m_Data.m_aServers[i].m_aIp[3] = pCurrent->m_Address.ip[3]; + m_aPackets[m_NumPackets].m_Data.m_aServers[i].m_Port = pCurrent->m_Address.port; + pCurrent++; } - packets[num_packets].size = sizeof(SERVERBROWSE_LIST) + sizeof(NETADDR_IPv4)*chunk; + m_aPackets[m_NumPackets].m_Size = sizeof(SERVERBROWSE_LIST) + sizeof(NETADDR_IPv4)*Chunk; - num_packets++; + m_NumPackets++; } } -void send_ok(NETADDR *addr) +void SendOk(NETADDR *pAddr) { - NETCHUNK p; - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_FWOK); - p.data = SERVERBROWSE_FWOK; + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = *pAddr; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_DataSize = sizeof(SERVERBROWSE_FWOK); + p.m_pData = SERVERBROWSE_FWOK; // send on both to be sure - net_checker.send(&p); - net_op.send(&p); + m_NetChecker.Send(&p); + m_NetOp.Send(&p); } -void send_error(NETADDR *addr) +void SendError(NETADDR *pAddr) { - NETCHUNK p; - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_FWERROR); - p.data = SERVERBROWSE_FWERROR; - net_op.send(&p); + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = *pAddr; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_DataSize = sizeof(SERVERBROWSE_FWERROR); + p.m_pData = SERVERBROWSE_FWERROR; + m_NetOp.Send(&p); } -void send_check(NETADDR *addr) +void SendCheck(NETADDR *pAddr) { - NETCHUNK p; - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_FWCHECK); - p.data = SERVERBROWSE_FWCHECK; - net_checker.send(&p); + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = *pAddr; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_DataSize = sizeof(SERVERBROWSE_FWCHECK); + p.m_pData = SERVERBROWSE_FWCHECK; + m_NetChecker.Send(&p); } -void add_checkserver(NETADDR *info, NETADDR *alt) +void AddCheckserver(NETADDR *pInfo, NETADDR *pAlt) { // add server - if(num_checkservers == MAX_SERVERS) + if(m_NumCheckServers == MAX_SERVERS) { dbg_msg("mastersrv", "error: mastersrv is full"); return; } dbg_msg("mastersrv", "checking: %d.%d.%d.%d:%d (%d.%d.%d.%d:%d)", - info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port, - alt->ip[0], alt->ip[1], alt->ip[2], alt->ip[3], alt->port); - check_servers[num_checkservers].address = *info; - check_servers[num_checkservers].alt_address = *alt; - check_servers[num_checkservers].try_count = 0; - check_servers[num_checkservers].try_time = 0; - num_checkservers++; + pInfo->ip[0], pInfo->ip[1], pInfo->ip[2], pInfo->ip[3], pInfo->port, + pAlt->ip[0], pAlt->ip[1], pAlt->ip[2], pAlt->ip[3], pAlt->port); + m_aCheckServers[m_NumCheckServers].m_Address = *pInfo; + m_aCheckServers[m_NumCheckServers].m_AltAddress = *pAlt; + m_aCheckServers[m_NumCheckServers].m_TryCount = 0; + m_aCheckServers[m_NumCheckServers].m_TryTime = 0; + m_NumCheckServers++; } -void add_server(NETADDR *info) +void AddServer(NETADDR *pInfo) { // see if server already exists in list int i; - for(i = 0; i < num_servers; i++) + for(i = 0; i < m_NumServers; i++) { - if(net_addr_comp(&servers[i].address, info) == 0) + if(net_addr_comp(&m_aServers[i].m_Address, pInfo) == 0) { dbg_msg("mastersrv", "updated: %d.%d.%d.%d:%d", - info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port); - servers[i].expire = time_get()+time_freq()*EXPIRE_TIME; + pInfo->ip[0], pInfo->ip[1], pInfo->ip[2], pInfo->ip[3], pInfo->port); + m_aServers[i].m_Expire = time_get()+time_freq()*EXPIRE_TIME; return; } } // add server - if(num_servers == MAX_SERVERS) + if(m_NumServers == MAX_SERVERS) { dbg_msg("mastersrv", "error: mastersrv is full"); return; } dbg_msg("mastersrv", "added: %d.%d.%d.%d:%d", - info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port); - servers[num_servers].address = *info; - servers[num_servers].expire = time_get()+time_freq()*EXPIRE_TIME; - num_servers++; + pInfo->ip[0], pInfo->ip[1], pInfo->ip[2], pInfo->ip[3], pInfo->port); + m_aServers[m_NumServers].m_Address = *pInfo; + m_aServers[m_NumServers].m_Expire = time_get()+time_freq()*EXPIRE_TIME; + m_NumServers++; } -void update_servers() +void UpdateServers() { - int64 now = time_get(); - int64 freq = time_freq(); - for(int i = 0; i < num_checkservers; i++) + int64 Now = time_get(); + int64 Freq = time_freq(); + for(int i = 0; i < m_NumCheckServers; i++) { - if(now > check_servers[i].try_time+freq) + if(Now > m_aCheckServers[i].m_TryTime+Freq) { - if(check_servers[i].try_count == 10) + if(m_aCheckServers[i].m_TryCount == 10) { dbg_msg("mastersrv", "check failed: %d.%d.%d.%d:%d", - check_servers[i].address.ip[0], check_servers[i].address.ip[1], - check_servers[i].address.ip[2], check_servers[i].address.ip[3],check_servers[i].address.port, - check_servers[i].alt_address.ip[0], check_servers[i].alt_address.ip[1], - check_servers[i].alt_address.ip[2], check_servers[i].alt_address.ip[3],check_servers[i].alt_address.port); + m_aCheckServers[i].m_Address.ip[0], m_aCheckServers[i].m_Address.ip[1], + m_aCheckServers[i].m_Address.ip[2], m_aCheckServers[i].m_Address.ip[3],m_aCheckServers[i].m_Address.port, + m_aCheckServers[i].m_AltAddress.ip[0], m_aCheckServers[i].m_AltAddress.ip[1], + m_aCheckServers[i].m_AltAddress.ip[2], m_aCheckServers[i].m_AltAddress.ip[3],m_aCheckServers[i].m_AltAddress.port); // FAIL!! - send_error(&check_servers[i].address); - check_servers[i] = check_servers[num_checkservers-1]; - num_checkservers--; + SendError(&m_aCheckServers[i].m_Address); + m_aCheckServers[i] = m_aCheckServers[m_NumCheckServers-1]; + m_NumCheckServers--; i--; } else { - check_servers[i].try_count++; - check_servers[i].try_time = now; - if(check_servers[i].try_count&1) - send_check(&check_servers[i].address); + m_aCheckServers[i].m_TryCount++; + m_aCheckServers[i].m_TryTime = Now; + if(m_aCheckServers[i].m_TryCount&1) + SendCheck(&m_aCheckServers[i].m_Address); else - send_check(&check_servers[i].alt_address); + SendCheck(&m_aCheckServers[i].m_AltAddress); } } } } -void purge_servers() +void PurgeServers() { - int64 now = time_get(); + int64 Now = time_get(); int i = 0; - while(i < num_servers) + while(i < m_NumServers) { - if(servers[i].expire < now) + if(m_aServers[i].m_Expire < Now) { // remove server dbg_msg("mastersrv", "expired: %d.%d.%d.%d:%d", - servers[i].address.ip[0], servers[i].address.ip[1], - servers[i].address.ip[2], servers[i].address.ip[3], servers[i].address.port); - servers[i] = servers[num_servers-1]; - num_servers--; + m_aServers[i].m_Address.ip[0], m_aServers[i].m_Address.ip[1], + m_aServers[i].m_Address.ip[2], m_aServers[i].m_Address.ip[3], m_aServers[i].m_Address.port); + m_aServers[i] = m_aServers[m_NumServers-1]; + m_NumServers--; } else i++; } } -int main(int argc, char **argv) +int main(int argc, char **argv) // ignore_convention { - int64 last_build = 0; - NETADDR bindaddr; + int64 LastBuild = 0; + NETADDR BindAddr; dbg_logger_stdout(); net_init(); - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = MASTERSERVER_PORT; - - - net_op.open(bindaddr, 0); + mem_zero(&BindAddr, sizeof(BindAddr)); + BindAddr.port = MASTERSERVER_PORT; + + m_NetOp.Open(BindAddr, 0); - bindaddr.port = MASTERSERVER_PORT+1; - net_checker.open(bindaddr, 0); + BindAddr.port = MASTERSERVER_PORT+1; + m_NetChecker.Open(BindAddr, 0); // TODO: check socket for errors //mem_copy(data.header, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)); - mem_copy(count_data.header, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)); + mem_copy(m_CountData.m_Header, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)); dbg_msg("mastersrv", "started"); while(1) { - net_op.update(); - net_checker.update(); + m_NetOp.Update(); + m_NetChecker.Update(); - // process packets - NETCHUNK packet; - while(net_op.recv(&packet)) + // process m_aPackets + CNetChunk Packet; + while(m_NetOp.Recv(&Packet)) { - if(packet.data_size == sizeof(SERVERBROWSE_HEARTBEAT)+2 && - memcmp(packet.data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)) == 0) + if(Packet.m_DataSize == sizeof(SERVERBROWSE_HEARTBEAT)+2 && + mem_comp(Packet.m_pData, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)) == 0) { - NETADDR alt; - unsigned char *d = (unsigned char *)packet.data; - alt = packet.address; - alt.port = + NETADDR Alt; + unsigned char *d = (unsigned char *)Packet.m_pData; + Alt = Packet.m_Address; + Alt.port = (d[sizeof(SERVERBROWSE_HEARTBEAT)]<<8) | d[sizeof(SERVERBROWSE_HEARTBEAT)+1]; // add it - add_checkserver(&packet.address, &alt); + AddCheckserver(&Packet.m_Address, &Alt); } - else if(packet.data_size == sizeof(SERVERBROWSE_GETCOUNT) && - memcmp(packet.data, SERVERBROWSE_GETCOUNT, sizeof(SERVERBROWSE_GETCOUNT)) == 0) + else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETCOUNT) && + mem_comp(Packet.m_pData, SERVERBROWSE_GETCOUNT, sizeof(SERVERBROWSE_GETCOUNT)) == 0) { - dbg_msg("mastersrv", "count requested, responding with %d", num_servers); + dbg_msg("mastersrv", "count requested, responding with %d", m_NumServers); - NETCHUNK p; - p.client_id = -1; - p.address = packet.address; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = sizeof(count_data); - p.data = &count_data; - count_data.high = (num_servers>>8)&0xff; - count_data.low = num_servers&0xff; - net_op.send(&p); + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = Packet.m_Address; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_DataSize = sizeof(m_CountData); + p.m_pData = &m_CountData; + m_CountData.m_High = (m_NumServers>>8)&0xff; + m_CountData.m_Low = m_NumServers&0xff; + m_NetOp.Send(&p); } - else if(packet.data_size == sizeof(SERVERBROWSE_GETLIST) && - memcmp(packet.data, SERVERBROWSE_GETLIST, sizeof(SERVERBROWSE_GETLIST)) == 0) + else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETLIST) && + mem_comp(Packet.m_pData, SERVERBROWSE_GETLIST, sizeof(SERVERBROWSE_GETLIST)) == 0) { // someone requested the list - dbg_msg("mastersrv", "requested, responding with %d servers", num_servers); - NETCHUNK p; - p.client_id = -1; - p.address = packet.address; - p.flags = NETSENDFLAG_CONNLESS; + dbg_msg("mastersrv", "requested, responding with %d m_aServers", m_NumServers); + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = Packet.m_Address; + p.m_Flags = NETSENDFLAG_CONNLESS; - for(int i = 0; i < num_packets; i++) + for(int i = 0; i < m_NumPackets; i++) { - p.data_size = packets[i].size; - p.data = &packets[i].data; - net_op.send(&p); + p.m_DataSize = m_aPackets[i].m_Size; + p.m_pData = &m_aPackets[i].m_Data; + m_NetOp.Send(&p); } } } - // process packets - while(net_checker.recv(&packet)) + // process m_aPackets + while(m_NetChecker.Recv(&Packet)) { - if(packet.data_size == sizeof(SERVERBROWSE_FWRESPONSE) && - memcmp(packet.data, SERVERBROWSE_FWRESPONSE, sizeof(SERVERBROWSE_FWRESPONSE)) == 0) + if(Packet.m_DataSize == sizeof(SERVERBROWSE_FWRESPONSE) && + mem_comp(Packet.m_pData, SERVERBROWSE_FWRESPONSE, sizeof(SERVERBROWSE_FWRESPONSE)) == 0) { // remove it from checking - for(int i = 0; i < num_checkservers; i++) + for(int i = 0; i < m_NumCheckServers; i++) { - if(net_addr_comp(&check_servers[i].address, &packet.address) == 0 || - net_addr_comp(&check_servers[i].alt_address, &packet.address) == 0) + if(net_addr_comp(&m_aCheckServers[i].m_Address, &Packet.m_Address) == 0 || + net_addr_comp(&m_aCheckServers[i].m_AltAddress, &Packet.m_Address) == 0) { - num_checkservers--; - check_servers[i] = check_servers[num_checkservers]; + m_NumCheckServers--; + m_aCheckServers[i] = m_aCheckServers[m_NumCheckServers]; break; } } - add_server(&packet.address); - send_ok(&packet.address); + AddServer(&Packet.m_Address); + SendOk(&Packet.m_Address); } } - if(time_get()-last_build > time_freq()*5) + if(time_get()-LastBuild > time_freq()*5) { - last_build = time_get(); + LastBuild = time_get(); - purge_servers(); - update_servers(); - build_packets(); + PurgeServers(); + UpdateServers(); + BuildPackets(); } // be nice to the CPU diff --git a/src/mastersrv/mastersrv.h b/src/mastersrv/mastersrv.h index cfac2e71..50fea646 100644 --- a/src/mastersrv/mastersrv.h +++ b/src/mastersrv/mastersrv.h @@ -1,10 +1,11 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef MASTERSRV_MASTERSRV_H +#define MASTERSRV_MASTERSRV_H static const int MASTERSERVER_PORT = 8300; typedef struct MASTERSRV_ADDR { - unsigned char ip[4]; - unsigned char port[2]; + unsigned char m_aIp[4]; + unsigned char m_aPort[2]; } MASTERSRV_ADDR; static const unsigned char SERVERBROWSE_HEARTBEAT[] = {255, 255, 255, 255, 'b', 'e', 'a', 't'}; @@ -25,3 +26,4 @@ static const unsigned char SERVERBROWSE_FWCHECK[] = {255, 255, 255, 255, 'f', 'w static const unsigned char SERVERBROWSE_FWRESPONSE[] = {255, 255, 255, 255, 'f', 'w', '!', '!'}; static const unsigned char SERVERBROWSE_FWOK[] = {255, 255, 255, 255, 'f', 'w', 'o', 'k'}; static const unsigned char SERVERBROWSE_FWERROR[] = {255, 255, 255, 255, 'f', 'w', 'e', 'r'}; +#endif diff --git a/src/osxlaunch/client.h b/src/osxlaunch/client.h index 4683df57..be59d0b9 100644 --- a/src/osxlaunch/client.h +++ b/src/osxlaunch/client.h @@ -1,3 +1,5 @@ +#ifndef OSXLAUNCH_CLIENT_H +#define OSXLAUNCH_CLIENT_H /* SDLMain.m - main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser <dwaliss1@purdue.edu> Non-NIB-Code & other changes: Max Horn <max@quendi.de> @@ -9,3 +11,4 @@ @interface SDLMain : NSObject @end +#endif diff --git a/src/tools/crapnet.cpp b/src/tools/crapnet.cpp index 888f3b2e..e05cc237 100644 --- a/src/tools/crapnet.cpp +++ b/src/tools/crapnet.cpp @@ -1,167 +1,167 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> #include <cstdlib> -struct PACKET +struct CPacket { - PACKET *prev; - PACKET *next; + CPacket *m_pPrev; + CPacket *m_pNext; - NETADDR send_to; - int64 timestamp; - int id; - int data_size; - char data[1]; + NETADDR m_SendTo; + int64 m_Timestamp; + int m_Id; + int m_DataSize; + char m_aData[1]; }; -static PACKET *first = (PACKET *)0; -static PACKET *last = (PACKET *)0; -static int current_latency = 0; +static CPacket *m_pFirst = (CPacket *)0; +static CPacket *m_pLast = (CPacket *)0; +static int m_CurrentLatency = 0; -struct PINGCONFIG +struct CPingConfig { - int base; - int flux; - int spike; - int loss; - int delay; - int delay_freq; + int m_Base; + int m_Flux; + int m_Spike; + int m_Loss; + int m_Delay; + int m_DelayFreq; }; -static PINGCONFIG config_pings[] = { +static CPingConfig m_aConfigPings[] = { // base flux spike loss delay delayfreq {0, 0, 0, 0, 0, 0}, {40, 20, 100, 0, 0, 0}, {140, 40, 200, 0, 0, 0}, }; -static int config_numpingconfs = sizeof(config_pings)/sizeof(PINGCONFIG); -static int config_interval = 10; /* seconds between different pingconfigs */ -static int config_log = 0; -static int config_reorder = 0; +static int m_ConfigNumpingconfs = sizeof(m_aConfigPings)/sizeof(CPingConfig); +static int m_ConfigInterval = 10; // seconds between different pingconfigs +static int m_ConfigLog = 0; +static int m_ConfigReorder = 0; -int run(int port, NETADDR dest) +void Run(int Port, NETADDR Dest) { - NETADDR src = {NETTYPE_IPV4, {0,0,0,0},port}; - NETSOCKET socket = net_udp_create(src); + NETADDR Src = {NETTYPE_IPV4, {0,0,0,0}, Port}; + NETSOCKET Socket = net_udp_create(Src); - char buffer[1024*2]; - int id = 0; - int delaycounter = 0; + char aBuffer[1024*2]; + int Id = 0; + int Delaycounter = 0; while(1) { - static int lastcfg = 0; - int n = ((time_get()/time_freq())/config_interval) % config_numpingconfs; - PINGCONFIG ping = config_pings[n]; + static int Lastcfg = 0; + int n = ((time_get()/time_freq())/m_ConfigInterval) % m_ConfigNumpingconfs; + CPingConfig Ping = m_aConfigPings[n]; - if(n != lastcfg) + if(n != Lastcfg) dbg_msg("crapnet", "cfg = %d", n); - lastcfg = n; + Lastcfg = n; // handle incomming packets while(1) { // fetch data - int data_trash = 0; - NETADDR from; - int bytes = net_udp_recv(socket, &from, buffer, 1024*2); - if(bytes <= 0) + int DataTrash = 0; + NETADDR From; + int Bytes = net_udp_recv(Socket, &From, aBuffer, 1024*2); + if(Bytes <= 0) break; - if((rand()%100) < ping.loss) // drop the packet + if((rand()%100) < Ping.m_Loss) // drop the packet { - if(config_log) + if(m_ConfigLog) dbg_msg("crapnet", "dropped packet"); continue; } // create new packet - PACKET *p = (PACKET *)mem_alloc(sizeof(PACKET)+bytes, 1); + CPacket *p = (CPacket *)mem_alloc(sizeof(CPacket)+Bytes, 1); - if(net_addr_comp(&from, &dest) == 0) - p->send_to = src; // from the server + if(net_addr_comp(&From, &Dest) == 0) + p->m_SendTo = Src; // from the server else { - src = from; // from the client - p->send_to = dest; + Src = From; // from the client + p->m_SendTo = Dest; } // queue packet - p->prev = last; - p->next = 0; - if(last) - last->next = p; + p->m_pPrev = m_pLast; + p->m_pNext = 0; + if(m_pLast) + m_pLast->m_pNext = p; else { - first = p; - last = p; + m_pFirst = p; + m_pLast = p; } - last = p; + m_pLast = p; // set data in packet - p->timestamp = time_get(); - p->data_size = bytes; - p->id = id++; - mem_copy(p->data, buffer, bytes); + p->m_Timestamp = time_get(); + p->m_DataSize = Bytes; + p->m_Id = Id++; + mem_copy(p->m_aData, aBuffer, Bytes); - if(id > 20 && bytes > 6 && data_trash) + if(Id > 20 && Bytes > 6 && DataTrash) { - p->data[6+(rand()%(bytes-6))] = rand()&255; // modify a byte + p->m_aData[6+(rand()%(Bytes-6))] = rand()&255; // modify a byte if((rand()%10) == 0) { - p->data_size -= rand()%32; - if(p->data_size < 6) - p->data_size = 6; + p->m_DataSize -= rand()%32; + if(p->m_DataSize < 6) + p->m_DataSize = 6; } } - if(delaycounter <= 0) + if(Delaycounter <= 0) { - if(ping.delay) - p->timestamp += (time_freq()*1000)/ping.delay; - delaycounter = ping.delay_freq; + if(Ping.m_Delay) + p->m_Timestamp += (time_freq()*1000)/Ping.m_Delay; + Delaycounter = Ping.m_DelayFreq; } - delaycounter--; + Delaycounter--; - if(config_log) - dbg_msg("crapnet", "<< %08d %d.%d.%d.%d:%5d (%d)", p->id, from.ip[0], from.ip[1], from.ip[2], from.ip[3], from.port, p->data_size); + if(m_ConfigLog) + dbg_msg("crapnet", "<< %08d %d.%d.%d.%d:%5d (%d)", p->m_Id, From.ip[0], From.ip[1], From.ip[2], From.ip[3], From.port, p->m_DataSize); } // /*while(1) {*/ - PACKET *p = 0; - PACKET *next = first; + CPacket *p = 0; + CPacket *pNext = m_pFirst; while(1) { - p = next; + p = pNext; if(!p) break; - next = p->next; + pNext = p->m_pNext; - if((time_get()-p->timestamp) > current_latency) + if((time_get()-p->m_Timestamp) > m_CurrentLatency) { - char flags[] = " "; + char aFlags[] = " "; - if(config_reorder && (rand()%2) == 0 && p->next) + if(m_ConfigReorder && (rand()%2) == 0 && p->m_pNext) { - flags[0] = 'R'; - p = first->next; + aFlags[0] = 'R'; + p = m_pFirst->m_pNext; } - if(p->next) - p->next->prev = p->prev; + if(p->m_pNext) + p->m_pNext->m_pPrev = p->m_pPrev; else - last = p->prev; + m_pLast = p->m_pPrev; - if(p->prev) - p->prev->next = p->next; + if(p->m_pPrev) + p->m_pPrev->m_pNext = p->m_pNext; else - first = p->next; + m_pFirst = p->m_pNext; - /*PACKET *cur = first; + /*CPacket *cur = first; while(cur) { dbg_assert(cur != p, "p still in list"); @@ -170,27 +170,27 @@ int run(int port, NETADDR dest) // send and remove packet //if((rand()%20) != 0) // heavy packetloss - net_udp_send(socket, &p->send_to, p->data, p->data_size); + net_udp_send(Socket, &p->m_SendTo, p->m_aData, p->m_DataSize); // update lag - double flux = rand()/(double)RAND_MAX; - int ms_spike = ping.spike; - int ms_flux = ping.flux; - int ms_ping = ping.base; - current_latency = ((time_freq()*ms_ping)/1000) + (int64)(((time_freq()*ms_flux)/1000)*flux); // 50ms + double Flux = rand()/(double)RAND_MAX; + int MsSpike = Ping.m_Spike; + int MsFlux = Ping.m_Flux; + int MsPing = Ping.m_Base; + m_CurrentLatency = ((time_freq()*MsPing)/1000) + (int64)(((time_freq()*MsFlux)/1000)*Flux); // 50ms - if(ms_spike && (p->id%100) == 0) + if(MsSpike && (p->m_Id%100) == 0) { - current_latency += (time_freq()*ms_spike)/1000; - flags[1] = 'S'; + m_CurrentLatency += (time_freq()*MsSpike)/1000; + aFlags[1] = 'S'; } - if(config_log) + if(m_ConfigLog) { - dbg_msg("crapnet", ">> %08d %d.%d.%d.%d:%5d (%d) %s", p->id, - p->send_to.ip[0], p->send_to.ip[1], - p->send_to.ip[2], p->send_to.ip[3], - p->send_to.port, p->data_size, flags); + dbg_msg("crapnet", ">> %08d %d.%d.%d.%d:%5d (%d) %s", p->m_Id, + p->m_SendTo.ip[0], p->m_SendTo.ip[1], + p->m_SendTo.ip[2], p->m_SendTo.ip[3], + p->m_SendTo.port, p->m_DataSize, aFlags); } @@ -202,10 +202,10 @@ int run(int port, NETADDR dest) } } -int main(int argc, char **argv) +int main(int argc, char **argv) // ignore_convention { - NETADDR a = {NETTYPE_IPV4, {127,0,0,1},8303}; + NETADDR Addr = {NETTYPE_IPV4, {127,0,0,1},8303}; dbg_logger_stdout(); - run(8302, a); + Run(8302, Addr); return 0; } diff --git a/src/tools/dilate.c b/src/tools/dilate.c index 57dce705..a8b28fc4 100644 --- a/src/tools/dilate.c +++ b/src/tools/dilate.c @@ -1,6 +1,6 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "../engine/external/pnglite/pnglite.c" +#include "./engine/external/pnglite/pnglite.c" typedef struct pixel_t { diff --git a/src/tools/fake_server.c b/src/tools/fake_server.c deleted file mode 100644 index 04eeac4d..00000000 --- a/src/tools/fake_server.c +++ /dev/null @@ -1,232 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> -#include <engine/e_config.h> -#include <engine/e_network.h> -#include <mastersrv/mastersrv.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -/* - 32 version - 64 servername - 32 mapname - 2 gametype - 2 flags - 4 progression - 3 num players - 3 max players - { - 48 name - 6 score - } * players - - 111111111122222222223333333333444444444 -123456789012345678901234567890123456789012345678 -0.3 2d82e361de24cb25 -my own private little server -magnus.auvinen@teeworlds.somehost-strage-host.com -*/ - -NETSERVER *net; - -int progression = 50; -int game_type = 0; -int flags = 0; - -const char *version = "0.3.0 2d82e361de24cb25"; -const char *map = "somemap"; -const char *server_name = "unnamed server"; -NETADDR master_servers[16] = {{0,{0},0}}; -int num_masters = 0; - -const char *player_names[16] = {0}; -int player_scores[16] = {0}; -int num_players = 0; -int max_players = 0; - - - -static void send_heartbeats() -{ - static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; - NETCHUNK packet; - int i; - - mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); - - packet.client_id = -1; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2; - packet.data = &data; - - /* supply the set port that the master can use if it has problems */ - data[sizeof(SERVERBROWSE_HEARTBEAT)] = 0; - data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = 0; - - for(i = 0; i < num_masters; i++) - { - packet.address = master_servers[i]; - netserver_send(net, &packet); - } -} - -char infomsg[1024]; -int infomsg_size; - -static void writestr(const char *str) -{ - int l = strlen(str)+1; - memcpy(&infomsg[infomsg_size], str, l); - infomsg_size += l; -} - -static void writeint(int i) -{ - char buf[64]; - sprintf(buf, "%d", i); - writestr(buf); -} - -static void build_infomessage() -{ - int i; - infomsg_size = sizeof(SERVERBROWSE_OLD_INFO); - memcpy(infomsg, SERVERBROWSE_OLD_INFO, infomsg_size); - - writestr(version); - writestr(server_name); - writestr(map); - writeint(game_type); - writeint(flags); - writeint(progression); - writeint(num_players); - writeint(max_players); - for(i = 0; i < num_players; i++) - { - writestr(player_names[i]); - writeint(player_scores[i]); - } -} - -static void send_serverinfo(NETADDR *addr) -{ - NETCHUNK p; - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = infomsg_size; - p.data = infomsg; - netserver_send(net, &p); -} - -static void send_fwcheckresponse(NETADDR *addr) -{ - NETCHUNK p; - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_FWRESPONSE); - p.data = SERVERBROWSE_FWRESPONSE; - netserver_send(net, &p); -} - -static int run() -{ - int64 next_heartbeat = 0; - NETADDR bindaddr = {NETTYPE_IPV4, {0},0}; - net = netserver_open(bindaddr, 0, 0); - if(!net) - return -1; - - while(1) - { - NETCHUNK p; - netserver_update(net); - while(netserver_recv(net, &p)) - { - if(p.client_id == -1) - { - if(p.data_size == sizeof(SERVERBROWSE_OLD_GETINFO) && - memcmp(p.data, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) - { - send_serverinfo(&p.address); - } - else if(p.data_size == sizeof(SERVERBROWSE_FWCHECK) && - memcmp(p.data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) - { - send_fwcheckresponse(&p.address); - } - } - } - - /* send heartbeats if needed */ - if(next_heartbeat < time_get()) - { - next_heartbeat = time_get()+time_freq()*(15+(rand()%15)); - send_heartbeats(); - } - - thread_sleep(100); - } -} - -int main(int argc, char **argv) -{ - net_init(); - - while(argc) - { - if(strcmp(*argv, "-m") == 0) - { - argc--; argv++; - net_host_lookup(*argv, &master_servers[num_masters], NETTYPE_IPV4); - argc--; argv++; - master_servers[num_masters].port = atoi(*argv); - num_masters++; - } - else if(strcmp(*argv, "-p") == 0) - { - argc--; argv++; - player_names[num_players++] = *argv; - argc--; argv++; - player_scores[num_players] = atoi(*argv); - } - else if(strcmp(*argv, "-a") == 0) - { - argc--; argv++; - map = *argv; - } - else if(strcmp(*argv, "-x") == 0) - { - argc--; argv++; - max_players = atoi(*argv); - } - else if(strcmp(*argv, "-t") == 0) - { - argc--; argv++; - game_type = atoi(*argv); - } - else if(strcmp(*argv, "-g") == 0) - { - argc--; argv++; - progression = atoi(*argv); - } - else if(strcmp(*argv, "-f") == 0) - { - argc--; argv++; - flags = atoi(*argv); - } - else if(strcmp(*argv, "-n") == 0) - { - argc--; argv++; - server_name = *argv; - } - - argc--; argv++; - } - - build_infomessage(); - return run(); -} - diff --git a/src/tools/fake_server.cpp b/src/tools/fake_server.cpp new file mode 100644 index 00000000..b833c1b1 --- /dev/null +++ b/src/tools/fake_server.cpp @@ -0,0 +1,211 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include <stdlib.h> //rand +#include <base/system.h> +#include <engine/shared/config.h> +#include <engine/shared/network.h> +#include <mastersrv/mastersrv.h> + +CNetServer *pNet; + +int Progression = 50; +int GameType = 0; +int Flags = 0; + +const char *pVersion = "trunk"; +const char *pMap = "somemap"; +const char *pServerName = "unnamed server"; + +NETADDR aMasterServers[16] = {{0,{0},0}}; +int NumMasters = 0; + +const char *PlayerNames[16] = {0}; +int PlayerScores[16] = {0}; +int NumPlayers = 0; +int MaxPlayers = 0; + +char aInfoMsg[1024]; +int aInfoMsgSize; + +static void SendHeartBeats() +{ + static unsigned char aData[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; + CNetChunk Packet; + + mem_copy(aData, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); + + Packet.m_ClientID = -1; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2; + Packet.m_pData = &aData; + + /* supply the set port that the master can use if it has problems */ + aData[sizeof(SERVERBROWSE_HEARTBEAT)] = 0; + aData[sizeof(SERVERBROWSE_HEARTBEAT)+1] = 0; + + for(int i = 0; i < NumMasters; i++) + { + Packet.m_Address = aMasterServers[i]; + pNet->Send(&Packet); + } +} + +static void WriteStr(const char *pStr) +{ + int l = str_length(pStr)+1; + mem_copy(&aInfoMsg[aInfoMsgSize], pStr, l); + aInfoMsgSize += l; +} + +static void WriteInt(int i) +{ + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%d", i); + WriteStr(aBuf); +} + +static void BuildInfoMsg() +{ + aInfoMsgSize = sizeof(SERVERBROWSE_OLD_INFO); + mem_copy(aInfoMsg, SERVERBROWSE_OLD_INFO, aInfoMsgSize); + + WriteStr(pVersion); + WriteStr(pServerName); + WriteStr(pMap); + WriteInt(GameType); + WriteInt(Flags); + WriteInt(Progression); + WriteInt(NumPlayers); + WriteInt(MaxPlayers); + + for(int i = 0; i < NumPlayers; i++) + { + WriteStr(PlayerNames[i]); + WriteInt(PlayerScores[i]); + } +} + +static void SendServerInfo(NETADDR *pAddr) +{ + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = *pAddr; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_DataSize = aInfoMsgSize; + p.m_pData = aInfoMsg; + pNet->Send(&p); +} + +static void SendFWCheckResponse(NETADDR *pAddr) +{ + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = *pAddr; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE); + p.m_pData = SERVERBROWSE_FWRESPONSE; + pNet->Send(&p); +} + +static int Run() +{ + int64 NextHeartBeat = 0; + NETADDR BindAddr = {NETTYPE_IPV4, {0},0}; + + if(!pNet->Open(BindAddr, 0, 0)) + return 0; + + while(1) + { + CNetChunk p; + pNet->Update(); + while(pNet->Recv(&p)) + { + if(p.m_ClientID == -1) + { + if(p.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && + mem_comp(p.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) + { + SendServerInfo(&p.m_Address); + } + else if(p.m_DataSize == sizeof(SERVERBROWSE_FWCHECK) && + mem_comp(p.m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) + { + SendFWCheckResponse(&p.m_Address); + } + } + } + + /* send heartbeats if needed */ + if(NextHeartBeat < time_get()) + { + NextHeartBeat = time_get()+time_freq()*(15+(rand()%15)); + SendHeartBeats(); + } + + thread_sleep(100); + } +} + +int main(int argc, char **argv) +{ + pNet = new CNetServer; + + while(argc) + { + // ? + /*if(str_comp(*argv, "-m") == 0) + { + argc--; argv++; + net_host_lookup(*argv, &aMasterServers[NumMasters], NETTYPE_IPV4); + argc--; argv++; + aMasterServers[NumMasters].port = str_toint(*argv); + NumMasters++; + } + else */if(str_comp(*argv, "-p") == 0) + { + argc--; argv++; + PlayerNames[NumPlayers++] = *argv; + argc--; argv++; + PlayerScores[NumPlayers] = str_toint(*argv); + } + else if(str_comp(*argv, "-a") == 0) + { + argc--; argv++; + pMap = *argv; + } + else if(str_comp(*argv, "-x") == 0) + { + argc--; argv++; + MaxPlayers = str_toint(*argv); + } + else if(str_comp(*argv, "-t") == 0) + { + argc--; argv++; + GameType = str_toint(*argv); + } + else if(str_comp(*argv, "-g") == 0) + { + argc--; argv++; + Progression = str_toint(*argv); + } + else if(str_comp(*argv, "-f") == 0) + { + argc--; argv++; + Flags = str_toint(*argv); + } + else if(str_comp(*argv, "-n") == 0) + { + argc--; argv++; + pServerName = *argv; + } + + argc--; argv++; + } + + BuildInfoMsg(); + int RunReturn = Run(); + + delete pNet; + return RunReturn; +} + diff --git a/src/tools/map_resave.c b/src/tools/map_resave.c deleted file mode 100644 index 37e00904..00000000 --- a/src/tools/map_resave.c +++ /dev/null @@ -1,36 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <engine/e_datafile.h> - -int main(int argc, char **argv) -{ - int i, id, type, size; - void *ptr; - DATAFILE *df; - DATAFILE_OUT *df_out; - - if(argc != 3) - return -1; - - df = datafile_load(argv[1]); - df_out = datafile_create(argv[2]); - - /* add all items */ - for(i = 0; i < datafile_num_items(df); i++) - { - ptr = datafile_get_item(df, i, &type, &id); - size = datafile_get_itemsize(df, i); - datafile_add_item(df_out, type, id, size, ptr); - } - - /* add all data */ - for(i = 0; i < datafile_num_data(df); i++) - { - ptr = datafile_get_data(df, i); - size = datafile_get_datasize(df, i); - datafile_add_data(df_out, size, ptr); - } - - datafile_unload(df); - datafile_finish(df_out); - return 0; -} diff --git a/src/tools/map_resave.cpp b/src/tools/map_resave.cpp new file mode 100644 index 00000000..a6c55c43 --- /dev/null +++ b/src/tools/map_resave.cpp @@ -0,0 +1,44 @@ +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <base/system.h> +#include <engine/shared/datafile.h> +#include <engine/storage.h> + +int main(int argc, const char **argv) +{ + IStorage *pStorage = CreateStorage("Teeworlds", argv[0]); + int Index, Id = 0, Type = 0, Size; + void *pPtr; + char aFileName[1024]; + CDataFileReader DataFile; + CDataFileWriter df; + + if(argc != 3) + return -1; + + str_format(aFileName, sizeof(aFileName), "maps/%s", argv[2]); + + if(!DataFile.Open(pStorage, argv[1])) + return -1; + if(!df.Open(pStorage, aFileName)) + return -1; + + // add all items + for(Index = 0; Index < DataFile.NumItems(); Index++) + { + pPtr = DataFile.GetItem(Index, &Type, &Id); + Size = DataFile.GetItemSize(Index); + df.AddItem(Type, Id, Size, pPtr); + } + + // add all data + for(Index = 0; Index < DataFile.NumData(); Index++) + { + pPtr = DataFile.GetData(Index); + Size = DataFile.GetDataSize(Index); + df.AddData(Size, pPtr); + } + + DataFile.Close(); + df.Finish(); + return 0; +} diff --git a/src/tools/tileset_borderfix.c b/src/tools/tileset_borderfix.c index 945f2832..90e512eb 100644 --- a/src/tools/tileset_borderfix.c +++ b/src/tools/tileset_borderfix.c @@ -1,6 +1,6 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "../engine/external/pnglite/pnglite.c" +#include "./engine/external/pnglite/pnglite.c" typedef struct pixel_t { diff --git a/src/versionsrv/versionsrv.cpp b/src/versionsrv/versionsrv.cpp index ed96023f..8c0801e7 100644 --- a/src/versionsrv/versionsrv.cpp +++ b/src/versionsrv/versionsrv.cpp @@ -1,57 +1,54 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> +// copyright (c) 2007 magnus auvinen, see licence.txt for more info #include <base/system.h> -extern "C" { - #include <engine/e_network.h> -} +#include <engine/shared/network.h> #include "versionsrv.h" -static net_client net_op; // main +static CNetClient g_NetOp; // main -void send_ver(NETADDR *addr) +void SendVer(NETADDR *pAddr) { - NETCHUNK p; - unsigned char data[sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)]; + CNetChunk p; + unsigned char aData[sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)]; - memcpy(data, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)); - memcpy(data + sizeof(VERSIONSRV_VERSION), VERSION_DATA, sizeof(VERSION_DATA)); + mem_copy(aData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)); + mem_copy(aData + sizeof(VERSIONSRV_VERSION), VERSION_DATA, sizeof(VERSION_DATA)); - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; - p.data = data; - p.data_size = sizeof(data); + p.m_ClientID = -1; + p.m_Address = *pAddr; + p.m_Flags = NETSENDFLAG_CONNLESS; + p.m_pData = aData; + p.m_DataSize = sizeof(aData); - net_op.send(&p); + g_NetOp.Send(&p); } -int main(int argc, char **argv) +int main(int argc, char **argv) // ignore_convention { - NETADDR bindaddr; + NETADDR BindAddr; dbg_logger_stdout(); net_init(); - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = VERSIONSRV_PORT; - net_op.open(bindaddr, 0); + mem_zero(&BindAddr, sizeof(BindAddr)); + BindAddr.port = VERSIONSRV_PORT; + g_NetOp.Open(BindAddr, 0); dbg_msg("versionsrv", "started"); while(1) { - net_op.update(); + g_NetOp.Update(); // process packets - NETCHUNK packet; - while(net_op.recv(&packet)) + CNetChunk Packet; + while(g_NetOp.Recv(&Packet)) { - if(packet.data_size == sizeof(VERSIONSRV_GETVERSION) && - memcmp(packet.data, VERSIONSRV_GETVERSION, sizeof(VERSIONSRV_GETVERSION)) == 0) + if(Packet.m_DataSize == sizeof(VERSIONSRV_GETVERSION) && + mem_comp(Packet.m_pData, VERSIONSRV_GETVERSION, sizeof(VERSIONSRV_GETVERSION)) == 0) { - send_ver(&packet.address); + SendVer(&Packet.m_Address); } } diff --git a/src/versionsrv/versionsrv.h b/src/versionsrv/versionsrv.h index 298f6c27..d458a54c 100644 --- a/src/versionsrv/versionsrv.h +++ b/src/versionsrv/versionsrv.h @@ -1,7 +1,9 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef VERSIONSRV_VERSIONSRV_H +#define VERSIONSRV_VERSIONSRV_H static const int VERSIONSRV_PORT = 8302; static const unsigned char VERSION_DATA[] = {0x00, 0, 5, 1}; static const unsigned char VERSIONSRV_GETVERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 'g'}; static const unsigned char VERSIONSRV_VERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 's'}; +#endif |