]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/util.qc
Merge branch 'master' into Mario/stats_eloranking
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / util.qc
index 9cf439f52bdca0e9a33e93bb626246f8ba4a3886..183302b3a1e0460c246c9c1c8357edfbace43c53 100644 (file)
 #include "util.qh"
 
 #if defined(CSQC)
-    #include "../client/defs.qh"
     #include "constants.qh"
-       #include "../client/mutators/events.qh"
+       #include <client/mutators/_mod.qh>
     #include "mapinfo.qh"
     #include "notifications/all.qh"
+       #include "scores.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 <server/mutators/_mod.qh>
     #include "notifications/all.qh"
     #include <common/deathtypes/all.qh>
+       #include "scores.qh"
     #include "mapinfo.qh"
 #endif
 
-#ifndef MENUQC
+#ifdef SVQC
+float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) // returns the number of traces done, for benchmarking
+{
+       vector pos, dir, t;
+       float nudge;
+       entity stopentity;
+
+       //nudge = 2 * cvar("collision_impactnudge"); // why not?
+       nudge = 0.5;
+
+       dir = normalize(v2 - v1);
+
+       pos = v1 + dir * nudge;
+
+       float c;
+       c = 0;
+
+       for (;;)
+       {
+               if(pos * dir >= v2 * dir)
+               {
+                       // went too far
+                       trace_fraction = 1;
+                       trace_endpos = v2;
+                       return c;
+               }
+
+               tracebox(pos, mi, ma, v2, nomonsters, forent);
+               ++c;
+
+               if(c == 50)
+               {
+                       LOG_TRACE("When tracing from ", vtos(v1), " to ", vtos(v2));
+                       LOG_TRACE("  Nudging gets us nowhere at ", vtos(pos));
+                       LOG_TRACE("  trace_endpos is ", vtos(trace_endpos));
+                       LOG_TRACE("  trace distance is ", ftos(vlen(pos - trace_endpos)));
+               }
+
+               stopentity = trace_ent;
+
+               if(trace_startsolid)
+               {
+                       // we started inside solid.
+                       // then trace from endpos to pos
+                       t = trace_endpos;
+                       tracebox(t, mi, ma, pos, nomonsters, forent);
+                       ++c;
+                       if(trace_startsolid)
+                       {
+                               // t is still inside solid? bad
+                               // force advance, then, and retry
+                               pos = t + dir * nudge;
+
+                               // but if we hit an entity, stop RIGHT before it
+                               if(stopatentity && stopentity && stopentity != ignorestopatentity)
+                               {
+                                       trace_ent = stopentity;
+                                       trace_endpos = t;
+                                       trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
+                                       return c;
+                               }
+                       }
+                       else
+                       {
+                               // we actually LEFT solid!
+                               trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
+                               return c;
+                       }
+               }
+               else
+               {
+                       // pos is outside solid?!? but why?!? never mind, just return it.
+                       trace_endpos = pos;
+                       trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
+                       return c;
+               }
+       }
+}
+
+void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
+{
+       tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity);
+}
+#endif
+
+#ifdef GAMEQC
+/*
+==================
+findbetterlocation
+
+Returns a point at least 12 units away from walls
+(useful for explosion animations, although the blast is performed where it really happened)
+Ripped from DPMod
+==================
+*/
+vector findbetterlocation (vector org, float mindist)
+{
+       vector vec = mindist * '1 0 0';
+       int c = 0;
+       while (c < 6)
+       {
+               traceline (org, org + vec, true, NULL);
+               vec = vec * -1;
+               if (trace_fraction < 1)
+               {
+                       vector loc = trace_endpos;
+                       traceline (loc, loc + vec, true, NULL);
+                       if (trace_fraction >= 1)
+                               org = loc + vec;
+               }
+               if (c & 1)
+               {
+                       float h = vec.y;
+                       vec.y = vec.x;
+                       vec.x = vec.z;
+                       vec.z = h;
+               }
+               c = c + 1;
+       }
+
+       return org;
+}
+
 /*
 * Get "real" origin, in worldspace, even if ent is attached to something else.
 */
 vector real_origin(entity ent)
 {
-       entity e;
        vector v = ((ent.absmin + ent.absmax) * 0.5);
+       entity e = ent.tag_entity;
 
-       e = ent.tag_entity;
        while(e)
        {
                v = v + ((e.absmin + e.absmax) * 0.5);
@@ -55,8 +175,7 @@ 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)
 {
@@ -80,7 +199,6 @@ void wordwrap_sprint(entity to, string s, float l)
        return;
 }
 #endif
-#endif
 
 #ifndef SVQC
 string draw_UseSkinFor(string pic)
@@ -186,6 +304,7 @@ void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(
        }
 }
 
+#ifdef GAMEQC
 string ScoreString(int pFlags, float pValue)
 {
        string valstr;
@@ -217,6 +336,7 @@ string ScoreString(int pFlags, float pValue)
 
        return valstr;
 }
+#endif
 
 // compressed vector format:
 // like MD3, just even shorter
@@ -230,13 +350,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;
@@ -245,15 +365,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;
@@ -265,26 +385,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);
        }
 
@@ -328,7 +448,7 @@ float compressShortVector(vector vec)
 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)
        {
@@ -338,21 +458,22 @@ STATIC_INIT(compressShortVector)
 
        if(cvar("developer"))
        {
-               LOG_INFO("Verifying vector compression table...\n");
+               LOG_TRACE("Verifying vector compression table...");
                for(i = 0x0F00; i < 0xFFFF; ++i)
                        if(i != compressShortVector(decompressShortVector(i)))
                        {
-                               LOG_INFO("BROKEN vector compression: ", ftos(i));
-                               LOG_INFO(" -> ", vtos(decompressShortVector(i)));
-                               LOG_INFO(" -> ", ftos(compressShortVector(decompressShortVector(i))));
-                               LOG_INFO("\n");
-                               error("b0rk");
+                               LOG_FATALF(
+                                   "BROKEN vector compression: %s -> %s -> %s",
+                                   ftos(i),
+                                   vtos(decompressShortVector(i)),
+                                   ftos(compressShortVector(decompressShortVector(i)))
+                );
                        }
-               LOG_INFO("Done.\n");
+               LOG_TRACE("Done.");
        }
 }
 
-#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;
@@ -399,6 +520,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;
@@ -413,11 +537,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);
@@ -425,15 +549,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), " ");
@@ -448,19 +569,17 @@ string swapInPriorityList(string order, float i, float j)
        return order;
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 void get_mi_min_max(float mode)
 {
        vector mi, ma;
 
-       if(mi_shortname)
-               strunzone(mi_shortname);
-       mi_shortname = mapname;
-       if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/"))
-               mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5);
-       if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp"))
-               mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4);
-       mi_shortname = strzone(mi_shortname);
+       string s = mapname;
+       if(!strcasecmp(substring(s, 0, 5), "maps/"))
+               s = substring(s, 5, strlen(s) - 5);
+       if(!strcasecmp(substring(s, strlen(s) - 4, 4), ".bsp"))
+               s = substring(s, 0, strlen(s) - 4);
+       strcpy(mi_shortname, s);
 
 #ifdef CSQC
        mi = world.mins;
@@ -598,11 +717,11 @@ float cvar_settemp(string tmp_cvar, string tmp_value)
 
        if(!cvar_type(tmp_cvar))
        {
-               LOG_INFOF("Error: cvar %s doesn't exist!\n", tmp_cvar);
+               LOG_INFOF("Error: cvar %s doesn't exist!", tmp_cvar);
                return 0;
        }
 
-       FOREACH_ENTITY_CLASS("saved_cvar_value", it.netname == tmp_cvar,
+       IL_EACH(g_saved_cvars, it.netname == tmp_cvar,
        {
                created_saved_value = -1; // skip creation
                break; // no need to continue
@@ -612,6 +731,7 @@ float cvar_settemp(string tmp_cvar, string tmp_value)
        {
                // creating a new entity to keep track of this cvar
                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;
@@ -639,7 +759,7 @@ int cvar_settemp_restore()
                        ++j;
                }
                else
-                       LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", it.netname);
+                       LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", it.netname);
        });
 
 #else
@@ -653,13 +773,64 @@ int cvar_settemp_restore()
                        ++j;
                }
                else
-                       print(sprintf("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.", e.netname));
        }
 #endif
 
        return j;
 }
 
+bool isCaretEscaped(string theText, float pos)
+{
+       int i = 0;
+       while(pos - i >= 1 && substring(theText, pos - i - 1, 1) == "^")
+               ++i;
+       return (i & 1);
+}
+
+int skipIncompleteTag(string theText, float pos, int len)
+{
+       int tag_start = -1;
+
+       if(substring(theText, pos - 1, 1) == "^")
+       {
+               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
+       {
+               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;
+                       }
+               }
+       }
+
+       if(tag_start >= 0)
+       {
+               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 0;
+}
+
 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
 {
        // STOP.
@@ -670,57 +841,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;
 }
 
@@ -734,57 +873,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;
 }
 
@@ -794,8 +901,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;
@@ -808,14 +914,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
@@ -1083,26 +1189,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)
-       {
-               LOG_INFO("shot origin ", vtos(v), " x out of bounds\n");
-               x = bound(0, x, 255);
+       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_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)
 {
@@ -1113,7 +1218,7 @@ 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...
@@ -1143,6 +1248,8 @@ vector healtharmor_applydamage(float a, float armorblock, int deathtype, float d
        vector v;
        if (DEATH_IS(deathtype, DEATH_DROWN))  // Why should armor help here...
                armorblock = 0;
+       if (deathtype & HITTYPE_ARMORPIERCE)
+               armorblock = 0;
        v.y = bound(0, damage * armorblock, a); // save
        v.x = bound(0, damage - v.y, damage); // take
        v.z = 0;
@@ -1227,8 +1334,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
 
@@ -1293,7 +1401,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)
@@ -1304,6 +1412,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)))
@@ -1375,7 +1485,7 @@ void m_shutdown()
 {
        if(shutdown_running)
        {
-               LOG_INFO("Recursive shutdown detected! Only restoring cvars...\n");
+               LOG_INFO("Recursive shutdown detected! Only restoring cvars...");
        }
        else
        {
@@ -1386,7 +1496,7 @@ void m_shutdown()
        cvar_settemp_restore(); // this must be done LAST, but in any case
 }
 
-#ifndef MENUQC
+#ifdef GAMEQC
 .float skeleton_bones_index;
 void Skeleton_SetBones(entity e)
 {
@@ -1408,8 +1518,7 @@ void execute_next_frame()
        if(to_execute_next_frame)
        {
                localcmd("\n", to_execute_next_frame, "\n");
-               strunzone(to_execute_next_frame);
-               to_execute_next_frame = string_null;
+               strfree(to_execute_next_frame);
        }
 }
 void queue_to_execute_next_frame(string s)
@@ -1417,9 +1526,8 @@ void queue_to_execute_next_frame(string s)
        if(to_execute_next_frame)
        {
                s = strcat(s, "\n", to_execute_next_frame);
-               strunzone(to_execute_next_frame);
        }
-       to_execute_next_frame = strzone(s);
+       strcpy(to_execute_next_frame, s);
 }
 
 .float FindConnectedComponent_processing;
@@ -1464,7 +1572,7 @@ 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
@@ -1483,7 +1591,7 @@ vector animfixfps(entity e, vector a, vector b)
 }
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 Notification Announcer_PickNumber(int type, int num)
 {
     return = NULL;
@@ -1595,7 +1703,7 @@ Notification Announcer_PickNumber(int type, int num)
 }
 #endif
 
-#ifndef MENUQC
+#ifdef GAMEQC
 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
 {
        switch(nativecontents)