]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/util.qc
Merge MR 'Various Q3 and QL map entity features and fixes'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / util.qc
index 8dd6c341b3208d61e2c2275da60c0fea53a908f7..ca120d29535b154ecaeb4e9855cd8a5fc297183e 100644 (file)
@@ -210,6 +210,70 @@ string draw_UseSkinFor(string pic)
        else
                return strcat(draw_currentSkin, "/", pic);
 }
+
+void mut_set_active(int mut)
+{
+       if (mut >= 24)
+               active_mutators[1] |= BIT(mut - 24);
+       else
+               active_mutators[0] |= BIT(mut);
+}
+
+bool mut_is_active(int mut)
+{
+       if (mut >= 24)
+               return (active_mutators[1] & (BIT(mut - 24)));
+       else
+               return (active_mutators[0] & BIT(mut));
+}
+
+// if s == "" (MENUQC) builds the mutator list for the Mutators dialog based on local cvar values
+// otherwise (CSQC) translates the mutator list (s) that client has received from server
+// NOTE: this function merges MENUQC and CSQC code in order to avoid duplicating and separating strings
+string build_mutator_list(string s)
+{
+       int i = -1, n = 0; // allow only 1 iteration in the following for loop if (s == "")
+       if (s != "")
+       {
+               i = 0;
+               n = tokenizebyseparator(s, ", ");
+       }
+       string s2 = "";
+       for (string arg = ""; i < n; ++i)
+       {
+               if (i >= 0) arg = argv(i);
+               // cond is the condition for showing the mutator enabled in the menu
+               #define X(name, translated_name, mut, cond) \
+                       if(arg == name || (!n && (cond))) { s2 = cons_mid(s2, ", ", translated_name); mut_set_active(mut); }
+               X("Dodging"                   , _("Dodging")                   , MUT_DODGING                   , cvar("g_dodging"))
+               X("InstaGib"                  , _("InstaGib")                  , MUT_INSTAGIB                  , cvar("g_instagib"))
+               X("New Toys"                  , _("New Toys")                  , MUT_NEW_TOYS                  , cvar("g_new_toys"))
+               X("NIX"                       , _("NIX")                       , MUT_NIX                       , cvar("g_nix"))
+               X("Rocket Flying"             , _("Rocket Flying")             , MUT_ROCKET_FLYING             , cvar("g_rocket_flying"))
+               X("Invincible Projectiles"    , _("Invincible Projectiles")    , MUT_INVINCIBLE_PROJECTILES    , cvar("g_invincible_projectiles"))
+               X("Low gravity"               , _("Low gravity")               , MUT_GRAVITY                   , cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
+               X("Cloaked"                   , _("Cloaked")                   , MUT_CLOAKED                   , cvar("g_cloaked"))
+               X("Hook"                      , _("Hook")                      , MUT_GRAPPLING_HOOK            , cvar("g_grappling_hook"))
+               X("Midair"                    , _("Midair")                    , MUT_MIDAIR                    , cvar("g_midair"))
+               X("Melee only Arena"          , _("Melee only Arena")          , MUT_MELEE_ONLY                , cvar("g_melee_only"))
+               X("Vampire"                   , _("Vampire")                   , MUT_VAMPIRE                   , cvar("g_vampire"))
+               X("Piñata"                    , _("Piñata")                    , MUT_PINATA                    , cvar("g_pinata"))
+               X("Weapons stay"              , _("Weapons stay")              , MUT_WEAPON_STAY               , cvar("g_weapon_stay"))
+               X("Blood loss"                , _("Blood loss")                , MUT_BLOODLOSS                 , cvar("g_bloodloss") > 0)
+               X("Jetpack"                   , _("Jetpack")                   , MUT_JETPACK                   , cvar("g_jetpack"))
+               X("Buffs"                     , _("Buffs")                     , MUT_BUFFS                     , cvar("g_buffs") > 0)
+               X("Overkill"                  , _("Overkill")                  , MUT_OVERKILL                  , cvar("g_overkill"))
+               X("No powerups"               , _("No powerups")               , MUT_NO_POWERUPS               , cvar("g_powerups") == 0)
+               X("Powerups"                  , _("Powerups")                  , MUT_POWERUPS                  , cvar("g_powerups") > 0)
+               X("Touch explode"             , _("Touch explode")             , MUT_TOUCHEXPLODE              , cvar("g_touchexplode") > 0)
+               X("Wall jumping"              , _("Wall jumping")              , MUT_WALLJUMP                  , cvar("g_walljump"))
+               X("No start weapons"          , _("No start weapons")          , MUT_NO_START_WEAPONS          , cvar_string("g_weaponarena") == "0" && cvar("g_balance_blaster_weaponstartoverride") == 0)
+               X("Nades"                     , _("Nades")                     , MUT_NADES                     , cvar("g_nades"))
+               X("Offhand blaster"           , _("Offhand blaster")           , MUT_OFFHAND_BLASTER           , cvar("g_offhand_blaster"))
+               #undef X
+       }
+       return s2;
+}
 #endif
 
 void wordwrap_cb(string s, float l, void(string) callback)
@@ -308,7 +372,7 @@ void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(
 }
 
 #ifdef GAMEQC
-string ScoreString(int pFlags, float pValue)
+string ScoreString(int pFlags, float pValue, int rounds_played)
 {
        string valstr;
        float l;
@@ -318,9 +382,11 @@ string ScoreString(int pFlags, float pValue)
        if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
                valstr = "";
        else if(pFlags & SFL_RANK)
-               valstr = count_ordinal(pValue);
+               valstr = (pValue < 256 ? count_ordinal(pValue) : _("N/A"));
        else if(pFlags & SFL_TIME)
-               valstr = TIME_ENCODED_TOSTRING(pValue);
+               valstr = TIME_ENCODED_TOSTRING(pValue, true);
+       else if (rounds_played)
+               valstr = sprintf("%.1f", pValue / rounds_played);
        else
                valstr = ftos(pValue);
 
@@ -378,7 +444,7 @@ vector decompressShortVector(int data)
        float q = (data & 0x0F80) / 0x80;
        int len = (data & 0x007F);
 
-       //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n");
+       //print("\ndecompress: p:", ftos(p)); print(" q:", ftos(q)); print(" len:", ftos(len), "\n");
 
        if(p == 0)
        {
@@ -391,7 +457,7 @@ vector decompressShortVector(int data)
        }
        else
        {
-               q   = .19634954084936207740 * q;
+               q = .19634954084936207740 * q;
                p = .19634954084936207740 * p - 1.57079632679489661922;
                out.x = cos(q) *  cos(p);
                out.y = sin(q) *  cos(p);
@@ -427,10 +493,10 @@ float compressShortVector(vector vec)
                        y = 30;
        }
        else
-               y = floor(0.5 + ang.y * 32 / 360)          & 31; // 0..360 to 0..32
+               y = floor(0.5 + ang.y * 32 / 360) & 31; // 0..360 to 0..32
        len = invertLengthLog(vlen(vec));
 
-       //print("compressed: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
+       //print("compressed: p:", ftos(p)); print(" y:", ftos(y)); print(" len:", ftos(len), "\n");
 
        return (p * 0x1000) + (y * 0x80) + len;
 }
@@ -806,14 +872,14 @@ float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLe
        return left;
 }
 
-float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
+float textLengthUpToLength(string theText, int maxLength, textLengthUpToLength_lenFunction_t w)
 {
        // STOP.
        // The following function is SLOW.
        // For your safety and for the protection of those around you...
        // DO NOT CALL THIS AT HOME.
        // No really, don't.
-       if(w(theText) <= maxWidth)
+       if(w(theText) <= maxLength)
                return strlen(theText); // yeah!
 
        bool colors = (w("^7") == 0);
@@ -832,7 +898,7 @@ float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_
                        ofs = (!res.x) ? 0 : res.x - res.y;
                }
 
-               if(w(substring(theText, 0, middle + ofs)) <= maxWidth)
+               if(w(substring(theText, 0, middle + ofs)) <= maxLength)
                        left = middle + ofs;
                else
                        right = middle;
@@ -877,17 +943,17 @@ string find_last_color_code(string s)
        return "";
 }
 
-string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
+string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
 {
        string s = getWrappedLine_remaining;
 
-       if(w <= 0)
+       if(maxWidth <= 0)
        {
                getWrappedLine_remaining = string_null;
                return s; // the line has no size ANYWAY, nothing would be displayed.
        }
 
-       int take_until = textLengthUpToWidth(s, w, theFontSize, tw);
+       int take_until = textLengthUpToWidth(s, maxWidth, theFontSize, tw);
        if(take_until > 0 && take_until < strlen(s))
        {
                int last_word = take_until - 1;
@@ -915,17 +981,17 @@ string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunc
        }
 }
 
-string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
+string getWrappedLineLen(int maxLength, textLengthUpToLength_lenFunction_t tw)
 {
        string s = getWrappedLine_remaining;
 
-       if(w <= 0)
+       if(maxLength <= 0)
        {
                getWrappedLine_remaining = string_null;
                return s; // the line has no size ANYWAY, nothing would be displayed.
        }
 
-       int take_until = textLengthUpToLength(s, w, tw);
+       int take_until = textLengthUpToLength(s, maxLength, tw);
        if(take_until > 0 && take_until < strlen(s))
        {
                int last_word = take_until - 1;
@@ -1118,35 +1184,56 @@ vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_styl
        return myvel + spd * mydir;
 }
 
+// compresses the shot origin offset vector to an int with the following format:
+// xxxxxxxx SSyyyyyy SUzzzzzz
+// 32109876 54321098 76543210
+// 1st byte: x component (it uses all 8 bits)
+// 2nd byte: y component in the last 6 bits and the signs of the x and y components
+// 3rd byte: z component in the last 6 bits and the sign of the z component (the 2nd bit is unused)
+// all values are doubled on compression and halved on decompression
+// so the precision for all components is 0.5
+// values are bound to the following ranges:
+// x: -127.5 +127.5
+// y:  -31.5  +31.5
+// z:  -31.5  +31.5
 float compressShotOrigin(vector v)
 {
-       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)
+       int rx_neg = (v.x < 0) ? 1 : 0;
+       int ry_neg = (v.y < 0) ? 1 : 0;
+       int rz_neg = (v.z < 0) ? 1 : 0;
+       int rx = rint(fabs(v.x) * 2);
+       int ry = rint(fabs(v.y) * 2);
+       int rz = rint(fabs(v.z) * 2);
+       if(rx > 255) // 128 * 2 - 1
        {
                LOG_DEBUG("shot origin ", vtos(v), " x out of bounds\n");
                rx = bound(0, rx, 255);
        }
-       if(ry > 255 || ry < 0)
+       if(ry > 63) // 32 * 2 - 1
        {
                LOG_DEBUG("shot origin ", vtos(v), " y out of bounds\n");
-               ry = bound(0, ry, 255);
+               ry = bound(0, ry, 63);
        }
-       if(rz > 255 || rz < 0)
+       if(rz > 63) // 32 * 2 - 1
        {
                LOG_DEBUG("shot origin ", vtos(v), " z out of bounds\n");
-               rz = bound(0, rz, 255);
+               rz = bound(0, rz, 63);
        }
+       ry |= ry_neg * BIT(6) + rx_neg * BIT(7);
+       rz |= rz_neg * BIT(6); // BIT(7) unused
        return rx * 0x10000 + ry * 0x100 + rz;
 }
 vector decompressShotOrigin(int f)
 {
        vector v;
-       v.x = ((f & 0xFF0000) / 0x10000) / 2;
-       v.y = ((f & 0xFF00) / 0x100 - 128) / 4;
-       v.z = ((f & 0xFF) - 128) / 4;
-       return v;
+       v.x = f >> 16;
+       v.y = (f & 0xFF00) >> 8;
+       v.z = f & 0xFF;
+       // remove sign bits and apply sign
+       if (v.y & BIT(7)) { v.y &= ~BIT(7); v.x *= -1; }
+       if (v.y & BIT(6)) { v.y &= ~BIT(6); v.y *= -1; }
+       if (v.z & BIT(6)) { v.z &= ~BIT(6); v.z *= -1; }
+       return v / 2;
 }
 
 #ifdef GAMEQC
@@ -1710,23 +1797,6 @@ Notification Announcer_PickNumber(int type, int num)
                        }
                        break;
                }
-               case CNT_IDLE:
-               {
-                       switch(num)
-                       {
-                               case 10: return ANNCE_NUM_IDLE_10;
-                               case 9:  return ANNCE_NUM_IDLE_9;
-                               case 8:  return ANNCE_NUM_IDLE_8;
-                               case 7:  return ANNCE_NUM_IDLE_7;
-                               case 6:  return ANNCE_NUM_IDLE_6;
-                               case 5:  return ANNCE_NUM_IDLE_5;
-                               case 4:  return ANNCE_NUM_IDLE_4;
-                               case 3:  return ANNCE_NUM_IDLE_3;
-                               case 2:  return ANNCE_NUM_IDLE_2;
-                               case 1:  return ANNCE_NUM_IDLE_1;
-                       }
-                       break;
-               }
                case CNT_KILL:
                {
                        switch(num)
@@ -1913,14 +1983,13 @@ void unfollow_sameorigin(entity e)
 .float aiment_deadflag;
 void SetMovetypeFollow(entity ent, entity e)
 {
-       // FIXME this may not be warpzone aware
        set_movetype(ent, MOVETYPE_FOLLOW); // make the hole follow
        ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.
        ent.aiment = e; // make the hole follow bmodel
        ent.punchangle = e.angles; // the original angles of bmodel
        ent.view_ofs = ent.origin - e.origin; // relative origin
        ent.v_angle = ent.angles - e.angles; // relative angles
-       ent.aiment_classname = strzone(e.classname);
+       ent.aiment_classname = e.classname;
        ent.aiment_deadflag = e.deadflag;
 
        if(IS_PLAYER(ent.aiment))
@@ -1936,8 +2005,7 @@ void UnsetMovetypeFollow(entity ent)
 {
        set_movetype(ent, MOVETYPE_FLY);
        PROJECTILE_MAKETRIGGER(ent);
-       if (ent.aiment_classname)
-               strunzone(ent.classname);
+       ent.aiment_classname = string_null;
        // FIXME: engine bug?
        // resetting aiment the engine will set orb's origin close to world's origin
        //ent.aiment = NULL;