From: TimePath Date: Thu, 5 Nov 2015 09:40:40 +0000 (+1100) Subject: Split up common/util.qc X-Git-Tag: xonotic-v0.8.2~1710 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=5b338ceaf351d6c94dba3919c0f70f9aab701c68;p=xonotic%2Fxonotic-data.pk3dir.git Split up common/util.qc --- diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index a6a57c7df..3e380ca69 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -163,205 +163,6 @@ void depthfirst(entity start, .entity up, .entity downleft, .entity right, void( } } -// converts a number to a string with the indicated number of decimals -// works for up to 10 decimals! -string ftos_decimals(float number, float decimals) -{ - // inhibit stupid negative zero - if(number == 0) - number = 0; - // we have sprintf... - return sprintf("%.*f", decimals, number); -} - -// Databases (hash tables) -const float DB_BUCKETS = 8192; -void db_save(float db, string pFilename) -{ - float fh, i, n; - fh = fopen(pFilename, FILE_WRITE); - if(fh < 0) - { - LOG_INFO(strcat("^1Can't write DB to ", pFilename)); - return; - } - n = buf_getsize(db); - fputs(fh, strcat(ftos(DB_BUCKETS), "\n")); - for(i = 0; i < n; ++i) - fputs(fh, strcat(bufstr_get(db, i), "\n")); - fclose(fh); -} - -int db_create() -{ - return buf_create(); -} - -int db_load(string pFilename) -{ - float db, fh, i, j, n; - string l; - db = buf_create(); - if(db < 0) - return -1; - fh = fopen(pFilename, FILE_READ); - if(fh < 0) - return db; - l = fgets(fh); - if(stof(l) == DB_BUCKETS) - { - i = 0; - while((l = fgets(fh))) - { - if(l != "") - bufstr_set(db, i, l); - ++i; - } - } - else - { - // different count of buckets, or a dump? - // need to reorganize the database then (SLOW) - // - // note: we also parse the first line (l) in case the DB file is - // missing the bucket count - do - { - n = tokenizebyseparator(l, "\\"); - for(j = 2; j < n; j += 2) - db_put(db, argv(j-1), uri_unescape(argv(j))); - } - while((l = fgets(fh))); - } - fclose(fh); - return db; -} - -void db_dump(float db, string pFilename) -{ - float fh, i, j, n, m; - fh = fopen(pFilename, FILE_WRITE); - if(fh < 0) - error(strcat("Can't dump DB to ", pFilename)); - n = buf_getsize(db); - fputs(fh, "0\n"); - for(i = 0; i < n; ++i) - { - m = tokenizebyseparator(bufstr_get(db, i), "\\"); - for(j = 2; j < m; j += 2) - fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n")); - } - fclose(fh); -} - -void db_close(float db) -{ - buf_del(db); -} - -string db_get(float db, string pKey) -{ - float h; - h = crc16(false, pKey) % DB_BUCKETS; - return uri_unescape(infoget(bufstr_get(db, h), pKey)); -} - -void db_put(float db, string pKey, string pValue) -{ - float h; - h = crc16(false, pKey) % DB_BUCKETS; - bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue))); -} - -void db_test() -{ - float db, i; - LOG_INFO("LOAD...\n"); - db = db_load("foo.db"); - LOG_INFO("LOADED. FILL...\n"); - for(i = 0; i < DB_BUCKETS; ++i) - db_put(db, ftos(random()), "X"); - LOG_INFO("FILLED. SAVE...\n"); - db_save(db, "foo.db"); - LOG_INFO("SAVED. CLOSE...\n"); - db_close(db); - LOG_INFO("CLOSED.\n"); -} - -// Multiline text file buffers -int buf_load(string pFilename) -{ - float buf, fh, i; - string l; - buf = buf_create(); - if(buf < 0) - return -1; - fh = fopen(pFilename, FILE_READ); - if(fh < 0) - { - buf_del(buf); - return -1; - } - i = 0; - while((l = fgets(fh))) - { - bufstr_set(buf, i, l); - ++i; - } - fclose(fh); - return buf; -} - -void buf_save(float buf, string pFilename) -{ - float fh, i, n; - fh = fopen(pFilename, FILE_WRITE); - if(fh < 0) - error(strcat("Can't write buf to ", pFilename)); - n = buf_getsize(buf); - for(i = 0; i < n; ++i) - fputs(fh, strcat(bufstr_get(buf, i), "\n")); - fclose(fh); -} - -string format_time(float seconds) -{ - float days, hours, minutes; - seconds = floor(seconds + 0.5); - days = floor(seconds / 864000); - seconds -= days * 864000; - hours = floor(seconds / 36000); - seconds -= hours * 36000; - minutes = floor(seconds / 600); - seconds -= minutes * 600; - if (days > 0) - return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds); - else - return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds); -} - -string mmsss(float tenths) -{ - float minutes; - string s; - tenths = floor(tenths + 0.5); - minutes = floor(tenths / 600); - tenths -= minutes * 600; - s = ftos(1000 + tenths); - return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1)); -} - -string mmssss(float hundredths) -{ - float minutes; - string s; - hundredths = floor(hundredths + 0.5); - minutes = floor(hundredths / 6000); - hundredths -= minutes * 6000; - s = ftos(10000 + hundredths); - return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2)); -} - string ScoreString(int pFlags, float pValue) { string valstr; @@ -624,23 +425,6 @@ string swapInPriorityList(string order, float i, float j) return order; } -float cvar_value_issafe(string s) -{ - if(strstrofs(s, "\"", 0) >= 0) - return 0; - if(strstrofs(s, "\\", 0) >= 0) - return 0; - if(strstrofs(s, ";", 0) >= 0) - return 0; - if(strstrofs(s, "$", 0) >= 0) - return 0; - if(strstrofs(s, "\r", 0) >= 0) - return 0; - if(strstrofs(s, "\n", 0) >= 0) - return 0; - return 1; -} - #ifndef MENUQC void get_mi_min_max(float mode) { @@ -835,150 +619,6 @@ float cvar_settemp_restore() return i; } -float rgb_mi_ma_to_hue(vector rgb, float mi, float ma) -{ - if(mi == ma) - return 0; - else if(ma == rgb.x) - { - if(rgb.y >= rgb.z) - return (rgb.y - rgb.z) / (ma - mi); - else - return (rgb.y - rgb.z) / (ma - mi) + 6; - } - else if(ma == rgb.y) - return (rgb.z - rgb.x) / (ma - mi) + 2; - else // if(ma == rgb_z) - return (rgb.x - rgb.y) / (ma - mi) + 4; -} - -vector hue_mi_ma_to_rgb(float hue, float mi, float ma) -{ - vector rgb; - - hue -= 6 * floor(hue / 6); - - //else if(ma == rgb_x) - // hue = 60 * (rgb_y - rgb_z) / (ma - mi); - if(hue <= 1) - { - rgb.x = ma; - rgb.y = hue * (ma - mi) + mi; - rgb.z = mi; - } - //else if(ma == rgb_y) - // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120; - else if(hue <= 2) - { - rgb.x = (2 - hue) * (ma - mi) + mi; - rgb.y = ma; - rgb.z = mi; - } - else if(hue <= 3) - { - rgb.x = mi; - rgb.y = ma; - rgb.z = (hue - 2) * (ma - mi) + mi; - } - //else // if(ma == rgb_z) - // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240; - else if(hue <= 4) - { - rgb.x = mi; - rgb.y = (4 - hue) * (ma - mi) + mi; - rgb.z = ma; - } - else if(hue <= 5) - { - rgb.x = (hue - 4) * (ma - mi) + mi; - rgb.y = mi; - rgb.z = ma; - } - //else if(ma == rgb_x) - // hue = 60 * (rgb_y - rgb_z) / (ma - mi); - else // if(hue <= 6) - { - rgb.x = ma; - rgb.y = mi; - rgb.z = (6 - hue) * (ma - mi) + mi; - } - - return rgb; -} - -vector rgb_to_hsv(vector rgb) -{ - float mi, ma; - vector hsv; - - mi = min(rgb.x, rgb.y, rgb.z); - ma = max(rgb.x, rgb.y, rgb.z); - - hsv.x = rgb_mi_ma_to_hue(rgb, mi, ma); - hsv.z = ma; - - if(ma == 0) - hsv.y = 0; - else - hsv.y = 1 - mi/ma; - - return hsv; -} - -vector hsv_to_rgb(vector hsv) -{ - return hue_mi_ma_to_rgb(hsv.x, hsv.z * (1 - hsv.y), hsv.z); -} - -vector rgb_to_hsl(vector rgb) -{ - float mi, ma; - vector hsl; - - mi = min(rgb.x, rgb.y, rgb.z); - ma = max(rgb.x, rgb.y, rgb.z); - - hsl.x = rgb_mi_ma_to_hue(rgb, mi, ma); - - hsl.z = 0.5 * (mi + ma); - if(mi == ma) - hsl.y = 0; - else if(hsl.z <= 0.5) - hsl.y = (ma - mi) / (2*hsl.z); - else // if(hsl_z > 0.5) - hsl.y = (ma - mi) / (2 - 2*hsl.z); - - return hsl; -} - -vector hsl_to_rgb(vector hsl) -{ - float mi, ma, maminusmi; - - if(hsl.z <= 0.5) - maminusmi = hsl.y * 2 * hsl.z; - else - maminusmi = hsl.y * (2 - 2 * hsl.z); - - // hsl_z = 0.5 * mi + 0.5 * ma - // maminusmi = - mi + ma - mi = hsl.z - 0.5 * maminusmi; - ma = hsl.z + 0.5 * maminusmi; - - return hue_mi_ma_to_rgb(hsl.x, mi, ma); -} - -string rgb_to_hexcolor(vector rgb) -{ - return - strcat( - "^x", - DEC_TO_HEXDIGIT(floor(rgb.x * 15 + 0.5)), - DEC_TO_HEXDIGIT(floor(rgb.y * 15 + 0.5)), - DEC_TO_HEXDIGIT(floor(rgb.z * 15 + 0.5)) - ); -} - float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w) { // STOP. @@ -1297,60 +937,6 @@ float isGametypeInFilter(float gt, float tp, float ts, string pattern) return 1; } -vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0 -{ - vector v; - float D; - v = '0 0 0'; - if(a == 0) - { - if(b != 0) - { - v.x = v.y = -c / b; - v.z = 1; - } - else - { - if(c == 0) - { - // actually, every number solves the equation! - v.z = 1; - } - } - } - else - { - D = b*b - 4*a*c; - if(D >= 0) - { - D = sqrt(D); - if(a > 0) // put the smaller solution first - { - v.x = ((-b)-D) / (2*a); - v.y = ((-b)+D) / (2*a); - } - else - { - v.x = (-b+D) / (2*a); - v.y = (-b-D) / (2*a); - } - v.z = 1; - } - else - { - // complex solutions! - D = sqrt(-D); - v.x = -b / (2*a); - if(a > 0) - v.y = D / (2*a); - else - v.y = -D / (2*a); - v.z = 0; - } - } - return v; -} - vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style) { vector ret; @@ -1535,27 +1121,6 @@ string getcurrentmod() return argv(n - 1); } -// from the GNU Scientific Library -float gsl_ran_gaussian_lastvalue; -float gsl_ran_gaussian_lastvalue_set; -float gsl_ran_gaussian(float sigma) -{ - float a, b; - if(gsl_ran_gaussian_lastvalue_set) - { - gsl_ran_gaussian_lastvalue_set = 0; - return sigma * gsl_ran_gaussian_lastvalue; - } - else - { - a = random() * 2 * M_PI; - b = sqrt(-2 * log(random())); - gsl_ran_gaussian_lastvalue = cos(a) * b; - gsl_ran_gaussian_lastvalue_set = 1; - return sigma * sin(a) * b; - } -} - float matchacl(string acl, string str) { string t, s; @@ -1713,57 +1278,6 @@ float get_model_parameters(string m, float sk) return 1; } -float vercmp_recursive(string v1, string v2) -{ - float dot1, dot2; - string s1, s2; - float r; - - dot1 = strstrofs(v1, ".", 0); - dot2 = strstrofs(v2, ".", 0); - if(dot1 == -1) - s1 = v1; - else - s1 = substring(v1, 0, dot1); - if(dot2 == -1) - s2 = v2; - else - s2 = substring(v2, 0, dot2); - - r = stof(s1) - stof(s2); - if(r != 0) - return r; - - r = strcasecmp(s1, s2); - if(r != 0) - return r; - - if(dot1 == -1) - if(dot2 == -1) - return 0; - else - return -1; - else - if(dot2 == -1) - return 1; - else - return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999)); -} - -float vercmp(string v1, string v2) -{ - if(strcasecmp(v1, v2) == 0) // early out check - return 0; - - // "git" beats all - if(v1 == "git") - return 1; - if(v2 == "git") - return -1; - - return vercmp_recursive(v1, v2); -} - // x-encoding (encoding as zero length invisible string) const string XENCODE_2 = "xX"; const string XENCODE_22 = "0123456789abcdefABCDEF"; @@ -1807,16 +1321,6 @@ string strlimitedlen(string input, string truncation, float strip_colors, float return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation); }*/ -#ifdef CSQC -entity ReadCSQCEntity() -{ - int f = ReadShort(); - if(f == 0) - return world; - return findfloat(world, entnum, f); -} -#endif - float shutdown_running; #ifdef SVQC void SV_Shutdown() @@ -1840,44 +1344,6 @@ void m_shutdown() cvar_settemp_restore(); // this must be done LAST, but in any case } -const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05; -#define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT) -#define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT) -// this will use the value: -// 128 -// accuracy near zero is APPROXPASTTIME_MAX/(256*255) -// accuracy at x is 1/derivative, i.e. -// APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536 -#ifdef SVQC -void WriteApproxPastTime(float dst, float t) -{ - float dt = time - t; - - // warning: this is approximate; do not resend when you don't have to! - // be careful with sendflags here! - // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75 - - // map to range... - dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt)); - - // round... - dt = rint(bound(0, dt, 255)); - - WriteByte(dst, dt); -} -#endif -#ifdef CSQC -float ReadApproxPastTime() -{ - float dt = ReadByte(); - - // map from range...PPROXPASTTIME_MAX / 256 - dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt)); - - return servertime - dt; -} -#endif - #ifndef MENUQC .float skeleton_bones_index; void Skeleton_SetBones(entity e) diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index fac4269d7..f0d180dad 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -101,11 +101,6 @@ float almost_in_bounds(float a, float b, float c); float power2of(float e); float log2of(float x); -const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef"; -#define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0)) -#define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10) -#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1)) - vector rgb_to_hsl(vector rgb); vector hsl_to_rgb(vector hsl); vector rgb_to_hsv(vector rgb); @@ -211,10 +206,6 @@ const float XENCODE_LEN = 5; string xencode(float f); float xdecode(string s); -#ifdef CSQC -entity ReadCSQCEntity(); -#endif - #ifndef MENUQC string strtolower(string s); #endif diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index 5a6b7394e..416978814 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -43,6 +43,7 @@ #include "lazy.qh" #include "linkedlist.qh" #include "log.qh" +#include "map.qc" #include "math.qh" #include "misc.qh" #include "net.qh" diff --git a/qcsrc/lib/color.qh b/qcsrc/lib/color.qh index e1748a936..442d8ee89 100644 --- a/qcsrc/lib/color.qh +++ b/qcsrc/lib/color.qh @@ -1,6 +1,8 @@ #ifndef COLOR_H #define COLOR_H +#include "string.qh" + #define colormapPaletteColor(c, isPants) colormapPaletteColor_(c, isPants, time) vector colormapPaletteColor_(int c, bool isPants, float t) { @@ -34,4 +36,145 @@ vector colormapPaletteColor_(int c, bool isPants, float t) } } +float rgb_mi_ma_to_hue(vector rgb, float mi, float ma) +{ + if (mi == ma) + { + return 0; + } + else if (ma == rgb.x) + { + if (rgb.y >= rgb.z) return (rgb.y - rgb.z) / (ma - mi); + else return (rgb.y - rgb.z) / (ma - mi) + 6; + } + else if (ma == rgb.y) + { + return (rgb.z - rgb.x) / (ma - mi) + 2; + } + else // if(ma == rgb_z) + { + return (rgb.x - rgb.y) / (ma - mi) + 4; + } +} + +vector hue_mi_ma_to_rgb(float hue, float mi, float ma) +{ + vector rgb; + + hue -= 6 * floor(hue / 6); + + // else if(ma == rgb_x) + // hue = 60 * (rgb_y - rgb_z) / (ma - mi); + if (hue <= 1) + { + rgb.x = ma; + rgb.y = hue * (ma - mi) + mi; + rgb.z = mi; + } + // else if(ma == rgb_y) + // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120; + else if (hue <= 2) + { + rgb.x = (2 - hue) * (ma - mi) + mi; + rgb.y = ma; + rgb.z = mi; + } + else if (hue <= 3) + { + rgb.x = mi; + rgb.y = ma; + rgb.z = (hue - 2) * (ma - mi) + mi; + } + // else // if(ma == rgb_z) + // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240; + else if (hue <= 4) + { + rgb.x = mi; + rgb.y = (4 - hue) * (ma - mi) + mi; + rgb.z = ma; + } + else if (hue <= 5) + { + rgb.x = (hue - 4) * (ma - mi) + mi; + rgb.y = mi; + rgb.z = ma; + } + // else if(ma == rgb_x) + // hue = 60 * (rgb_y - rgb_z) / (ma - mi); + else // if(hue <= 6) + { + rgb.x = ma; + rgb.y = mi; + rgb.z = (6 - hue) * (ma - mi) + mi; + } + + return rgb; +} + +vector rgb_to_hsv(vector rgb) +{ + float mi, ma; + vector hsv; + + mi = min(rgb.x, rgb.y, rgb.z); + ma = max(rgb.x, rgb.y, rgb.z); + + hsv.x = rgb_mi_ma_to_hue(rgb, mi, ma); + hsv.z = ma; + + if (ma == 0) hsv.y = 0; + else hsv.y = 1 - mi / ma; + + return hsv; +} + +vector hsv_to_rgb(vector hsv) +{ + return hue_mi_ma_to_rgb(hsv.x, hsv.z * (1 - hsv.y), hsv.z); +} + +vector rgb_to_hsl(vector rgb) +{ + float mi, ma; + vector hsl; + + mi = min(rgb.x, rgb.y, rgb.z); + ma = max(rgb.x, rgb.y, rgb.z); + + hsl.x = rgb_mi_ma_to_hue(rgb, mi, ma); + + hsl.z = 0.5 * (mi + ma); + if (mi == ma) hsl.y = 0; + else if (hsl.z <= 0.5) hsl.y = (ma - mi) / (2 * hsl.z); + else // if(hsl_z > 0.5) + hsl.y = (ma - mi) / (2 - 2 * hsl.z); + + return hsl; +} + +vector hsl_to_rgb(vector hsl) +{ + float mi, ma, maminusmi; + + if (hsl.z <= 0.5) maminusmi = hsl.y * 2 * hsl.z; + else maminusmi = hsl.y * (2 - 2 * hsl.z); + + // hsl_z = 0.5 * mi + 0.5 * ma + // maminusmi = - mi + ma + mi = hsl.z - 0.5 * maminusmi; + ma = hsl.z + 0.5 * maminusmi; + + return hue_mi_ma_to_rgb(hsl.x, mi, ma); +} + +string rgb_to_hexcolor(vector rgb) +{ + return strcat( + "^x", + DEC_TO_HEXDIGIT(floor(rgb.x * 15 + 0.5)), + DEC_TO_HEXDIGIT(floor(rgb.y * 15 + 0.5)), + DEC_TO_HEXDIGIT(floor(rgb.z * 15 + 0.5)) + ); +} + #endif diff --git a/qcsrc/lib/cvar.qh b/qcsrc/lib/cvar.qh index 81aaffe13..d79ae1154 100644 --- a/qcsrc/lib/cvar.qh +++ b/qcsrc/lib/cvar.qh @@ -5,7 +5,18 @@ #include "progname.qh" #include "static.qh" -void RegisterCvars(void(string name, string def, string desc, bool archive, string file)f) {} +void RegisterCvars(void(string name, string def, string desc, bool archive, string file) f) {} + +bool cvar_value_issafe(string s) +{ + if (strstrofs(s, "\"", 0) >= 0) return false; + if (strstrofs(s, "\\", 0) >= 0) return false; + if (strstrofs(s, ";", 0) >= 0) return false; + if (strstrofs(s, "$", 0) >= 0) return false; + if (strstrofs(s, "\r", 0) >= 0) return false; + if (strstrofs(s, "\n", 0) >= 0) return false; + return true; +} /** escape the string to make it safe for consoles */ string MakeConsoleSafe(string input) @@ -64,7 +75,7 @@ const noref vector default_vector = '0 0 0'; #define repr_cvar_vector(x) (sprintf("%v", x)) #define __AUTOCVAR(file, archive, var, type, desc, default) \ - [[accumulate]] void RegisterCvars(void(string, string, string, bool, string)f) \ + [[accumulate]] void RegisterCvars(void(string, string, string, bool, string) f) \ { \ f( #var, repr_cvar_##type(default), desc, archive, file); \ } \ diff --git a/qcsrc/lib/map.qc b/qcsrc/lib/map.qc new file mode 100644 index 000000000..d71c2639f --- /dev/null +++ b/qcsrc/lib/map.qc @@ -0,0 +1,107 @@ +#ifndef MAP_H +#define MAP_H + +// Databases (hash tables) +const float DB_BUCKETS = 8192; +void db_save(float db, string pFilename) +{ + int fh = fopen(pFilename, FILE_WRITE); + if (fh < 0) + { + LOG_INFO(strcat("^1Can't write DB to ", pFilename)); + return; + } + int n = buf_getsize(db); + fputs(fh, strcat(ftos(DB_BUCKETS), "\n")); + for (int i = 0; i < n; ++i) + fputs(fh, strcat(bufstr_get(db, i), "\n")); + fclose(fh); +} + +int db_create() +{ + return buf_create(); +} + +void db_put(float db, string pKey, string pValue); + +int db_load(string pFilename) +{ + int db = buf_create(); + if (db < 0) return -1; + int fh = fopen(pFilename, FILE_READ); + if (fh < 0) return db; + string l = fgets(fh); + if (stof(l) == DB_BUCKETS) + { + for (int i = 0; (l = fgets(fh)); ++i) + { + if (l != "") bufstr_set(db, i, l); + } + } + else + { + // different count of buckets, or a dump? + // need to reorganize the database then (SLOW) + // + // note: we also parse the first line (l) in case the DB file is + // missing the bucket count + do + { + int n = tokenizebyseparator(l, "\\"); + for (int j = 2; j < n; j += 2) + db_put(db, argv(j - 1), uri_unescape(argv(j))); + } + while ((l = fgets(fh))); + } + fclose(fh); + return db; +} + +void db_dump(float db, string pFilename) +{ + int fh = fopen(pFilename, FILE_WRITE); + if (fh < 0) error(strcat("Can't dump DB to ", pFilename)); + int n = buf_getsize(db); + fputs(fh, "0\n"); + for (int i = 0; i < n; ++i) + { + int m = tokenizebyseparator(bufstr_get(db, i), "\\"); + for (int j = 2; j < m; j += 2) + fputs(fh, strcat("\\", argv(j - 1), "\\", argv(j), "\n")); + } + fclose(fh); +} + +void db_close(float db) +{ + buf_del(db); +} + +string db_get(float db, string pKey) +{ + int h = crc16(false, pKey) % DB_BUCKETS; + return uri_unescape(infoget(bufstr_get(db, h), pKey)); +} + +void db_put(float db, string pKey, string pValue) +{ + int h = crc16(false, pKey) % DB_BUCKETS; + bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue))); +} + +void db_test() +{ + LOG_INFO("LOAD...\n"); + int db = db_load("foo.db"); + LOG_INFO("LOADED. FILL...\n"); + for (int i = 0; i < DB_BUCKETS; ++i) + db_put(db, ftos(random()), "X"); + LOG_INFO("FILLED. SAVE...\n"); + db_save(db, "foo.db"); + LOG_INFO("SAVED. CLOSE...\n"); + db_close(db); + LOG_INFO("CLOSED.\n"); +} + +#endif diff --git a/qcsrc/lib/math.qh b/qcsrc/lib/math.qh index ea968e84c..a32e9b47c 100644 --- a/qcsrc/lib/math.qh +++ b/qcsrc/lib/math.qh @@ -247,5 +247,57 @@ float log2of(float x) else return 0; } +/** ax^2 + bx + c = 0 */ +vector solve_quadratic(float a, float b, float c) +{ + vector v; + float D; + v = '0 0 0'; + if (a == 0) + { + if (b != 0) + { + v.x = v.y = -c / b; + v.z = 1; + } + else + { + if (c == 0) + { + // actually, every number solves the equation! + v.z = 1; + } + } + } + else + { + D = b * b - 4 * a * c; + if (D >= 0) + { + D = sqrt(D); + if (a > 0) // put the smaller solution first + { + v.x = ((-b) - D) / (2 * a); + v.y = ((-b) + D) / (2 * a); + } + else + { + v.x = (-b + D) / (2 * a); + v.y = (-b - D) / (2 * a); + } + v.z = 1; + } + else + { + // complex solutions! + D = sqrt(-D); + v.x = -b / (2 * a); + if (a > 0) v.y = D / (2 * a); + else v.y = -D / (2 * a); + v.z = 0; + } + } + return v; +} #endif diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index bcb8af632..1f8d27e80 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -4,12 +4,12 @@ #ifdef SVQC .int Version; // deprecated, use SendFlags .int SendFlags; - .bool(entity to, int sendflags)SendEntity; - .bool(entity this, entity to, int sendflags)SendEntity3; + .bool(entity to, int sendflags) SendEntity; + .bool(entity this, entity to, int sendflags) SendEntity3; bool SendEntity_self(entity to, int sendflags) { return self.SendEntity3(self, to, sendflags); } - void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags)sendfunc) + void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc) { if (e.classname == "") e.classname = "net_linked"; @@ -37,7 +37,7 @@ .void() uncustomizeentityforclient; .float uncustomizeentityforclient_set; - void SetCustomizer(entity e, float()customizer, void()uncustomizer) + void SetCustomizer(entity e, float() customizer, void() uncustomizer) { e.customizeentityforclient = customizer; e.uncustomizeentityforclient = uncustomizer; @@ -148,7 +148,17 @@ STATIC_INIT(RegisterTempEntities_renumber) } #ifndef MENUQC + const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05; + #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT) + #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT) + #ifdef CSQC + entity ReadCSQCEntity() + { + int f = ReadShort(); + if (f == 0) return world; + return findfloat(world, entnum, f); + } int ReadInt24_t() { int v = ReadShort() << 8; // note: this is signed @@ -171,6 +181,17 @@ STATIC_INIT(RegisterTempEntities_renumber) v.z = ReadInt24_t(); return v; } + + float ReadApproxPastTime() + { + float dt = ReadByte(); + + // map from range...PPROXPASTTIME_MAX / 256 + dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt)); + + return servertime - dt; + } + #else const int MSG_ENTITY = 5; @@ -192,10 +213,44 @@ STATIC_INIT(RegisterTempEntities_renumber) WriteInt24_t(dst, val.z); } + // this will use the value: + // 128 + // accuracy near zero is APPROXPASTTIME_MAX/(256*255) + // accuracy at x is 1/derivative, i.e. + // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536 + void WriteApproxPastTime(float dst, float t) + { + float dt = time - t; + + // warning: this is approximate; do not resend when you don't have to! + // be careful with sendflags here! + // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75 + + // map to range... + dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt)); + + // round... + dt = rint(bound(0, dt, 255)); + + WriteByte(dst, dt); + } + // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example) - #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname - #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement) - #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0 + #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname, statement) \ + entity varname; varname = msg_entity; \ + FOR_EACH_REALCLIENT(msg_entity) \ + if (msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) \ + statement msg_entity = varname + #define WRITESPECTATABLE_MSG_ONE(statement) \ + do \ + { \ + WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement); \ + } \ + while (0) + #define WRITESPECTATABLE(msg, statement) \ + if (msg == MSG_ONE) WRITESPECTATABLE_MSG_ONE(statement); \ + else \ + statement float WRITESPECTATABLE_workaround = 0 #endif #endif diff --git a/qcsrc/lib/oo.qh b/qcsrc/lib/oo.qh index 1b4b6bbba..a0d6f35db 100644 --- a/qcsrc/lib/oo.qh +++ b/qcsrc/lib/oo.qh @@ -3,6 +3,7 @@ #include "misc.qh" #include "nil.qh" +#include "static.qh" #ifdef MENUQC #define NULL (null_entity) diff --git a/qcsrc/lib/random.qc b/qcsrc/lib/random.qc index be0f80a08..aff961c55 100644 --- a/qcsrc/lib/random.qc +++ b/qcsrc/lib/random.qc @@ -66,6 +66,26 @@ float DistributeEvenly_GetRandomized(float weight) return f; } +// from the GNU Scientific Library +float gsl_ran_gaussian_lastvalue; +float gsl_ran_gaussian_lastvalue_set; +float gsl_ran_gaussian(float sigma) +{ + if (gsl_ran_gaussian_lastvalue_set) + { + gsl_ran_gaussian_lastvalue_set = 0; + return sigma * gsl_ran_gaussian_lastvalue; + } + else + { + float a = random() * 2 * M_PI; + float b = sqrt(-2 * log(random())); + gsl_ran_gaussian_lastvalue = cos(a) * b; + gsl_ran_gaussian_lastvalue_set = 1; + return sigma * sin(a) * b; + } +} + // prandom - PREDICTABLE random number generator (not seeded yet) #ifdef USE_PRANDOM diff --git a/qcsrc/lib/string.qh b/qcsrc/lib/string.qh index 048b5c721..6f6115580 100644 --- a/qcsrc/lib/string.qh +++ b/qcsrc/lib/string.qh @@ -1,6 +1,10 @@ #ifndef STRING_H #define STRING_H +#include "nil.qh" +#include "sort.qh" +#include "oo.qh" + #ifndef SVQC float stringwidth_colors(string s, vector theSize) { @@ -13,8 +17,6 @@ } #endif -// Timer (#5) -// // TODO: macro string seconds_tostring(float sec) { @@ -23,6 +25,37 @@ string seconds_tostring(float sec) return sprintf("%d:%02d", minutes, sec); } +string format_time(float seconds) +{ + seconds = floor(seconds + 0.5); + float days = floor(seconds / 864000); + seconds -= days * 864000; + float hours = floor(seconds / 36000); + seconds -= hours * 36000; + float minutes = floor(seconds / 600); + seconds -= minutes * 600; + if (days > 0) return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds); + else return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds); +} + +string mmsss(float tenths) +{ + tenths = floor(tenths + 0.5); + float minutes = floor(tenths / 600); + tenths -= minutes * 600; + string s = ftos(1000 + tenths); + return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1)); +} + +string mmssss(float hundredths) +{ + hundredths = floor(hundredths + 0.5); + float minutes = floor(hundredths / 6000); + hundredths -= minutes * 6000; + string s = ftos(10000 + hundredths); + return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2)); +} + int ColorTranslateMode; string ColorTranslateRGB(string s) @@ -209,4 +242,79 @@ bool isInvisibleString(string s) return true; } +// Multiline text file buffers + +int buf_load(string pFilename) +{ + int buf = buf_create(); + if (buf < 0) return -1; + int fh = fopen(pFilename, FILE_READ); + if (fh < 0) + { + buf_del(buf); + return -1; + } + string l; + for (int i = 0; (l = fgets(fh)); ++i) + bufstr_set(buf, i, l); + fclose(fh); + return buf; +} + +void buf_save(float buf, string pFilename) +{ + int fh = fopen(pFilename, FILE_WRITE); + if (fh < 0) error(strcat("Can't write buf to ", pFilename)); + int n = buf_getsize(buf); + for (int i = 0; i < n; ++i) + fputs(fh, strcat(bufstr_get(buf, i), "\n")); + fclose(fh); +} + +/** + * converts a number to a string with the indicated number of decimals + * works for up to 10 decimals! + */ +string ftos_decimals(float number, int decimals) +{ + // inhibit stupid negative zero + if (number == 0) number = 0; + // we have sprintf... + return sprintf("%.*f", decimals, number); +} + +int vercmp_recursive(string v1, string v2) +{ + int dot1 = strstrofs(v1, ".", 0); + int dot2 = strstrofs(v2, ".", 0); + string s1 = (dot1 == -1) ? v1 : substring(v1, 0, dot1); + string s2 = (dot2 == -1) ? v2 : substring(v2, 0, dot2); + + float r; + r = stof(s1) - stof(s2); + if (r != 0) return r; + + r = strcasecmp(s1, s2); + if (r != 0) return r; + + if (dot1 == -1) return (dot2 == -1) ? 0 : -1; + else return (dot2 == -1) ? 1 : vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999)); +} + +int vercmp(string v1, string v2) +{ + if (strcasecmp(v1, v2) == 0) return 0; // early out check + + // "git" beats all + if (v1 == "git") return 1; + if (v2 == "git") return -1; + + return vercmp_recursive(v1, v2); +} + +const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef"; +#define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0)) +#define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10) +#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1)) + #endif diff --git a/qcsrc/menu/classes.qc b/qcsrc/menu/classes.qc new file mode 100644 index 000000000..9454a3113 --- /dev/null +++ b/qcsrc/menu/classes.qc @@ -0,0 +1,9 @@ +#ifndef CLASSES_H +#define CLASSES_H + +#include "classes.inc" +#define IMPLEMENTATION +#include "classes.inc" +#undef IMPLEMENTATION + +#endif diff --git a/qcsrc/menu/command/menu_cmd.qc b/qcsrc/menu/command/menu_cmd.qc index 6293241f1..5a5fe26a6 100644 --- a/qcsrc/menu/command/menu_cmd.qc +++ b/qcsrc/menu/command/menu_cmd.qc @@ -1,7 +1,7 @@ #include "menu_cmd.qh" #include "../menu.qh" -#include "../oo/classes.qc" +#include "../classes.qc" #include "../mutators/events.qh" diff --git a/qcsrc/menu/draw.qc b/qcsrc/menu/draw.qc index 7edc6eff5..b2ce50382 100644 --- a/qcsrc/menu/draw.qc +++ b/qcsrc/menu/draw.qc @@ -27,16 +27,6 @@ void draw_reset(float cw, float ch, float ox, float oy) draw_endBoldFont(); } -void draw_beginBoldFont() -{ - drawfont = FONT_USER+3; -} - -void draw_endBoldFont() -{ - drawfont = FONT_USER+0; -} - vector globalToBox(vector v, vector theOrigin, vector theScale) { v -= theOrigin; diff --git a/qcsrc/menu/draw.qh b/qcsrc/menu/draw.qh index 873ccb6df..bb12e29c8 100644 --- a/qcsrc/menu/draw.qh +++ b/qcsrc/menu/draw.qh @@ -10,8 +10,8 @@ vector draw_scale; float draw_alpha; void draw_reset(float cw, float ch, float ox, float oy); -void draw_beginBoldFont(); -void draw_endBoldFont(); +#define draw_beginBoldFont() do { drawfont = FONT_USER + 3; } while (0) +#define draw_endBoldFont() do { drawfont = FONT_USER + 0; } while (0) void draw_setMousePointer(string pic, vector theSize, vector theOffset); void draw_drawMousePointer(vector where); diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index bd5a24fee..ad2b000f9 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -1,5 +1,5 @@ #include "menu.qh" -#include "oo/classes.qc" +#include "classes.qc" #include "xonotic/util.qh" #include "../common/items/all.qh" diff --git a/qcsrc/menu/oo/classes.qc b/qcsrc/menu/oo/classes.qc deleted file mode 100644 index 16a830da0..000000000 --- a/qcsrc/menu/oo/classes.qc +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CLASSES_H -#define CLASSES_H - -#include "../classes.inc" -#define IMPLEMENTATION -#include "../classes.inc" -#undef IMPLEMENTATION - -#endif diff --git a/qcsrc/menu/progs.inc b/qcsrc/menu/progs.inc index ae221de27..5282a0188 100644 --- a/qcsrc/menu/progs.inc +++ b/qcsrc/menu/progs.inc @@ -2,7 +2,7 @@ #define world NULL -#include "oo/classes.qc" +#include "classes.qc" #include "draw.qc" #include "menu.qc"