]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/util.qc
Merge branch 'amade/small-fixes' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / util.qc
index 9c794c0eb3d229c12627efc98d811254d506098e..91d68f1ba1313cb0a7eb543dd7b6406fafd26d7e 100644 (file)
@@ -5,19 +5,38 @@
     #include "constants.qh"
        #include "../client/mutators/events.qh"
     #include "mapinfo.qh"
-    #include "notifications.qh"
-    #include "deathtypes.qh"
+    #include "notifications/all.qh"
+    #include <common/deathtypes/all.qh>
 #elif defined(MENUQC)
 #elif defined(SVQC)
     #include "constants.qh"
     #include "../server/autocvars.qh"
     #include "../server/defs.qh"
        #include "../server/mutators/events.qh"
-    #include "notifications.qh"
-    #include "deathtypes.qh"
+    #include "notifications/all.qh"
+    #include <common/deathtypes/all.qh>
     #include "mapinfo.qh"
 #endif
 
+#ifdef GAMEQC
+/*
+* Get "real" origin, in worldspace, even if ent is attached to something else.
+*/
+vector real_origin(entity ent)
+{
+       vector v = ((ent.absmin + ent.absmax) * 0.5);
+       entity e = ent.tag_entity;
+
+       while(e)
+       {
+               v = v + ((e.absmin + e.absmax) * 0.5);
+               e = e.tag_entity;
+       }
+
+       return v;
+}
+#endif
+
 string wordwrap_buffer;
 
 void wordwrap_buffer_put(string s)
@@ -35,29 +54,30 @@ string wordwrap(string s, float l)
        return r;
 }
 
-#ifndef MENUQC
-#ifndef CSQC
+#ifdef SVQC
+entity _wordwrap_buffer_sprint_ent;
 void wordwrap_buffer_sprint(string s)
-{SELFPARAM();
+{
        wordwrap_buffer = strcat(wordwrap_buffer, s);
        if(s == "\n")
        {
-               sprint(self, wordwrap_buffer);
+               sprint(_wordwrap_buffer_sprint_ent, wordwrap_buffer);
                wordwrap_buffer = "";
        }
 }
 
-void wordwrap_sprint(string s, float l)
-{SELFPARAM();
+void wordwrap_sprint(entity to, string s, float l)
+{
        wordwrap_buffer = "";
+       _wordwrap_buffer_sprint_ent = to;
        wordwrap_cb(s, l, wordwrap_buffer_sprint);
+       _wordwrap_buffer_sprint_ent = NULL;
        if(wordwrap_buffer != "")
-               sprint(self, strcat(wordwrap_buffer, "\n"));
+               sprint(to, strcat(wordwrap_buffer, "\n"));
        wordwrap_buffer = "";
        return;
 }
 #endif
-#endif
 
 #ifndef SVQC
 string draw_UseSkinFor(string pic)
@@ -163,239 +183,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);
-}
-
-vector colormapPaletteColor(float c, bool isPants)
-{
-       switch (c)
-       {
-               case  0: return '1.000000 1.000000 1.000000';
-               case  1: return '1.000000 0.333333 0.000000';
-               case  2: return '0.000000 1.000000 0.501961';
-               case  3: return '0.000000 1.000000 0.000000';
-               case  4: return '1.000000 0.000000 0.000000';
-               case  5: return '0.000000 0.666667 1.000000';
-               case  6: return '0.000000 1.000000 1.000000';
-               case  7: return '0.501961 1.000000 0.000000';
-               case  8: return '0.501961 0.000000 1.000000';
-               case  9: return '1.000000 0.000000 1.000000';
-               case 10: return '1.000000 0.000000 0.501961';
-               case 11: return '0.000000 0.000000 1.000000';
-               case 12: return '1.000000 1.000000 0.000000';
-               case 13: return '0.000000 0.333333 1.000000';
-               case 14: return '1.000000 0.666667 0.000000';
-               case 15:
-                       if (isPants)
-                               return
-                                         '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
-                                       + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
-                                       + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
-                       else
-                               return
-                                         '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
-                                       + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
-                                       + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
-               default: return '0.000 0.000 0.000';
-       }
-}
-
-// 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;
@@ -440,13 +227,13 @@ string ScoreString(int pFlags, float pValue)
 
 float lengthLogTable[128];
 
-float invertLengthLog(float x)
+float invertLengthLog(float dist)
 {
        int l, r, m;
 
-       if(x >= lengthLogTable[127])
+       if(dist >= lengthLogTable[127])
                return 127;
-       if(x <= lengthLogTable[0])
+       if(dist <= lengthLogTable[0])
                return 0;
 
        l = 0;
@@ -455,15 +242,15 @@ float invertLengthLog(float x)
        while(r - l > 1)
        {
                m = floor((l + r) / 2);
-               if(lengthLogTable[m] < x)
+               if(lengthLogTable[m] < dist)
                        l = m;
                else
                        r = m;
        }
 
        // now: r is >=, l is <
-       float lerr = (x - lengthLogTable[l]);
-       float rerr = (lengthLogTable[r] - x);
+       float lerr = (dist - lengthLogTable[l]);
+       float rerr = (lengthLogTable[r] - dist);
        if(lerr < rerr)
                return l;
        return r;
@@ -475,26 +262,26 @@ vector decompressShortVector(int data)
        if(data == 0)
                return '0 0 0';
        float p = (data & 0xF000) / 0x1000;
-       float y = (data & 0x0F80) / 0x80;
+       float q = (data & 0x0F80) / 0x80;
        int len = (data & 0x007F);
 
-       //print("\ndecompress: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
+       //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n");
 
        if(p == 0)
        {
                out.x = 0;
                out.y = 0;
-               if(y == 31)
+               if(q == 31)
                        out.z = -1;
                else
                        out.z = +1;
        }
        else
        {
-               y   = .19634954084936207740 * y;
+               q   = .19634954084936207740 * q;
                p = .19634954084936207740 * p - 1.57079632679489661922;
-               out.x = cos(y) *  cos(p);
-               out.y = sin(y) *  cos(p);
+               out.x = cos(q) *  cos(p);
+               out.y = sin(q) *  cos(p);
                out.z =          -sin(p);
        }
 
@@ -507,7 +294,7 @@ float compressShortVector(vector vec)
 {
        vector ang;
        float p, y, len;
-       if(vlen(vec) == 0)
+       if(vec == '0 0 0')
                return 0;
        //print("compress: ", vtos(vec), "\n");
        ang = vectoangles(vec);
@@ -535,10 +322,10 @@ float compressShortVector(vector vec)
        return (p * 0x1000) + (y * 0x80) + len;
 }
 
-void compressShortVector_init()
+STATIC_INIT(compressShortVector)
 {
        float l = 1;
-       float f = pow(2, 1/8);
+       float f = (2 ** (1/8));
        int i;
        for(i = 0; i < 128; ++i)
        {
@@ -562,7 +349,7 @@ void compressShortVector_init()
        }
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
 {
        traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0;
@@ -609,6 +396,9 @@ string fixPriorityList(string order, float from, float to, float subtract, float
                n = tokenize_console(neworder);
                for(w = to; w >= from; --w)
                {
+                       int wflags = Weapons_from(w).spawnflags;
+                       if((wflags & WEP_FLAG_HIDDEN) && (wflags & WEP_FLAG_MUTATORBLOCKED) && !(wflags & WEP_FLAG_NORMAL))
+                               continue;
                        for(i = 0; i < n; ++i)
                                if(stof(argv(i)) == w)
                                        break;
@@ -623,11 +413,11 @@ string fixPriorityList(string order, float from, float to, float subtract, float
 string mapPriorityList(string order, string(string) mapfunc)
 {
        string neworder;
-       float i, n;
+       float n;
 
        n = tokenize_console(order);
        neworder = "";
-       for(i = 0; i < n; ++i)
+       for(float i = 0; i < n; ++i)
                neworder = strcat(neworder, mapfunc(argv(i)), " ");
 
        return substring(neworder, 0, strlen(neworder) - 1);
@@ -635,15 +425,12 @@ string mapPriorityList(string order, string(string) mapfunc)
 
 string swapInPriorityList(string order, float i, float j)
 {
-       string s;
-       float w, n;
-
-       n = tokenize_console(order);
+       float n = tokenize_console(order);
 
        if(i >= 0 && i < n && j >= 0 && j < n && i != j)
        {
-               s = "";
-               for(w = 0; w < n; ++w)
+               string s = "";
+               for(float w = 0; w < n; ++w)
                {
                        if(w == i)
                                s = strcat(s, argv(j), " ");
@@ -658,24 +445,7 @@ 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
+#ifdef GAMEQC
 void get_mi_min_max(float mode)
 {
        vector mi, ma;
@@ -699,7 +469,7 @@ void get_mi_min_max(float mode)
 
        mi_min = mi;
        mi_max = ma;
-       MapInfo_Get_ByName(mi_shortname, 0, 0);
+       MapInfo_Get_ByName(mi_shortname, 0, NULL);
        if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x)
        {
                mi_min = MapInfo_Map_mins;
@@ -716,7 +486,7 @@ void get_mi_min_max(float mode)
                                         '0 1 0' * ma.y + '0 0 1' * ma.z,
                                         '1 0 0' * ma.x,
                                         MOVE_WORLDONLY,
-                                        world);
+                                        NULL);
                        if(!trace_startsolid)
                                mi_min.x = trace_endpos.x;
 
@@ -725,7 +495,7 @@ void get_mi_min_max(float mode)
                                         '1 0 0' * ma.x + '0 0 1' * ma.z,
                                         '0 1 0' * ma.y,
                                         MOVE_WORLDONLY,
-                                        world);
+                                        NULL);
                        if(!trace_startsolid)
                                mi_min.y = trace_endpos.y;
 
@@ -734,7 +504,7 @@ void get_mi_min_max(float mode)
                                         '1 0 0' * ma.x + '0 1 0' * ma.y,
                                         '0 0 1' * ma.z,
                                         MOVE_WORLDONLY,
-                                        world);
+                                        NULL);
                        if(!trace_startsolid)
                                mi_min.z = trace_endpos.z;
 
@@ -743,7 +513,7 @@ void get_mi_min_max(float mode)
                                         '0 1 0' * ma.y + '0 0 1' * ma.z,
                                         '1 0 0' * mi.x,
                                         MOVE_WORLDONLY,
-                                        world);
+                                        NULL);
                        if(!trace_startsolid)
                                mi_max.x = trace_endpos.x;
 
@@ -752,7 +522,7 @@ void get_mi_min_max(float mode)
                                         '1 0 0' * ma.x + '0 0 1' * ma.z,
                                         '0 1 0' * mi.y,
                                         MOVE_WORLDONLY,
-                                        world);
+                                        NULL);
                        if(!trace_startsolid)
                                mi_max.y = trace_endpos.y;
 
@@ -761,7 +531,7 @@ void get_mi_min_max(float mode)
                                         '1 0 0' * ma.x + '0 1 0' * ma.y,
                                         '0 0 1' * mi.z,
                                         MOVE_WORLDONLY,
-                                        world);
+                                        NULL);
                        if(!trace_startsolid)
                                mi_max.z = trace_endpos.z;
                }
@@ -814,13 +584,12 @@ void get_mi_min_max_texcoords(float mode)
 float cvar_settemp(string tmp_cvar, string tmp_value)
 {
        float created_saved_value;
-       entity e;
 
        created_saved_value = 0;
 
        if (!(tmp_cvar || tmp_value))
        {
-               LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !\n");
+               LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !");
                return 0;
        }
 
@@ -830,15 +599,17 @@ float cvar_settemp(string tmp_cvar, string tmp_value)
                return 0;
        }
 
-       for(e = world; (e = find(e, classname, "saved_cvar_value")); )
-               if(e.netname == tmp_cvar)
-                       created_saved_value = -1; // skip creation
+       IL_EACH(g_saved_cvars, it.netname == tmp_cvar,
+       {
+               created_saved_value = -1; // skip creation
+               break; // no need to continue
+       });
 
        if(created_saved_value != -1)
        {
                // creating a new entity to keep track of this cvar
-               e = spawn();
-               e.classname = "saved_cvar_value";
+               entity e = new_pure(saved_cvar_value);
+               IL_PUSH(g_saved_cvars, e);
                e.netname = strzone(tmp_cvar);
                e.message = strzone(cvar_string(tmp_cvar));
                created_saved_value = 1;
@@ -850,167 +621,92 @@ float cvar_settemp(string tmp_cvar, string tmp_value)
        return created_saved_value;
 }
 
-float cvar_settemp_restore()
+int cvar_settemp_restore()
 {
-       float i = 0;
-       entity e = world;
+       int j = 0;
+       // FIXME this new-style loop fails!
+#if 0
+       FOREACH_ENTITY_CLASS("saved_cvar_value", true,
+       {
+               if(cvar_type(it.netname))
+               {
+                       cvar_set(it.netname, it.message);
+                       strunzone(it.netname);
+                       strunzone(it.message);
+                       delete(it);
+                       ++j;
+               }
+               else
+                       LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", it.netname);
+       });
+
+#else
+       entity e = NULL;
        while((e = find(e, classname, "saved_cvar_value")))
        {
                if(cvar_type(e.netname))
                {
                        cvar_set(e.netname, e.message);
-                       remove(e);
-                       ++i;
+                       delete(e);
+                       ++j;
                }
                else
-                       LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", e.netname);
+                       print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", e.netname));
        }
+#endif
 
-       return i;
+       return j;
 }
 
-float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
+bool isCaretEscaped(string theText, float pos)
 {
-       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;
+       int i = 0;
+       while(pos - i >= 1 && substring(theText, pos - i - 1, 1) == "^")
+               ++i;
+       return (i & 1);
 }
 
-vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
+int skipIncompleteTag(string theText, float pos, int len)
 {
-       vector rgb;
+       int tag_start = -1;
 
-       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)
+       if(substring(theText, pos - 1, 1) == "^")
        {
-               rgb.x = mi;
-               rgb.y = (4 - hue) * (ma - mi) + mi;
-               rgb.z = ma;
+               if(isCaretEscaped(theText, pos - 1) || pos >= len)
+                       return 0;
+
+               int ch = str2chr(theText, pos);
+               if(ch >= '0' && ch <= '9')
+                       return 1; // ^[0-9] color code found
+               else if (ch == 'x')
+                       tag_start = pos - 1; // ^x tag found
+               else
+                       return 0;
        }
-       else if(hue <= 5)
+       else
        {
-               rgb.x = (hue - 4) * (ma - mi) + mi;
-               rgb.y = mi;
-               rgb.z = ma;
+               for(int i = 2; pos - i >= 0 && i <= 4; ++i)
+               {
+                       if(substring(theText, pos - i, 2) == "^x")
+                       {
+                               tag_start = pos - i; // ^x tag found
+                               break;
+                       }
+               }
        }
-       //else if(ma == rgb_x)
-       //      hue = 60 * (rgb_y - rgb_z) / (ma - mi);
-       else // if(hue <= 6)
+
+       if(tag_start >= 0)
        {
-               rgb.x = ma;
-               rgb.y = mi;
-               rgb.z = (6 - hue) * (ma - mi) + mi;
+               if(tag_start + 5 < len)
+               if(IS_HEXDIGIT(substring(theText, tag_start + 2, 1)))
+               if(IS_HEXDIGIT(substring(theText, tag_start + 3, 1)))
+               if(IS_HEXDIGIT(substring(theText, tag_start + 4, 1)))
+               {
+                       if(!isCaretEscaped(theText, tag_start))
+                               return 5 - (pos - tag_start); // ^xRGB color code found
+               }
        }
-
-       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))
-               );
+       return 0;
 }
 
 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
@@ -1023,57 +719,25 @@ float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLe
        if(w(theText, theSize) <= maxWidth)
                return strlen(theText); // yeah!
 
+       bool colors = (w("^7", theSize) == 0);
+
        // binary search for right place to cut string
-       float ch;
-       float left, right, middle; // this always works
+       int len, left, right, middle;
        left = 0;
-       right = strlen(theText); // this always fails
+       right = len = strlen(theText);
+       int ofs = 0;
        do
        {
                middle = floor((left + right) / 2);
-               if(w(substring(theText, 0, middle), theSize) <= maxWidth)
-                       left = middle;
+               if(colors)
+                       ofs = skipIncompleteTag(theText, middle, len);
+               if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth)
+                       left = middle + ofs;
                else
                        right = middle;
        }
        while(left < right - 1);
 
-       if(w("^7", theSize) == 0) // detect color codes support in the width function
-       {
-               // NOTE: when color codes are involved, this binary search is,
-               // mathematically, BROKEN. However, it is obviously guaranteed to
-               // terminate, as the range still halves each time - but nevertheless, it is
-               // guaranteed that it finds ONE valid cutoff place (where "left" is in
-               // range, and "right" is outside).
-
-               // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
-               // and decrease left on the basis of the chars detected of the truncated tag
-               // Even if the ^xrgb tag is not complete/correct, left is decreased
-               // (sometimes too much but with a correct result)
-               // it fixes also ^[0-9]
-               while(left >= 1 && substring(theText, left-1, 1) == "^")
-                       left-=1;
-
-               if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
-                       left-=2;
-               else if (left >= 3 && substring(theText, left-3, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-1);
-                               if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
-                                       left-=3;
-                       }
-               else if (left >= 4 && substring(theText, left-4, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-2);
-                               if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
-                               {
-                                       ch = str2chr(theText, left-1);
-                                       if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
-                                               left-=4;
-                               }
-                       }
-       }
-
        return left;
 }
 
@@ -1087,57 +751,25 @@ float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_
        if(w(theText) <= maxWidth)
                return strlen(theText); // yeah!
 
+       bool colors = (w("^7") == 0);
+
        // binary search for right place to cut string
-       float ch;
-       float left, right, middle; // this always works
+       int len, left, right, middle;
        left = 0;
-       right = strlen(theText); // this always fails
+       right = len = strlen(theText);
+       int ofs = 0;
        do
        {
                middle = floor((left + right) / 2);
-               if(w(substring(theText, 0, middle)) <= maxWidth)
-                       left = middle;
+               if(colors)
+                       ofs = skipIncompleteTag(theText, middle, len);
+               if(w(substring(theText, 0, middle + ofs)) <= maxWidth)
+                       left = middle + ofs;
                else
                        right = middle;
        }
        while(left < right - 1);
 
-       if(w("^7") == 0) // detect color codes support in the width function
-       {
-               // NOTE: when color codes are involved, this binary search is,
-               // mathematically, BROKEN. However, it is obviously guaranteed to
-               // terminate, as the range still halves each time - but nevertheless, it is
-               // guaranteed that it finds ONE valid cutoff place (where "left" is in
-               // range, and "right" is outside).
-
-               // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
-               // and decrease left on the basis of the chars detected of the truncated tag
-               // Even if the ^xrgb tag is not complete/correct, left is decreased
-               // (sometimes too much but with a correct result)
-               // it fixes also ^[0-9]
-               while(left >= 1 && substring(theText, left-1, 1) == "^")
-                       left-=1;
-
-               if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
-                       left-=2;
-               else if (left >= 3 && substring(theText, left-3, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-1);
-                               if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
-                                       left-=3;
-                       }
-               else if (left >= 4 && substring(theText, left-4, 2) == "^x")
-                       {
-                               ch = str2chr(theText, left-2);
-                               if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
-                               {
-                                       ch = str2chr(theText, left-1);
-                                       if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
-                                               left-=4;
-                               }
-                       }
-       }
-
        return left;
 }
 
@@ -1147,8 +779,7 @@ string find_last_color_code(string s)
        if (start == -1) // no caret found
                return "";
        int len = strlen(s)-1;
-       int i;
-       for(i = len; i >= start; --i)
+       for(int i = len; i >= start; --i)
        {
                if(substring(s, i, 1) != "^")
                        continue;
@@ -1161,14 +792,14 @@ string find_last_color_code(string s)
                if (carets & 1)
                {
                        if(i+1 <= len)
-                       if(strstrofs("0123456789", substring(s, i+1, 1), 0) >= 0)
+                       if(IS_DIGIT(substring(s, i+1, 1)))
                                return substring(s, i, 2);
 
                        if(i+4 <= len)
                        if(substring(s, i+1, 1) == "x")
-                       if(strstrofs("0123456789abcdefABCDEF", substring(s, i+2, 1), 0) >= 0)
-                       if(strstrofs("0123456789abcdefABCDEF", substring(s, i+3, 1), 0) >= 0)
-                       if(strstrofs("0123456789abcdefABCDEF", substring(s, i+4, 1), 0) >= 0)
+                       if(IS_HEXDIGIT(substring(s, i + 2, 1)))
+                       if(IS_HEXDIGIT(substring(s, i + 3, 1)))
+                       if(IS_HEXDIGIT(substring(s, i + 4, 1)))
                                return substring(s, i, 5);
                }
                i -= carets; // this also skips one char before the carets
@@ -1285,7 +916,7 @@ string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_
                return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
 }
 
-float isGametypeInFilter(float gt, float tp, float ts, string pattern)
+float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
 {
        string subpattern, subpattern2, subpattern3, subpattern4;
        subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
@@ -1331,60 +962,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;
@@ -1490,26 +1067,25 @@ vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_styl
 
 float compressShotOrigin(vector v)
 {
-       float x, y, z;
-       x = rint(v.x * 2);
-       y = rint(v.y * 4) + 128;
-       z = rint(v.z * 4) + 128;
-       if(x > 255 || x < 0)
+       float rx = rint(v.x * 2);
+       float ry = rint(v.y * 4) + 128;
+       float rz = rint(v.z * 4) + 128;
+       if(rx > 255 || rx < 0)
        {
-               LOG_INFO("shot origin ", vtos(v), " x out of bounds\n");
-               x = bound(0, x, 255);
+               LOG_DEBUG("shot origin ", vtos(v), " x out of bounds\n");
+               rx = bound(0, rx, 255);
        }
-       if(y > 255 || y < 0)
+       if(ry > 255 || ry < 0)
        {
-               LOG_INFO("shot origin ", vtos(v), " y out of bounds\n");
-               y = bound(0, y, 255);
+               LOG_DEBUG("shot origin ", vtos(v), " y out of bounds\n");
+               ry = bound(0, ry, 255);
        }
-       if(z > 255 || z < 0)
+       if(rz > 255 || rz < 0)
        {
-               LOG_INFO("shot origin ", vtos(v), " z out of bounds\n");
-               z = bound(0, z, 255);
+               LOG_DEBUG("shot origin ", vtos(v), " z out of bounds\n");
+               rz = bound(0, rz, 255);
        }
-       return x * 0x10000 + y * 0x100 + z;
+       return rx * 0x10000 + ry * 0x100 + rz;
 }
 vector decompressShotOrigin(int f)
 {
@@ -1520,12 +1096,12 @@ vector decompressShotOrigin(int f)
        return v;
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
 {
        // NOTE: we'll always choose the SMALLER value...
        float healthdamage, armordamage, armorideal;
-       if (deathtype == DEATH_DROWN)  // Why should armor help here...
+       if (DEATH_IS(deathtype, DEATH_DROWN))  // Why should armor help here...
                armorblock = 0;
        vector v;
        healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
@@ -1548,7 +1124,7 @@ vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
 vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
 {
        vector v;
-       if (deathtype == DEATH_DROWN)  // Why should armor help here...
+       if (DEATH_IS(deathtype, DEATH_DROWN))  // Why should armor help here...
                armorblock = 0;
        v.y = bound(0, damage * armorblock, a); // save
        v.x = bound(0, damage - v.y, damage); // take
@@ -1569,27 +1145,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;
@@ -1655,8 +1210,9 @@ float get_model_parameters(string m, float sk)
                get_model_parameters_bone_aimweight[i] = 0;
        }
        get_model_parameters_fixbone = 0;
+       get_model_parameters_hidden = false;
 
-#ifndef MENUQC
+#ifdef GAMEQC
        MUTATOR_CALLHOOK(ClearModelParams);
 #endif
 
@@ -1721,7 +1277,7 @@ float get_model_parameters(string m, float sk)
                        get_model_parameters_bone_upperbody = s;
                if(c == "bone_weapon")
                        get_model_parameters_bone_weapon = s;
-       #ifndef MENUQC
+       #ifdef GAMEQC
                MUTATOR_CALLHOOK(GetModelParams, c, s);
        #endif
                for(int i = 0; i < MAX_AIM_BONES; ++i)
@@ -1732,6 +1288,8 @@ float get_model_parameters(string m, float sk)
                        }
                if(c == "fixbone")
                        get_model_parameters_fixbone = stof(s);
+               if(c == "hidden")
+                       get_model_parameters_hidden = stob(s);
        }
 
        while((s = fgets(fh)))
@@ -1747,57 +1305,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";
@@ -1841,16 +1348,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()
@@ -1870,49 +1367,12 @@ void m_shutdown()
        {
                shutdown_running = 1;
                Shutdown();
+               shutdownhooks();
        }
        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
+#ifdef GAMEQC
 .float skeleton_bones_index;
 void Skeleton_SetBones(entity e)
 {
@@ -1962,7 +1422,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
 
        // start with a 1-element queue
        queue_start = queue_end = e;
-       queue_end.(fld) = world;
+       queue_end.(fld) = NULL;
        queue_end.FindConnectedComponent_processing = 1;
 
        // for each queued item:
@@ -1970,7 +1430,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
        {
                // find all neighbors of queue_start
                entity t;
-               for(t = world; (t = nxt(t, queue_start, pass)); )
+               for(t = NULL; (t = nxt(t, queue_start, pass)); )
                {
                        if(t.FindConnectedComponent_processing)
                                continue;
@@ -1979,7 +1439,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
                                // it is connected? ADD IT. It will look for neighbors soon too.
                                queue_end.(fld) = t;
                                queue_end = t;
-                               queue_end.(fld) = world;
+                               queue_end.(fld) = NULL;
                                queue_end.FindConnectedComponent_processing = 1;
                        }
                }
@@ -1990,37 +1450,30 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
                queue_start.FindConnectedComponent_processing = 0;
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 vector animfixfps(entity e, vector a, vector b)
 {
        // multi-frame anim: keep as-is
        if(a.y == 1)
        {
-               float dur;
-               dur = frameduration(e.modelindex, a.x);
-               if(dur <= 0 && b.y)
+               float dur = frameduration(e.modelindex, a.x);
+               if (dur <= 0 && b.y)
                {
                        a = b;
                        dur = frameduration(e.modelindex, a.x);
                }
-               if(dur > 0)
+               if (dur > 0)
                        a.z = 1.0 / dur;
        }
        return a;
 }
 #endif
 
-#ifdef SVQC
-void dedicated_print(string input) // print(), but only print if the server is not local
-{
-       if(server_is_dedicated) { LOG_INFO(input); }
-}
-#endif
-
-#ifndef MENUQC
-float Announcer_PickNumber(float type, float num)
+#ifdef GAMEQC
+Notification Announcer_PickNumber(int type, int num)
 {
-       switch(type)
+    return = NULL;
+       switch (type)
        {
                case CNT_GAMESTART:
                {
@@ -2125,11 +1578,10 @@ float Announcer_PickNumber(float type, float num)
                        break;
                }
        }
-       return NOTIF_ABORT; // abort sending if none of these numbers were right
 }
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
 {
        switch(nativecontents)