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)
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);
else
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.
{
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
{
middle = floor((left + right) / 2);
if(colors)
- ofs = skipIncompleteTag(theText, middle, len);
+ {
+ vector res = checkColorCode(theText, len, middle, true);
+ ofs = (!res.x) ? 0 : res.x - res.y;
+ }
+
if(w(substring(theText, 0, middle + ofs)) <= maxWidth)
left = middle + ofs;
else
string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
{
- float cantake;
- float take;
- string s;
-
- s = getWrappedLine_remaining;
+ string s = getWrappedLine_remaining;
if(w <= 0)
{
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, w, theFontSize, tw);
+ if(take_until > 0 && take_until < strlen(s))
{
- take = cantake - 1;
- while(take > 0 && substring(s, take, 1) != " ")
- --take;
+ int last_word = take_until - 1;
+ while(last_word > 0 && substring(s, last_word, 1) != " ")
+ --last_word;
int skip = 0;
- if(take != 0)
+ if(last_word != 0)
{
- cantake = take;
+ take_until = last_word;
skip = 1;
}
- getWrappedLine_remaining = substring(s, cantake + skip, strlen(s) - cantake);
+ 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, cantake)), getWrappedLine_remaining);
- return substring(s, 0, cantake);
+ getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining);
+ return substring(s, 0, take_until);
}
else
{
string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
{
- float cantake;
- float take;
- string s;
-
- s = getWrappedLine_remaining;
+ string s = getWrappedLine_remaining;
if(w <= 0)
{
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, w, tw);
+ if(take_until > 0 && take_until < strlen(s))
{
- take = cantake - 1;
- while(take > 0 && substring(s, take, 1) != " ")
- --take;
+ int last_word = take_until - 1;
+ while(last_word > 0 && substring(s, last_word, 1) != " ")
+ --last_word;
int skip = 0;
- if(take != 0)
+ if(last_word != 0)
{
- cantake = take;
+ take_until = last_word;
skip = 1;
}
- getWrappedLine_remaining = substring(s, cantake + skip, strlen(s) - cantake);
+ 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, cantake)), getWrappedLine_remaining);
- return substring(s, 0, cantake);
+ getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining);
+ return substring(s, 0, take_until);
}
else
{
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)
}
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)
}
break;
}
+ case CNT_NORMAL:
default:
{
switch(num)
.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