X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fcommon%2Futil.qc;h=3567a2b095d522857ec2e2e4454de22c3e2d3dd5;hp=95ab69ca6e39cbbea6a73898e8165be8860f35c2;hb=68523a95eb77d7c8af75739452fa9d2717ec881a;hpb=3572c1bb78e2a4b753082d308bcbbfe0dfe5dc1d diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 95ab69ca6..3fb2d7e1a 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -1,23 +1,147 @@ #include "util.qh" #if defined(CSQC) - #include "constants.qh" - #include "../client/mutators/events.qh" - #include "mapinfo.qh" - #include "notifications/all.qh" - #include "scores.qh" - #include + #include + #include + #include + #include + #include + #include + #include #elif defined(MENUQC) #elif defined(SVQC) - #include "constants.qh" - #include "../server/mutators/events.qh" - #include "notifications/all.qh" - #include - #include "scores.qh" - #include "mapinfo.qh" + #include + #include + #include + #include + #include + #include + #include +#endif + +#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. */ @@ -86,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) @@ -95,7 +283,8 @@ void wordwrap_cb(string s, float l, void(string) callback) s = strzone(s); lleft = l; - for (i = 0;i < strlen(s);++i) + int len = strlen(s); + for (i = 0; i < len; ++i) { if (substring(s, i, 2) == "\\n") { @@ -113,12 +302,12 @@ void wordwrap_cb(string s, float l, void(string) callback) if (lleft > 0) { callback(" "); - lleft = lleft - 1; + --lleft; } } else { - for (j = i+1;j < strlen(s);++j) + for (j = i+1; j < len; ++j) // ^^ this skips over the first character of a word, which // is ALWAYS part of the word // this is safe since if i+1 == strlen(s), i will become @@ -146,7 +335,7 @@ void wordwrap_cb(string s, float l, void(string) callback) lleft = l; } callback(substring(s, i, wlen)); - lleft = lleft - wlen; + lleft -= wlen; i = j - 1; } } @@ -193,22 +382,9 @@ string ScoreString(int pFlags, float pValue) if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME))) valstr = ""; else if(pFlags & SFL_RANK) - { - valstr = ftos(pValue); - l = strlen(valstr); - if((l >= 2) && (substring(valstr, l - 2, 1) == "1")) - valstr = strcat(valstr, "th"); - else if(substring(valstr, l - 1, 1) == "1") - valstr = strcat(valstr, "st"); - else if(substring(valstr, l - 1, 1) == "2") - valstr = strcat(valstr, "nd"); - else if(substring(valstr, l - 1, 1) == "3") - valstr = strcat(valstr, "rd"); - else - valstr = strcat(valstr, "th"); - } + 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 valstr = ftos(pValue); @@ -334,7 +510,7 @@ STATIC_INIT(compressShortVector) l *= f; } - if(cvar("developer")) + if(cvar("developer") > 0) { LOG_TRACE("Verifying vector compression table..."); for(i = 0x0F00; i < 0xFFFF; ++i) @@ -398,8 +574,8 @@ 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)) + int wflags = REGISTRY_GET(Weapons, w).spawnflags; + if(wflags & WEP_FLAG_SPECIALATTACK) continue; for(i = 0; i < n; ++i) if(stof(argv(i)) == w) @@ -658,57 +834,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. @@ -730,7 +855,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 @@ -741,14 +870,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); @@ -762,8 +891,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; @@ -808,44 +941,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; - - s = getWrappedLine_remaining; + 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. } - 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 { @@ -854,44 +979,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; - - s = getWrappedLine_remaining; + 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. } - 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 { @@ -1176,11 +1293,19 @@ float matchacl(string acl, string str) if(s == t) { r = d; + break; // if we found a killing case, apply it! other settings may be allowed in the future, but this one is caught } } return r; } +ERASEABLE +void write_String_To_File(int fh, string str, bool alsoprint) +{ + fputs(fh, str); + if (alsoprint) LOG_HELP(str); +} + string get_model_datafilename(string m, float sk, string fil) { if(m) @@ -1268,7 +1393,12 @@ float get_model_parameters(string m, float sk) case "reserved": get_model_parameters_species = SPECIES_RESERVED; break; } if(c == "sex") + { + if (s == "Male") s = _("Male"); + else if (s == "Female") s = _("Female"); + else if (s == "Undisclosed") s = _("Undisclosed"); get_model_parameters_sex = s; + } if(c == "weight") get_model_parameters_weight = stof(s); if(c == "age") @@ -1307,6 +1437,158 @@ float get_model_parameters(string m, float sk) return 1; } +string translate_key(string key) +{ + if (prvm_language == "en") return key; + + if (substring(key, 0, 1) == "<") + { + if (key == "") return _(""); + if (key == "") return _(""); + } + + switch(key) + { + case "TAB": return _("TAB"); + case "ENTER": return _("ENTER"); + case "ESCAPE": return _("ESCAPE"); + case "SPACE": return _("SPACE"); + + case "BACKSPACE": return _("BACKSPACE"); + case "UPARROW": return _("UPARROW"); + case "DOWNARROW": return _("DOWNARROW"); + case "LEFTARROW": return _("LEFTARROW"); + case "RIGHTARROW": return _("RIGHTARROW"); + + case "ALT": return _("ALT"); + case "CTRL": return _("CTRL"); + case "SHIFT": return _("SHIFT"); + + case "INS": return _("INS"); + case "DEL": return _("DEL"); + case "PGDN": return _("PGDN"); + case "PGUP": return _("PGUP"); + case "HOME": return _("HOME"); + case "END": return _("END"); + + case "PAUSE": return _("PAUSE"); + + case "NUMLOCK": return _("NUMLOCK"); + case "CAPSLOCK": return _("CAPSLOCK"); + case "SCROLLOCK": return _("SCROLLOCK"); + + case "SEMICOLON": return _("SEMICOLON"); + case "TILDE": return _("TILDE"); + case "BACKQUOTE": return _("BACKQUOTE"); + case "QUOTE": return _("QUOTE"); + case "APOSTROPHE": return _("APOSTROPHE"); + case "BACKSLASH": return _("BACKSLASH"); + } + + if (substring(key, 0, 1) == "F") + { + string subkey = substring(key, 1, -1); + if (IS_DIGIT(substring(key, 3, 1))) // check only first digit + { + return sprintf(_("F%d"), stof(subkey)); + } + // continue in case there is another key name starting with F + } + + if (substring(key, 0, 3) == "KP_") + { + string subkey = substring(key, 3, -1); + if (IS_DIGIT(substring(key, 3, 1))) // check only first digit + { + return sprintf(_("KP_%d"), stof(subkey)); + } + + switch(subkey) + { + case "INS": return sprintf(_("KP_%s"), _("INS")); + case "END": return sprintf(_("KP_%s"), _("END")); + case "DOWNARROW": return sprintf(_("KP_%s"), _("DOWNARROW")); + case "PGDN": return sprintf(_("KP_%s"), _("PGDN")); + case "LEFTARROW": return sprintf(_("KP_%s"), _("LEFTARROW")); + case "RIGHTARROW": return sprintf(_("KP_%s"), _("RIGHTARROW")); + case "HOME": return sprintf(_("KP_%s"), _("HOME")); + case "UPARROW": return sprintf(_("KP_%s"), _("UPARROW")); + case "PGUP": return sprintf(_("KP_%s"), _("PGUP")); + case "PERIOD": return sprintf(_("KP_%s"), _("PERIOD")); + case "DEL": return sprintf(_("KP_%s"), _("DEL")); + case "DIVIDE": return sprintf(_("KP_%s"), _("DIVIDE")); + case "SLASH": return sprintf(_("KP_%s"), _("SLASH")); + case "MULTIPLY": return sprintf(_("KP_%s"), _("MULTIPLY")); + case "MINUS": return sprintf(_("KP_%s"), _("MINUS")); + case "PLUS": return sprintf(_("KP_%s"), _("PLUS")); + case "ENTER": return sprintf(_("KP_%s"), _("ENTER")); + case "EQUALS": return sprintf(_("KP_%s"), _("EQUALS")); + default: return key; + } + } + + if (key == "PRINTSCREEN") return _("PRINTSCREEN"); + + if (substring(key, 0, 5) == "MOUSE") + return sprintf(_("MOUSE%d"), stof(substring(key, 5, -1))); + + if (key == "MWHEELUP") return _("MWHEELUP"); + if (key == "MWHEELDOWN") return _("MWHEELDOWN"); + + if (substring(key, 0,3) == "JOY") + return sprintf(_("JOY%d"), stof(substring(key, 3, -1))); + + if (substring(key, 0,3) == "AUX") + return sprintf(_("AUX%d"), stof(substring(key, 3, -1))); + + if (substring(key, 0, 4) == "X360_") + { + string subkey = substring(key, 4, -1); + switch(subkey) + { + case "DPAD_UP": return sprintf(_("X360_%s"), _("DPAD_UP")); + case "DPAD_DOWN": return sprintf(_("X360_%s"), _("DPAD_DOWN")); + case "DPAD_LEFT": return sprintf(_("X360_%s"), _("DPAD_LEFT")); + case "DPAD_RIGHT": return sprintf(_("X360_%s"), _("DPAD_RIGHT")); + case "START": return sprintf(_("X360_%s"), _("START")); + case "BACK": return sprintf(_("X360_%s"), _("BACK")); + case "LEFT_THUMB": return sprintf(_("X360_%s"), _("LEFT_THUMB")); + case "RIGHT_THUMB": return sprintf(_("X360_%s"), _("RIGHT_THUMB")); + case "LEFT_SHOULDER": return sprintf(_("X360_%s"), _("LEFT_SHOULDER")); + case "RIGHT_SHOULDER": return sprintf(_("X360_%s"), _("RIGHT_SHOULDER")); + case "LEFT_TRIGGER": return sprintf(_("X360_%s"), _("LEFT_TRIGGER")); + case "RIGHT_TRIGGER": return sprintf(_("X360_%s"), _("RIGHT_TRIGGER")); + case "LEFT_THUMB_UP": return sprintf(_("X360_%s"), _("LEFT_THUMB_UP")); + case "LEFT_THUMB_DOWN": return sprintf(_("X360_%s"), _("LEFT_THUMB_DOWN")); + case "LEFT_THUMB_LEFT": return sprintf(_("X360_%s"), _("LEFT_THUMB_LEFT")); + case "LEFT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("LEFT_THUMB_RIGHT")); + case "RIGHT_THUMB_UP": return sprintf(_("X360_%s"), _("RIGHT_THUMB_UP")); + case "RIGHT_THUMB_DOWN": return sprintf(_("X360_%s"), _("RIGHT_THUMB_DOWN")); + case "RIGHT_THUMB_LEFT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_LEFT")); + case "RIGHT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_RIGHT")); + default: return key; + } + } + + if (substring(key, 0, 4) == "JOY_") + { + string subkey = substring(key, 4, -1); + switch(subkey) + { + case "UP": return sprintf(_("JOY_%s"), _("UP")); + case "DOWN": return sprintf(_("JOY_%s"), _("DOWN")); + case "LEFT": return sprintf(_("JOY_%s"), _("LEFT")); + case "RIGHT": return sprintf(_("JOY_%s"), _("RIGHT")); + default: return key; + } + } + + if (substring(key, 0, 8) == "MIDINOTE") + return sprintf(_("MIDINOTE%d"), stof(substring(key, 8, -1))); + + return key; +} + // x-encoding (encoding as zero length invisible string) const string XENCODE_2 = "xX"; const string XENCODE_22 = "0123456789abcdefABCDEF"; @@ -1492,23 +1774,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) @@ -1560,6 +1825,7 @@ Notification Announcer_PickNumber(int type, int num) } break; } + case CNT_NORMAL: default: { switch(num) @@ -1617,3 +1883,227 @@ int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents) return CONTENT_EMPTY; } #endif + +#ifdef SVQC +void attach_sameorigin(entity e, entity to, string tag) +{ + vector org, t_forward, t_left, t_up, e_forward, e_up; + float tagscale; + + org = e.origin - gettaginfo(to, gettagindex(to, tag)); + tagscale = (vlen(v_forward) ** -2); // undo a scale on the tag + t_forward = v_forward * tagscale; + t_left = v_right * -tagscale; + t_up = v_up * tagscale; + + e.origin_x = org * t_forward; + e.origin_y = org * t_left; + e.origin_z = org * t_up; + + // current forward and up directions + if (substring(e.model, 0, 1) == "*") // bmodels have their own rules + e.angles = AnglesTransform_FromVAngles(e.angles); + else + e.angles = AnglesTransform_FromAngles(e.angles); + fixedmakevectors(e.angles); + + // untransform forward, up! + e_forward.x = v_forward * t_forward; + e_forward.y = v_forward * t_left; + e_forward.z = v_forward * t_up; + e_up.x = v_up * t_forward; + e_up.y = v_up * t_left; + e_up.z = v_up * t_up; + + e.angles = fixedvectoangles2(e_forward, e_up); + if (substring(e.model, 0, 1) == "*") // bmodels have their own rules + e.angles = AnglesTransform_ToVAngles(e.angles); + else + e.angles = AnglesTransform_ToAngles(e.angles); + + setattachment(e, to, tag); + setorigin(e, e.origin); +} + +void detach_sameorigin(entity e) +{ + vector org; + org = gettaginfo(e, 0); + e.angles = fixedvectoangles2(v_forward, v_up); + if (substring(e.model, 0, 1) == "*") // bmodels have their own rules + e.angles = AnglesTransform_ToVAngles(e.angles); + else + e.angles = AnglesTransform_ToAngles(e.angles); + setorigin(e, org); + setattachment(e, NULL, ""); + setorigin(e, e.origin); +} + +void follow_sameorigin(entity e, entity to) +{ + set_movetype(e, MOVETYPE_FOLLOW); // make the hole follow + e.aiment = to; // make the hole follow bmodel + e.punchangle = to.angles; // the original angles of bmodel + e.view_ofs = e.origin - to.origin; // relative origin + e.v_angle = e.angles - to.angles; // relative angles +} + +#if 0 +// TODO: unused, likely for a reason, possibly needs extensions (allow setting the new movetype as a parameter?) +void unfollow_sameorigin(entity e) +{ + set_movetype(e, MOVETYPE_NONE); +} +#endif + +.string aiment_classname; +.float aiment_deadflag; +void SetMovetypeFollow(entity ent, entity e) +{ + 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 = 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_classname = string_null; + // FIXME: engine bug? + // resetting aiment the engine will set orb's origin close to world's origin + //ent.aiment = NULL; +} + +int LostMovetypeFollow(entity ent) +{ +/* + if(ent.move_movetype != MOVETYPE_FOLLOW) + if(ent.aiment) + error("???"); +*/ + // 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 + +#ifdef GAMEQC +// decolorizes and team colors the player name when needed +string playername(string thename, int teamid, bool team_colorize) +{ + TC(int, teamid); + bool do_colorize = (teamplay && team_colorize); +#ifdef SVQC + if(do_colorize && !intermission_running) +#else + if(do_colorize) +#endif + { + string t = Team_ColorCode(teamid); + return strcat(t, strdecolorize(thename)); + } + else + return thename; +} + +float trace_hits_box_a0, trace_hits_box_a1; + +float trace_hits_box_1d(float end, float thmi, float thma) +{ + if (end == 0) + { + // just check if x is in range + if (0 < thmi) + return false; + if (0 > thma) + return false; + } + else + { + // do the trace with respect to x + // 0 -> end has to stay in thmi -> thma + trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end)); + trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end)); + if (trace_hits_box_a0 > trace_hits_box_a1) + return false; + } + return true; +} + +float trace_hits_box(vector start, vector end, vector thmi, vector thma) +{ + end -= start; + thmi -= start; + thma -= start; + // now it is a trace from 0 to end + + trace_hits_box_a0 = 0; + trace_hits_box_a1 = 1; + + if (!trace_hits_box_1d(end.x, thmi.x, thma.x)) + return false; + if (!trace_hits_box_1d(end.y, thmi.y, thma.y)) + return false; + if (!trace_hits_box_1d(end.z, thmi.z, thma.z)) + return false; + + return true; +} + +float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma) +{ + return trace_hits_box(start, end, thmi - ma, thma - mi); +} +#endif + +ERASEABLE +float cvar_or(string cv, float v) +{ + string s = cvar_string(cv); + if(s == "") + return v; + else + return stof(s); +} + +// NOTE base is the central value +// freq: circle frequency, = 2*pi*frequency in hertz +// start_pos: +// -1 start from the lower value +// 0 start from the base value +// 1 start from the higher value +ERASEABLE +float blink_synced(float base, float range, float freq, float start_time, int start_pos) +{ + // note: + // RMS = sqrt(base^2 + 0.5 * range^2) + // thus + // base = sqrt(RMS^2 - 0.5 * range^2) + // ensure RMS == 1 + + return base + range * sin((time - start_time - (M_PI / 2) * start_pos) * freq); +} + +ERASEABLE +float blink(float base, float range, float freq) +{ + return blink_synced(base, range, freq, 0, 0); +}