X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Futil.qc;h=8ef6b2019f07c4a08d5467897214d5717ce03c56;hb=HEAD;hp=35eff3f9ce59ac74c6899b5b5420acfd66946047;hpb=67610c5b92e218cc245c040209c1a79ab6b02758;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 35eff3f9c..ca120d295 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -1,22 +1,22 @@ #include "util.qh" #if defined(CSQC) - #include "constants.qh" #include - #include "mapinfo.qh" - #include "notifications/all.qh" - #include "scores.qh" - #include + #include + #include #include + #include + #include + #include #elif defined(MENUQC) #elif defined(SVQC) - #include "constants.qh" - #include - #include "notifications/all.qh" - #include + #include + #include #include - #include "scores.qh" - #include "mapinfo.qh" + #include + #include + #include + #include #endif #ifdef SVQC @@ -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; } @@ -770,57 +836,6 @@ int cvar_settemp_restore() 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. @@ -842,7 +857,11 @@ float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLe { middle = floor((left + right) / 2); if(colors) - ofs = skipIncompleteTag(theText, middle, len); + { + vector res = checkColorCode(theText, len, middle, false); + ofs = (res.x) ? res.x - res.y : 0; + } + if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth) left = middle + ofs; else @@ -853,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); @@ -874,8 +893,12 @@ float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_ { middle = floor((left + right) / 2); if(colors) - ofs = skipIncompleteTag(theText, middle, len); - if(w(substring(theText, 0, middle + ofs)) <= maxWidth) + { + vector res = checkColorCode(theText, len, middle, true); + ofs = (!res.x) ? 0 : res.x - res.y; + } + + if(w(substring(theText, 0, middle + ofs)) <= maxLength) left = middle + ofs; else right = middle; @@ -920,44 +943,36 @@ 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) { - float cantake; - float take; - string s; + string s = getWrappedLine_remaining; - 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. } - cantake = textLengthUpToWidth(s, w, theFontSize, tw); - if(cantake > 0 && cantake < strlen(s)) + int take_until = textLengthUpToWidth(s, maxWidth, theFontSize, tw); + if(take_until > 0 && take_until < strlen(s)) { - take = cantake - 1; - while(take > 0 && substring(s, take, 1) != " ") - --take; - if(take == 0) - { - getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake); - if(getWrappedLine_remaining == "") - getWrappedLine_remaining = string_null; - else if (tw("^7", theFontSize) == 0) - getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining); - return substring(s, 0, cantake); - } - else + int last_word = take_until - 1; + while(last_word > 0 && substring(s, last_word, 1) != " ") + --last_word; + + int skip = 0; + if(last_word != 0) { - getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take); - if(getWrappedLine_remaining == "") - getWrappedLine_remaining = string_null; - else if (tw("^7", theFontSize) == 0) - getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining); - return substring(s, 0, take); + take_until = last_word; + skip = 1; } + + getWrappedLine_remaining = substring(s, take_until + skip, strlen(s) - take_until); + if(getWrappedLine_remaining == "") + getWrappedLine_remaining = string_null; + else if (tw("^7", theFontSize) == 0) + getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining); + return substring(s, 0, take_until); } else { @@ -966,44 +981,36 @@ string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunc } } -string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw) +string getWrappedLineLen(int maxLength, textLengthUpToLength_lenFunction_t tw) { - float cantake; - float take; - string s; + string s = getWrappedLine_remaining; - 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. } - cantake = textLengthUpToLength(s, w, tw); - if(cantake > 0 && cantake < strlen(s)) + int take_until = textLengthUpToLength(s, maxLength, tw); + if(take_until > 0 && take_until < strlen(s)) { - take = cantake - 1; - while(take > 0 && substring(s, take, 1) != " ") - --take; - if(take == 0) - { - getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake); - if(getWrappedLine_remaining == "") - getWrappedLine_remaining = string_null; - else if (tw("^7") == 0) - getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining); - return substring(s, 0, cantake); - } - else + int last_word = take_until - 1; + while(last_word > 0 && substring(s, last_word, 1) != " ") + --last_word; + + int skip = 0; + if(last_word != 0) { - getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take); - if(getWrappedLine_remaining == "") - getWrappedLine_remaining = string_null; - else if (tw("^7") == 0) - getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining); - return substring(s, 0, take); + take_until = last_word; + skip = 1; } + + getWrappedLine_remaining = substring(s, take_until + skip, strlen(s) - take_until); + if(getWrappedLine_remaining == "") + getWrappedLine_remaining = string_null; + else if (tw("^7") == 0) + getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining); + return substring(s, 0, take_until); } else { @@ -1177,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 @@ -1298,7 +1326,7 @@ ERASEABLE void write_String_To_File(int fh, string str, bool alsoprint) { fputs(fh, str); - if (alsoprint) LOG_INFO(str); + if (alsoprint) LOG_HELP(str); } string get_model_datafilename(string m, float sk, string fil) @@ -1769,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) @@ -1837,6 +1848,7 @@ Notification Announcer_PickNumber(int type, int num) } break; } + case CNT_NORMAL: default: { switch(num) @@ -1971,36 +1983,47 @@ 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)) + { + entity pl = ent.aiment; + ent.view_ofs.x = bound(pl.mins.x + 4, ent.view_ofs.x, pl.maxs.x - 4); + ent.view_ofs.y = bound(pl.mins.y + 4, ent.view_ofs.y, pl.maxs.y - 4); + ent.view_ofs.z = bound(pl.mins.z + 4, ent.view_ofs.z, pl.maxs.z - 4); + } } + void UnsetMovetypeFollow(entity ent) { set_movetype(ent, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(ent); - ent.aiment = NULL; + 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; } -float LostMovetypeFollow(entity ent) + +int LostMovetypeFollow(entity ent) { /* if(ent.move_movetype != MOVETYPE_FOLLOW) if(ent.aiment) error("???"); */ - if(ent.aiment) - { - if(ent.aiment.classname != ent.aiment_classname) - return 1; - if(ent.aiment.deadflag != ent.aiment_deadflag) - return 1; - } + // FIXME: engine bug? + // when aiment disconnects the engine will set orb's origin close to world's origin + if(!ent.aiment) + return 2; + if(ent.aiment.classname != ent.aiment_classname || ent.aiment.deadflag != ent.aiment_deadflag) + return 1; return 0; } #endif