sizeincrease -6
size 10 10
trailspacing 40
-effect TE_TEI_G3_HIT
- type beam
- alpha 128 128 256
- color 0xFFFFFF 0xFFFFFF
- countabsolute 1
- size 8 8
- tex 200 200
-effect TE_TEI_G3_HIT
- type smoke
- airfriction -4
- alpha 256 256 512
- color 0xFFFFFF 0xFFFFFF
- sizeincrease -2
- size 2 2
- trailspacing 20
- velocityjitter 2 2 2
-effect TE_TEI_G3_HIT
- type smoke
- airfriction -4
- alpha 256 256 512
- color 0xFFFFFF 0xFFFFFF
- sizeincrease -6
- size 10 10
- trailspacing 40
effect particlegibs_damage_hit
type blood
airfriction 3
trailspacing 32
underwater
velocityjitter 16 16 16
+effect TE_TEI_G3_HIT
+ type beam
+ alpha 128 128 256
+ color 0xFFFFFF 0xFFFFFF
+ countabsolute 1
+ size 8 8
+ tex 200 200
+effect TE_TEI_G3_HIT
+ type smoke
+ airfriction -4
+ alpha 256 256 512
+ color 0xFFFFFF 0xFFFFFF
+ sizeincrease -2
+ size 2 2
+ trailspacing 20
+ velocityjitter 2 2 2
+effect TE_TEI_G3_HIT
+ type smoke
+ airfriction -4
+ alpha 256 256 512
+ color 0xFFFFFF 0xFFFFFF
+ sizeincrease -6
+ size 10 10
+ trailspacing 40
float autocvar_hud_panel_centerprint_fade_minfontsize = 0;
bool autocvar_hud_panel_centerprint_flip;
float autocvar_hud_panel_centerprint_fontscale;
-float autocvar_hud_panel_centerprint_fontscale_bold;
+float autocvar_hud_panel_centerprint_fontscale_bold = 1.8;
float autocvar_hud_panel_centerprint_time;
bool autocvar_hud_panel_chat;
bool autocvar_hud_panel_engineinfo;
void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag)
{
- float d;
vector ringsize, v, t;
ringsize = radi * '1 1 0';
centre = HUD_Shift(centre);
ringsize = HUD_Scale(ringsize);
- float co = cos(f * 2 * M_PI);
- float si = sin(f * 2 * M_PI);
- float q = fabs(co) + fabs(si);
- co /= q;
- si /= q;
-
if(f >= 1)
{
// draw full rectangle
v.y -= 0.5 * ringsize.y; t -= '0.5 -0.5 0';
R_PolygonVertex(v, t, rgb, a);
R_EndPolygon();
+ return; // Complete rectangle, nothing more needed.
+ }
- d = q - 1;
- if(d > 0)
- {
- R_BeginPolygon(pic, drawflag, true);
- v = centre; t = '0.5 0.5 0';
- R_PolygonVertex(v, t, rgb, a);
+ float co = cos(f * 2 * M_PI);
+ float si = sin(f * 2 * M_PI);
+ float q = fabs(co) + fabs(si);
+ co /= q;
+ si /= q;
- v = centre; t = '0.5 0.5 0';
- v.x += 0.5 * ringsize.x; t += '0.5 0.5 0';
- R_PolygonVertex(v, t, rgb, a);
- }
- }
- else if(f > 0.75)
+ if(f > 0.75)
{
- // draw upper and first triangle
+ // draw upper half in full
R_BeginPolygon(pic, drawflag, true);
v = centre; t = '0.5 0.5 0';
v.x += 0.5 * ringsize.x; t += '0.5 0.5 0';
v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0';
R_PolygonVertex(v, t, rgb, a);
R_EndPolygon();
+ // draw clipped lower half as a quad
R_BeginPolygon(pic, drawflag, true);
v = centre; t = '0.5 0.5 0';
R_PolygonVertex(v, t, rgb, a);
v = centre; t = '0.5 0.5 0';
v.y -= 0.5 * ringsize.y; t -= '0.5 -0.5 0';
R_PolygonVertex(v, t, rgb, a);
-
- d = q - 0.75;
- if(d <= 0)
- R_EndPolygon();
}
else if(f > 0.5)
{
- // draw upper triangle
+ // draw upper half in full
R_BeginPolygon(pic, drawflag, true);
v = centre; t = '0.5 0.5 0';
v.x += 0.5 * ringsize.x; t += '0.5 0.5 0';
v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0';
R_PolygonVertex(v, t, rgb, a);
R_EndPolygon();
+ // draw clipped lower half as a triangle
+ R_BeginPolygon(pic, drawflag, true);
+ v = centre; t = '0.5 0.5 0';
+ R_PolygonVertex(v, t, rgb, a);
- d = q - 0.5;
- if(d > 0)
- {
- R_BeginPolygon(pic, drawflag, true);
- v = centre; t = '0.5 0.5 0';
- R_PolygonVertex(v, t, rgb, a);
-
- v = centre; t = '0.5 0.5 0';
- v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0';
- R_PolygonVertex(v, t, rgb, a);
- }
+ v = centre; t = '0.5 0.5 0';
+ v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0';
+ R_PolygonVertex(v, t, rgb, a);
}
else if(f > 0.25)
{
- // draw first triangle
+ // draw clipped lower half as a quad
R_BeginPolygon(pic, drawflag, true);
v = centre; t = '0.5 0.5 0';
R_PolygonVertex(v, t, rgb, a);
v = centre; t = '0.5 0.5 0';
v.y += 0.5 * ringsize.y; t += '0.5 -0.5 0';
R_PolygonVertex(v, t, rgb, a);
-
- d = q - 0.25;
- if(d <= 0)
- R_EndPolygon();
}
- else
+ else if (f > 0)
{
- d = q;
- if(d > 0)
- {
- R_BeginPolygon(pic, drawflag, true);
- v = centre; t = '0.5 0.5 0';
- R_PolygonVertex(v, t, rgb, a);
-
- v = centre; t = '0.5 0.5 0';
- v.x += 0.5 * ringsize.x; t += '0.5 0.5 0';
- R_PolygonVertex(v, t, rgb, a);
- }
- }
+ // draw clipped lower half as a triangle
+ R_BeginPolygon(pic, drawflag, true);
+ v = centre; t = '0.5 0.5 0';
+ R_PolygonVertex(v, t, rgb, a);
- if(d > 0)
- {
v = centre; t = '0.5 0.5 0';
- v.x += co * 0.5 * ringsize.x; t += co * '0.5 0.5 0';
- v.y += si * 0.5 * ringsize.y; t += si * '0.5 -0.5 0';
+ v.x += 0.5 * ringsize.x; t += '0.5 0.5 0';
R_PolygonVertex(v, t, rgb, a);
- R_EndPolygon();
}
+ else
+ {
+ // Nothing to draw.
+ return;
+ }
+
+ // The last, moving vertex.
+ v = centre; t = '0.5 0.5 0';
+ v.x += co * 0.5 * ringsize.x; t += co * '0.5 0.5 0';
+ v.y += si * 0.5 * ringsize.y; t += si * '0.5 -0.5 0';
+ R_PolygonVertex(v, t, rgb, a);
+ R_EndPolygon();
}
/** engine callback */
TE_TEI_G3(PINK, "0xFF00FF", "0xFF11FF", "0x200020", "0x400040")
#undef TE_TEI_G3
-// Vaporizer hit effect
-DEF(TE_TEI_G3_HIT);
-SUB(TE_TEI_G3_HIT) {
- MY(alpha_min) = 128;
- MY(alpha_max) = 128;
- MY(alpha_fade) = 256;
- MY(color_min) = "0xFFFFFF";
- MY(color_max) = "0xFFFFFF";
- MY(countabsolute) = 1;
- MY(size_min) = 8;
- MY(size_max) = 8;
- MY(tex_min) = 200;
- MY(tex_max) = 200;
- MY(type) = "beam";
-}
-SUB(TE_TEI_G3_HIT) /* rings */ {
- MY(airfriction) = -4;
- MY(alpha_min) = 256;
- MY(alpha_max) = 256;
- MY(alpha_fade) = 512;
- MY(color_min) = "0xFFFFFF";
- MY(color_max) = "0xFFFFFF";
- MY(sizeincrease) = -2;
- MY(size_min) = 2;
- MY(size_max) = 2;
- MY(trailspacing) = 20;
- MY(type) = "smoke";
- MY(velocityjitter) = '2.0 2.0 2.0';
-}
-SUB(TE_TEI_G3_HIT) {
- MY(airfriction) = -4;
- MY(alpha_min) = 256;
- MY(alpha_max) = 256;
- MY(alpha_fade) = 512;
- MY(color_min) = "0xFFFFFF";
- MY(color_max) = "0xFFFFFF";
- MY(sizeincrease) = -6;
- MY(size_min) = 10;
- MY(size_max) = 10;
- MY(trailspacing) = 40;
- MY(type) = "smoke";
-}
-
#include "effectinfo_gentle_particlegibs.inc"
#include "effectinfo_onslaught.inc"
MY(underwater) = true;
MY(velocityjitter) = '16.0 16.0 16.0';
}
+
+// Vaporizer hit effect
+DEF(TE_TEI_G3_HIT);
+SUB(TE_TEI_G3_HIT) {
+ MY(alpha_min) = 128;
+ MY(alpha_max) = 128;
+ MY(alpha_fade) = 256;
+ MY(color_min) = "0xFFFFFF";
+ MY(color_max) = "0xFFFFFF";
+ MY(countabsolute) = 1;
+ MY(size_min) = 8;
+ MY(size_max) = 8;
+ MY(tex_min) = 200;
+ MY(tex_max) = 200;
+ MY(type) = "beam";
+}
+SUB(TE_TEI_G3_HIT) /* rings */ {
+ MY(airfriction) = -4;
+ MY(alpha_min) = 256;
+ MY(alpha_max) = 256;
+ MY(alpha_fade) = 512;
+ MY(color_min) = "0xFFFFFF";
+ MY(color_max) = "0xFFFFFF";
+ MY(sizeincrease) = -2;
+ MY(size_min) = 2;
+ MY(size_max) = 2;
+ MY(trailspacing) = 20;
+ MY(type) = "smoke";
+ MY(velocityjitter) = '2.0 2.0 2.0';
+}
+SUB(TE_TEI_G3_HIT) {
+ MY(airfriction) = -4;
+ MY(alpha_min) = 256;
+ MY(alpha_max) = 256;
+ MY(alpha_fade) = 512;
+ MY(color_min) = "0xFFFFFF";
+ MY(color_max) = "0xFFFFFF";
+ MY(sizeincrease) = -6;
+ MY(size_min) = 10;
+ MY(size_max) = 10;
+ MY(trailspacing) = 40;
+ MY(type) = "smoke";
+}
.float last_respawn;
void ctf_RespawnFlag(entity flag)
{
+ flag.watertype = CONTENT_EMPTY; // TODO: it is unclear why this workaround is needed, likely many other potential breakage points!!
// check for flag respawn being called twice in a row
if(flag.last_respawn > time - 0.5)
{ backtrace("flag respawn called twice quickly! please notify Samual about this..."); }
#ifdef CSQC
MUTATOR_HOOKFUNCTION(bloodloss, PlayerCanCrouch)
{
- if(STAT(HEALTH) <= STAT(BLOODLOSS))
+ if(STAT(HEALTH) > 0 && STAT(HEALTH) <= STAT(BLOODLOSS))
M_ARGV(1, bool) = true; // do_crouch
}
MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump)
{
- if(STAT(HEALTH) <= STAT(BLOODLOSS))
+ if(STAT(HEALTH) > 0 && STAT(HEALTH) <= STAT(BLOODLOSS))
return true;
}
#endif
REGISTER_MUTATOR(cl_buffs, true);
MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add)
{
- int allBuffs = STAT(BUFFS);
- FOREACH(Buffs, it.m_itemid & allBuffs, {
- addPowerupItem(it.m_name, strcat("buff_", it.netname), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60);
- });
+ int allBuffs = STAT(BUFFS);
+ if (allBuffs)
+ FOREACH(Buffs, it.m_itemid & allBuffs, {
+ addPowerupItem(it.m_name, strcat("buff_", it.netname), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60);
+ });
}
MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format)
{
// client side options
.float cvar_cl_buffs_autoreplace;
+
+float buff_Available(entity buff);
#include <common/weapons/_all.qh>
#include <common/mutators/mutator/buffs/buffs.qh>
+ #include <common/mutators/mutator/buffs/sv_buffs.qh>
#include "../lib/warpzone/util_server.qh"
#elif defined(CSQC)
bool GiveBuff(entity e, Buff thebuff, int op, int val)
{
bool had_buff = (STAT(BUFFS, e) & thebuff.m_itemid);
+ float new_buff_time = ((had_buff) ? STAT(BUFF_TIME, e) : 0);
switch (op)
{
case OP_SET:
- STAT(BUFF_TIME, e) = val;
+ new_buff_time = val;
break;
case OP_MIN:
- STAT(BUFF_TIME, e) = max(STAT(BUFF_TIME, e), val);
+ new_buff_time = max(new_buff_time, val);
break;
case OP_MAX:
- STAT(BUFF_TIME, e) = min(STAT(BUFF_TIME, e), val);
+ new_buff_time = min(new_buff_time, val);
break;
case OP_PLUS:
- STAT(BUFF_TIME, e) += val;
+ new_buff_time += val;
break;
case OP_MINUS:
- STAT(BUFF_TIME, e) -= val;
+ new_buff_time -= val;
break;
}
- if(STAT(BUFF_TIME, e) <= 0)
+ if(new_buff_time <= 0)
+ {
+ if(had_buff)
+ STAT(BUFF_TIME, e) = new_buff_time;
STAT(BUFFS, e) &= ~thebuff.m_itemid;
+ }
else
+ {
+ STAT(BUFF_TIME, e) = new_buff_time;
STAT(BUFFS, e) = thebuff.m_itemid; // NOTE: replaces any existing buffs on the player!
+ }
bool have_buff = (STAT(BUFFS, e) & thebuff.m_itemid);
return (had_buff != have_buff);
}
got += GiveResourceValue(e, RES_FUEL, op, val);
break;
default:
- FOREACH(Buffs, it != BUFF_Null && Buff_UndeprecateName(cmd) == it.netname,
+ FOREACH(Buffs, it != BUFF_Null && buff_Available(it) && Buff_UndeprecateName(cmd) == it.netname,
{
got += GiveBuff(e, it, op, val);
break;
METHOD(Vaporizer, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
{
- if(GetResource(actor, thiswep.ammo_type) > 0)
+ if((actor.items & IT_UNLIMITED_AMMO) || GetResource(actor, thiswep.ammo_type) > 0)
PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 1, false);
else
PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(vaporizer, speed), 0, WEP_CVAR_SEC(vaporizer, lifetime), false); // WEAPONTODO: replace with proper vaporizer cvars
#undef STAT_MOVEVARS_TIMESCALE
#undef STAT_MOVEVARS_GRAVITY
-#pragma noref 0
-
#define use use1
.void(entity this, entity actor, entity trigger) use;
#define touch move_touch
-// deglobalization:
-
void(vector ang) _makevectors_hidden = #1;
-//#define makevectors DO_NOT_USE_GLOBALS_PREFER_MAKE_VECTORS_MACRO_INSTEAD
-
-#define makestatic DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
-#define skel_get_bonerel DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
vector(float skel, float bonenum) _skel_get_boneabs_hidden = #270;
-//#define skel_get_boneabs DO_NOT_USE_GLOBALS_PREFER_SKEL_GET_BONE_ABS_MACRO_INSTEAD
-
void(float skel, float bonenum, vector org) _skel_set_bone_hidden = #271;
-//#define skel_set_bone DO_NOT_USE_GLOBALS_PREFER_SKEL_SET_BONE_MACRO_INSTEAD
-
-#define skel_mul_bone DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
-#define skel_mul_bones DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
void(vector org, float radius, vector lightcolours) _adddynamiclight_hidden = #305;
-//#define adddynamiclight DO_NOT_USE_GLOBALS_PREFER_ADD_DYNAMIC_LIGHT_MACRO_INSTEAD
-#define adddynamiclight2 DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
void(vector dir) _vectorvectors_hidden = #432;
-#define vectorvectors DO_NOT_USE_GLOBALS_PREFER_VECTOR_VECTORS_MACRO_INSTEAD
-
vector(entity ent, float tagindex) _gettaginfo_hidden = #452;
-//#define gettaginfo DO_NOT_USE_GLOBALS_PREFER_GET_TAG_INFO_MACRO_INSTEAD
-#define getentity DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-#define getentityvec DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
+#pragma noref 0
int() _buf_create = #460;
#define buf_create _buf_create
-#pragma noref 0
-
-// deglobalization:
-
-#define skel_get_bonerel DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
vector(float skel, float bonenum) _skel_get_boneabs_hidden = #270;
-//#define skel_get_boneabs DO_NOT_USE_GLOBALS_PREFER_SKEL_GET_BONE_ABS_MACRO_INSTEAD
-
void(float skel, float bonenum, vector org) _skel_set_bone_hidden = #271;
-//#define skel_set_bone DO_NOT_USE_GLOBALS_PREFER_SKEL_SET_BONE_MACRO_INSTEAD
-
-#define skel_mul_bone DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
-#define skel_mul_bones DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
vector(entity ent, float tagindex) _gettaginfo_hidden = #452;
-//#define gettaginfo DO_NOT_USE_GLOBALS_PREFER_GET_TAG_INFO_MACRO_INSTEAD
+
+#pragma noref 0
#else
#define NULL (RVALUE, world)
#endif
+
+// Shadow functions which use globals - see deglobalization.qh for details.
+#define makevectors DO_NOT_USE_GLOBALS_PREFER_MAKE_VECTORS_MACRO_INSTEAD
+#define aim DO_NOT_USE_GLOBALS
+#define makestatic DO_NOT_USE_GLOBALS
+#define skel_get_bonerel DO_NOT_USE_GLOBALS
+#define skel_get_boneabs DO_NOT_USE_GLOBALS_PREFER_SKEL_GET_BONE_ABS_MACRO_INSTEAD
+#define skel_set_bone DO_NOT_USE_GLOBALS_PREFER_SKEL_SET_BONE_MACRO_INSTEAD
+#define skel_mul_bone DO_NOT_USE_GLOBALS
+#define skel_mul_bones DO_NOT_USE_GLOBALS
+#define adddynamiclight DO_NOT_USE_GLOBALS_PREFER_ADD_DYNAMIC_LIGHT_MACRO_INSTEAD
+#define adddynamiclight2 DO_NOT_USE_GLOBALS
+#define vectorvectors DO_NOT_USE_GLOBALS_PREFER_VECTOR_VECTORS_MACRO_INSTEAD
+#define gettaginfo DO_NOT_USE_GLOBALS_PREFER_GET_TAG_INFO_MACRO_INSTEAD
+#define getentity DO_NOT_USE_GLOBALS
+#define getentityvec DO_NOT_USE_GLOBALS
if (IS_REAL_CLIENT(_cl)) stuffcmd(_cl, __VA_ARGS__); \
MACRO_END
-#pragma noref 0
-
#define use use1
.void(entity this, entity actor, entity trigger) use;
-// deglobalization:
-
void(vector ang) _makevectors_hidden = #1;
-//#define makevectors DO_NOT_USE_GLOBALS_PREFER_MAKE_VECTORS_MACRO_INSTEAD
-#define aim DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
-
-#define makestatic DO_NOT_USE_GLOBALS // not used anywhere so not wrapped
+#pragma noref 0
#include "warpzone/mathlib.qc"
+// needs to be included before any of the functions which use globals are called
+#include "deglobalization.qh"
+
#include "accumulate.qh"
#include "angle.qc"
#include "arraylist.qh"
#include "counting.qh"
#include "cvar.qh"
#include "defer.qh"
-#include "deglobalization.qh"
#include "draw.qh"
#include "enumclass.qh"
#include "file.qh"
{
this.mins = PHYS_PL_MIN(this);
this.maxs = PHYS_PL_MAX(this);
+ if (IS_DEAD(this))
+ this.maxs.z = 5;
this.view_ofs = PHYS_PL_VIEWOFS(this);
}
}
#endif
CSQCPlayer_SetMinsMaxs(e);
- e.angles_y = input_angles.y;
+ if (!IS_DEAD(e))
+ e.angles.y = input_angles.y;
}
// relink
+#include "lib/accumulate.qh"
#include "lib/float.qh"
+#include "lib/log.qh"
#include "lib/misc.qh"
#include "lib/static.qh"
#include "lib/vector.qh"
-// These macros wrap functions which use globals so mutation only occurs inside them and is not visible from outside.
-// Functions for which all usages are replaced with these macros can be hidden by #defines inside our `*defs.qh` files
-// to prevent anyone from using them accidentally in the future
+// These macros wrap functions which use globals so mutation of global state only occurs inside them and is not visible from outside.
+// This helps prevent bugs where refactoring accidentally breaks implicit assumptions about global state ("pls call makevectors before calling this").
+// Currently only functions that use v_forward/v_right/v_up are wrapped since those are most common.
+// Some functions don't have wrappers because they're not used anywhere.
-// TODO stuff in the engine that uses the v_forward/v_right/v_up globals and is not wrapped yet:
-// - RF_USEAXIS, addentities, predraw,
-// - CL_GetEntityMatrix (in engine but is called from other functions so transitively any of them can use the globals - e.g. V_CalcRefdef, maybe others)
-// - however RF_USEAXIS is only used if MF_ROTATE is used which is only set in one place
-// - e.camera_transform / CL_VM_TransformView (in engine)
-// - this is the only used function that both sets and gets the globals (aim does too but isn't used in our code)
+// Steps (slightly inspired by steps in self.qh):
+// 1) (done) Create alternative names for the builtins (e.g. _makevectors_hidden) to be used inside wrappers.
+// Shadow the originals with macros that tell the user to use the wrappers instead. These are in the *defs.qh files.
+// 2) Create wrapper macros with the same name (e.g. makevectors) that still use globals but log their usage.
+// - Would be nice to also log reads and writes to the globals themselves. Probably possible with macros, comma expressions and [[alias]].
+// 3) Create wrapper macros that use locals (e.g. MAKE_VECTORS).
+// TODO stuff in the engine that uses the v_forward/v_right/v_up globals and is not wrapped yet:
+// - RF_USEAXIS, addentities, predraw,
+// - CL_GetEntityMatrix (in engine but is called from other functions so transitively any of them can use the globals - e.g. V_CalcRefdef, maybe others)
+// - however RF_USEAXIS is only used if MF_ROTATE is used which is only set in one place
+// - e.camera_transform / CL_VM_TransformView (in engine)
+// - this is the only used function that both sets and gets the globals (aim does too but isn't used in our code)
+// 4) Gradually replace uses of each function with its wrapper.
+// 5) When a function is no longer used, remove the wrapper with the same name to cause compile errors that will prevent accidental use in the future.
-// convenience for deglobalization code - don't use these just to hide that globals are still used
-#define CLEAR_V_GLOBALS() v_forward = VEC_NAN; v_right = VEC_NAN; v_up = VEC_NAN
-#define GET_V_GLOBALS(forward, right, up) forward = v_forward; right = v_right; up = v_up
-#define SET_V_GLOBALS(forward, right, up) v_forward = forward; v_right = right; v_up = up
+// Final checking:
+// When all functions which use a global have been replaced with the wrappers,
+// the wrappers can check that the global contains NaN before its use and set it to NaN after its use.
+// This should detect if there is any remaining global mutation (even in the engine).
+// NaN is the most likely value to expose remaining usages - e.g. functions like traceline crash on it.
+
+#ifdef GAMEQC // menu doesn't use any globals
+
+// compile time switches in case perf is an issue
+#define DEGLOB_LOGGING 1
+#define DEGLOB_CLEAR 1
+
+const int DEGLOB_ORIGINAL = 1;
+const int DEGLOB_WRAPPED = 2;
+#if DEGLOB_LOGGING
+int autocvar_debug_deglobalization_logging = 0;
+// Varargs to this should already be stringized, otherwise they're expanded first which makes them less readable.
+// The downside is redundant quotes, fortunately none of these functions take strings.
+#define DEGLOB_LOG(kind, name, ...) deglob_log(kind, name, __FILE__, __LINE__, __FUNC__, #__VA_ARGS__)
+// This needs to be a function, not a macro,
+// because some wrappers of the old functions need to use comma expressions
+// because they return values.
+void deglob_log(int kind, string name, string file, int line, string func, string more_text) {
+ if (autocvar_debug_deglobalization_logging & kind) {
+ LOG_INFOF("%s %f %s %s:%d:%s args: %s", PROGNAME, time, name, file, line, func, more_text);
+ }
+}
+#else
+#define DEGLOB_LOG(kind, name, ...)
+void deglob_log(int kind, string name, string file, int line, string func, string more_text) {}
+#endif
-#ifdef GAMEQC
+// convenience for deglobalization code - don't use these just to hide that globals are still used
+#define GET_V_GLOBALS(forward, right, up) MACRO_BEGIN forward = v_forward; right = v_right; up = v_up; MACRO_END
+#define SET_V_GLOBALS(forward, right, up) MACRO_BEGIN v_forward = forward; v_right = right; v_up = up; MACRO_END
+#if DEGLOB_CLEAR
+bool autocvar_debug_deglobalization_clear = true;
+#define CLEAR_V_GLOBALS() MACRO_BEGIN \
+ if (autocvar_debug_deglobalization_clear) { \
+ v_forward = VEC_NAN; v_right = VEC_NAN; v_up = VEC_NAN \
+ } \
+MACRO_END
STATIC_INIT(globals) {
// set to NaN to more easily detect uninitialized use
- // TODO when all functions are wrapped and the raw functions are not used anymore,
- // uncomment the defines in *progs.qh files that hide the raw functions
- // and assert that the global vectors are NaN before calling the raw functions here
- // to make sure nobody (even builtins) is accidentally using them - NaN is the most likely value to expose remaining usages
-
CLEAR_V_GLOBALS();
}
+#else
+#define CLEAR_V_GLOBALS()
#endif
/// Same as the `makevectors` builtin but uses the provided locals instead of the `v_*` globals.
/// Always use this instead of raw `makevectors` to make the data flow clear.
/// Note that you might prefer `FIXED_MAKE_VECTORS` for new code.
#define MAKE_VECTORS(angles, forward, right, up) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_WRAPPED, "MAKE_VECTORS", #angles); \
_makevectors_hidden(angles); \
GET_V_GLOBALS(forward, right, up); \
CLEAR_V_GLOBALS(); \
/// Returns all 4 vectors by assigning to them (instead of returning a value) for consistency (and sanity)
#define SKEL_GET_BONE_ABS(skel, bonenum, forward, right, up, origin) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_WRAPPED, "SKEL_GET_BONE_ABS", #skel, #bonenum); \
origin = _skel_get_boneabs_hidden(skel, bonenum) \
GET_V_GLOBALS(forward, right, up); \
CLEAR_V_GLOBALS(); \
MACRO_END
#define SKEL_SET_BONE(skel, bonenum, org, forward, right, up) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_WRAPPED, "SKEL_SET_BONE", #skel, #bonenum, #org); \
SET_V_GLOBALS(forward, right, up); \
_skel_set_bone_hidden(skel, bonenum, org); \
CLEAR_V_GLOBALS(); \
MACRO_END
#define ADD_DYNAMIC_LIGHT(org, radius, lightcolours, forward, right, up) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_WRAPPED, "ADD_DYNAMIC_LIGHT", #org, #radius, #lightcolours); \
SET_V_GLOBALS(forward, right, up); \
_adddynamiclight_hidden(org, radius, lightcolours); \
CLEAR_V_GLOBALS(); \
MACRO_END
#define VECTOR_VECTORS(forward_in, forward, right, up) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_WRAPPED, "VECTOR_VECTORS", #forward_in); \
_vectorvectors_hidden(forward_in); \
GET_V_GLOBALS(forward, right, up); \
CLEAR_V_GLOBALS(); \
/// Note that this only avoids the v_* globals, not the gettaginfo_* ones
#define GET_TAG_INFO(ent, tagindex, forward, right, up, origin) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_WRAPPED, "GET_TAG_INFO", #ent, #tagindex); \
origin = _gettaginfo_hidden(ent, tagindex); \
GET_V_GLOBALS(forward, right, up); \
CLEAR_V_GLOBALS(); \
MACRO_END
+
+#undef makevectors
+#define makevectors(angles) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_ORIGINAL, "makevectors", #angles); \
+ _makevectors_hidden(angles); \
+MACRO_END
+
+#undef skel_get_boneabs
+#define skel_get_boneabs(skel, bonenum) ( \
+ deglob_log(DEGLOB_ORIGINAL, "skel_get_boneabs", __FILE__, __LINE__, __FUNC__, #skel ", " #bonenum), \
+ _skel_get_boneabs_hidden(skel, bonenum) \
+)
+
+#undef skel_set_bone
+#define skel_set_bone(skel, bonenum, org) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_ORIGINAL, "skel_set_bone", #skel, #bonenum, #org); \
+ _skel_set_bone_hidden(skel, bonenum, org); \
+MACRO_END
+
+#undef adddynamiclight
+#define adddynamiclight(org, radius, lightcolours) MACRO_BEGIN \
+ DEGLOB_LOG(DEGLOB_ORIGINAL, "adddynamiclight", #org, #radius, #lightcolours); \
+ _adddynamiclight_hidden(org, radius, lightcolours); \
+MACRO_END
+
+#undef gettaginfo
+#define gettaginfo(ent, tagindex) ( \
+ deglob_log(DEGLOB_ORIGINAL, "gettaginfo", __FILE__, __LINE__, __FUNC__, #ent ", " #tagindex), \
+ _gettaginfo_hidden(ent, tagindex) \
+)
+
+#endif // GAMEQC
.bool disabled;
void setDependent_Check(entity e)
{
+ bool disabled_prev = e.disabled;
float f;
string s;
if(e.func_setDependent)
e.disabled = (e.disabled + ((f >= e.cvar3Max_setDependent) && (f <= e.cvar3Min_setDependent)) > e.op_setDependent);
}
}
+ if (disabled_prev != e.disabled && e.loadCvars)
+ e.loadCvars(e);
}
void setDependent_Draw(entity e)
{
else
{
if (!this.jumppadcount && !STAT(FROZEN, this)
- && !(this.goalcurrent_prev && (this.goalcurrent_prev.wpflags & WAYPOINTFLAG_JUMP)))
+ && !(this.goalcurrent_prev && (this.goalcurrent_prev.wpflags & WAYPOINTFLAG_JUMP) && !IS_ONGROUND(this)))
{
// find a new goal
this.havocbot_role(this); // little too far down the rabbit hole
if(this.flags & FL_SPAWNING)
{
this.flags &= ~FL_SPAWNING;
- Join(this);
+ if(joinAllowed(this))
+ Join(this);
return;
}
}
void precache_playermodel(string m)
{
- float globhandle, i, n;
+ int globhandle, i, n;
string f;
+ // remove :<skinnumber> suffix
+ int j = strstrofs(m, ":", 0);
+ if(j >= 0)
+ m = substring(m, 0, j);
+
if(substring(m, -9, 5) == "_lod1")
return;
if(substring(m, -9, 5) == "_lod2")
// https://forums.xonotic.org/showthread.php?tid=8190
// https://gitlab.com/xonotic/xonotic-data.pk3dir/merge_requests/738
-alias test_blaster_switch "settemp g_balance_blaster_primary_animtime 0.1 ; settemp g_balance_blaster_switchdelay_drop 0.1 ; g_balance_blaster_switchdelay_raise 0.1"
+alias test_blaster_switch "settemp g_balance_blaster_primary_animtime 0.1 ; settemp g_balance_blaster_switchdelay_drop 0.1 ; settemp g_balance_blaster_switchdelay_raise 0.1"
-// https://gitlab.com/xonotic/xonotic-data.pk3dir/merge_requests/614
alias test_crylink_sec_horizontal "settemp g_balance_crylink_secondary_linkexplode 0 ; settemp g_balance_crylink_secondary_other_fadetime 2 ; settemp g_balance_crylink_secondary_other_lifetime 2 ; settemp g_balance_crylink_secondary_speed 4000 ; settemp g_balance_crylink_secondary_spread 0.08 ; settemp g_balance_crylink_secondary_spreadtype 0"
alias test_rocket_flying "settemp g_balance_devastator_remote_jump 1"
snd_identicalsoundrandomization_time -0.1
snd_identicalsoundrandomization_tics 1
+set debug_deglobalization_logging 0 "bitfield: 1 logs usage of the old functions which use globals implicitly, 2 logs usage of the new wrappers; support for this can be disabled at compile time for better performance"
+set debug_deglobalization_clear 0 "make the new wrappers set globals to NaN after use, this helps find bugs but can result in crashes; support for this can be disabled at compile time for better performance"
+
// load console command aliases and settings
exec commands.cfg