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)
}
#ifdef GAMEQC
-string ScoreString(int pFlags, float pValue)
+string ScoreString(int pFlags, float pValue, int rounds_played)
{
string valstr;
float l;
else if(pFlags & SFL_RANK)
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);
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)
{
}
else
{
- q = .19634954084936207740 * q;
+ q = .19634954084936207740 * q;
p = .19634954084936207740 * p - 1.57079632679489661922;
out.x = cos(q) * cos(p);
out.y = sin(q) * cos(p);
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;
}
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);
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;
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;
}
}
-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;
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
.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))
{
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;