#define PHYS_DODGING_AIR autocvar_sv_dodging_air_dodging
#define PHYS_DODGING_MAXSPEED autocvar_sv_dodging_maxspeed
#define PHYS_DODGING_AIR_MAXSPEED autocvar_sv_dodging_air_maxspeed
+#define PHYS_DODGING_CLIENTSELECT autocvar_sv_dodging_clientselect
// we ran out of stats slots! TODO: re-enable this when prediction is available for dodging
#if 0
#define PHYS_DODGING_AIR STAT(DODGING_AIR, this)
#define PHYS_DODGING_MAXSPEED STAT(DODGING_MAXSPEED, this)
#define PHYS_DODGING_AIR_MAXSPEED STAT(DODGING_AIR_MAXSPEED, this)
+#define PHYS_DODGING_CLIENTSELECT STAT(DODGING_CLIENTSELECT, this)
#endif
#ifdef CSQC
- float cvar_cl_dodging_timeout;
+ bool autocvar_cl_dodging;
#define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime))
#define PHYS_DODGING_TIMEOUT(s) STAT(DODGING_TIMEOUT)
#define PHYS_DODGING_PRESSED_KEYS(s) (s).pressedkeys
+ #define PHYS_DODGING_ENABLED(s) autocvar_cl_dodging
#elif defined(SVQC)
- .float cvar_cl_dodging_timeout;
#define PHYS_DODGING_FRAMETIME sys_frametime
- #define PHYS_DODGING_TIMEOUT(s) CS(s).cvar_cl_dodging_timeout
+ #define PHYS_DODGING_TIMEOUT(s) CS_CVAR(s).cvar_cl_dodging_timeout
#define PHYS_DODGING_PRESSED_KEYS(s) CS(s).pressedkeys
+ #define PHYS_DODGING_ENABLED(s) CS_CVAR(s).cvar_cl_dodging
#endif
+REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout");
+REPLICATE(cvar_cl_dodging, bool, "cl_dodging");
+
#ifdef SVQC
bool autocvar_sv_dodging_sound;
if (!this.dodging_action) return;
// when swimming or dead, no dodging allowed..
- if (this.waterlevel >= WATERLEVEL_SWIMMING || IS_DEAD(this))
+ bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
+ if (this.waterlevel >= WATERLEVEL_SWIMMING || IS_DEAD(this) || (PHYS_DODGING_CLIENTSELECT && !PHYS_DODGING_ENABLED(this) && !frozen_dodging))
{
this.dodging_action = 0;
this.dodging_direction.x = 0;
{
PM_dodging_checkpressedkeys(this);
+ // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
int keys = this.pressedkeys;
keys = BITSET(keys, KEY_FORWARD, PHYS_CS(this).movement.x > 0);
keys = BITSET(keys, KEY_BACKWARD, PHYS_CS(this).movement.x < 0);
keys = BITSET(keys, KEY_LEFT, PHYS_CS(this).movement.y < 0);
keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this));
- keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this));
+ keys = BITSET(keys, KEY_CROUCH, IS_DUCKED(this)); // workaround: player can't un-crouch until their path is clear, so we keep the button held here
keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
this.pressedkeys = keys;
#ifdef SVQC
-REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout");
+void dodging_ResetPlayer(entity this)
+{
+ this.last_dodging_time = 0;
+
+ this.dodging_action = 0;
+ this.dodging_single_action = 0;
+
+ this.dodging_force_total = 0;
+ this.dodging_force_remaining = 0;
+
+ this.dodging_direction = '0 0 0';
+}
+
+MUTATOR_HOOKFUNCTION(dodging, PlayerSpawn)
+{
+ entity player = M_ARGV(0, entity);
+ dodging_ResetPlayer(player);
+}
+
+MUTATOR_HOOKFUNCTION(dodging, MakePlayerObserver)
+{
+ entity player = M_ARGV(0, entity);
+ dodging_ResetPlayer(player);
+}
MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
{