#define PHYS_DODGING g_dodging
#define PHYS_DODGING_DELAY autocvar_sv_dodging_delay
#define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold
-#define PHYS_DODGING_FROZEN_NODOUBLETAP autocvar_sv_dodging_frozen_doubletap
+#define PHYS_DODGING_FROZEN_DOUBLETAP autocvar_sv_dodging_frozen_doubletap
#define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold
#define PHYS_DODGING_HORIZ_SPEED autocvar_sv_dodging_horiz_speed
#define PHYS_DODGING_HORIZ_SPEED_FROZEN autocvar_sv_dodging_horiz_speed_frozen
#define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed
#define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging
#define PHYS_DODGING_AIR autocvar_sv_dodging_air_dodging
-#define PHYS_DODGING_MAXSPEED autocvar_sv_dodging_maxspeed
+#define PHYS_DODGING_MAXSPEED autocvar_sv_dodging_maxspeed // TODO separate max air speed, fix frozen dodging
// we ran out of stats slots! TODO: re-enable this when prediction is available for dodging
#if 0
#define PHYS_DODGING STAT(DODGING, this)
#define PHYS_DODGING_DELAY STAT(DODGING_DELAY, this)
#define PHYS_DODGING_DISTANCE_THRESHOLD STAT(DODGING_DISTANCE_THRESHOLD, this)
-#define PHYS_DODGING_FROZEN_NODOUBLETAP STAT(DODGING_FROZEN_NO_DOUBLETAP, this)
+#define PHYS_DODGING_FROZEN_DOUBLETAP STAT(DODGING_FROZEN_DOUBLETAP, this)
#define PHYS_DODGING_HEIGHT_THRESHOLD STAT(DODGING_HEIGHT_THRESHOLD, this)
#define PHYS_DODGING_HORIZ_SPEED STAT(DODGING_HORIZ_SPEED, this)
#define PHYS_DODGING_HORIZ_SPEED_FROZEN STAT(DODGING_HORIZ_SPEED_FROZEN, this)
#define PHYS_DODGING_MAXSPEED STAT(DODGING_MAXSPEED, this)
#endif
-#ifdef CSQC
+#ifdef CSQC // FIXME this is an sv_ file, none of this will even be compiled
#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
#elif defined(SVQC)
#define PHYS_DODGING_FRAMETIME sys_frametime
- #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout
+ #define PHYS_DODGING_TIMEOUT(s) CS(s).cvar_cl_dodging_timeout
#define PHYS_DODGING_PRESSED_KEYS(s) CS(s).pressedkeys
#endif
bool autocvar_sv_dodging_sound;
-// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
-.float dodging_action;
-
-// the jump part of the dodge cannot be ramped
-.float dodging_single_action;
-
#include <common/animdecide.qh>
#include <common/physics/player.qh>
-.float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT);
+.float cvar_cl_dodging_timeout;
REGISTER_MUTATOR(dodging, cvar("g_dodging"))
{
// the jump part of the dodge cannot be ramped
.float dodging_single_action;
-
// these are used to store the last key press time for each of the keys..
.float last_FORWARD_KEY_time;
.float last_BACKWARD_KEY_time;
// This is the velocity gain to be added over the ramp time.
// It will decrease from frame to frame during dodging_action = 1
// until it's 0.
-.float dodging_velocity_gain;
+//.float dodging_velocity_gain;
-#ifdef CSQC
+.float dodging_force_total;
+.float dodging_force_remaining;
+
+#ifdef CSQC // TODO what is this? 1) CSQC in sv_ file never even gets compiled 2) pressedkeys is only defined on server so this would be always 0
.int pressedkeys;
#endif
-// returns 1 if the player is close to a wall
-bool check_close_to_wall(entity this, float threshold)
-{
- if (PHYS_DODGING_WALL == 0) { return false; }
-
-#define X(OFFSET) \
- tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \
- if(trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) && vdist(this.origin - trace_endpos, <, threshold)) \
+#define X(dir) \
+ tracebox(this.origin, this.mins, this.maxs, this.origin + threshold * dir, true, this); \
+ if (trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) \
return true;
- X(1000*v_right);
- X(-1000*v_right);
- X(1000*v_forward);
- X(-1000*v_forward);
-#undef X
+
+// returns true if the player is close to a wall
+bool is_close_to_wall(entity this, float threshold)
+{
+ X(v_right);
+ X(-v_right);
+ X(v_forward);
+ X(-v_forward);
return false;
}
-bool check_close_to_ground(entity this, float threshold)
+bool is_close_to_ground(entity this, float threshold)
{
- return IS_ONGROUND(this) ? true : false;
+ if (IS_ONGROUND(this)) return true;
+ X(-v_up); // necessary for dodging down a slope using doubletap (using `+dodge` works anyway)
+
+ return false;
+}
+
+#undef X
+
+// TODO use real cvars
+/*float autocvar_velocity_min = 200;
+float autocvar_velocity_max = 700;
+float autocvar_force_min = 50;
+float autocvar_force_max = 350;*/
+float determine_speed(entity player) {
+ float x = PHYS_FROZEN(player) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
+ x = x; // unused
+ return x;
+
+ /*float horiz_vel = vlen(vec2(player.velocity));
+ // force min and max are inverted - the faster you are the wekaer dodging becomes
+ // TODO document cvars in cfg
+ return map_bound_ranges(horiz_vel, autocvar_velocity_min, autocvar_velocity_max, autocvar_force_max, autocvar_force_min);*/
}
bool PM_dodging_checkpressedkeys(entity this)
{
- if(!PHYS_DODGING)
+ // first check if the last dodge is far enough back in time so we can dodge again
+ if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY)
return false;
bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
- bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
+ bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_DOUBLETAP);
- // first check if the last dodge is far enough back in time so we can dodge again
- if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY)
- return false;
+ float tap_direction_x = 0;
+ float tap_direction_y = 0;
+ bool dodge_detected = false;
+ vector mymovement = PHYS_CS(this).movement;
+
+ #define X(COND,BTN,RESULT) \
+ if (mymovement_##COND) \
+ /* is this a state change? */ \
+ if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \
+ tap_direction_##RESULT; \
+ if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) \
+ dodge_detected = true; \
+ if(PHYS_INPUT_BUTTON_DODGE(this)) \
+ dodge_detected = true; \
+ this.last_##BTN##_KEY_time = time; \
+ }
+ X(x < 0, BACKWARD, x--);
+ X(x > 0, FORWARD, x++);
+ X(y < 0, LEFT, y--);
+ X(y > 0, RIGHT, y++);
+ #undef X
+
+ if (!dodge_detected) return false;
makevectors(this.angles);
bool wall_dodge = false;
if(!PHYS_DODGING_AIR)
- if(!check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD))
+ if(!is_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD))
{
- wall_dodge = check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD);
+ wall_dodge = (PHYS_DODGING_WALL && is_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD));
if(!wall_dodge) // we're not on the ground, and wall dodging isn't allowed, end it!
- return true;
+ return false;
}
if(!wall_dodge && PHYS_DODGING_MAXSPEED && vdist(this.velocity, >, PHYS_DODGING_MAXSPEED))
return false;
- float tap_direction_x = 0;
- float tap_direction_y = 0;
- bool dodge_detected = false;
+ this.last_dodging_time = time;
- #define X(COND,BTN,RESULT) \
- if (this.movement_##COND) \
- /* is this a state change? */ \
- if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \
- tap_direction_##RESULT; \
- if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) \
- dodge_detected = true; \
- if(PHYS_INPUT_BUTTON_DODGE(this)) \
- dodge_detected = true; \
- this.last_##BTN##_KEY_time = time; \
- }
- X(x < 0, BACKWARD, x--);
- X(x > 0, FORWARD, x++);
- X(y < 0, LEFT, y--);
- X(y > 0, RIGHT, y++);
- #undef X
+ this.dodging_action = 1;
+ this.dodging_single_action = 1;
- if (dodge_detected)
- {
- this.last_dodging_time = time;
+ //this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
+ this.dodging_force_total = determine_speed(this);
+ this.dodging_force_remaining = this.dodging_force_total;
- this.dodging_action = 1;
- this.dodging_single_action = 1;
+ this.dodging_direction.x = tap_direction_x;
+ this.dodging_direction.y = tap_direction_y;
- this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
+ // normalize the dodging_direction vector.. (unlike UT99) XD
+ float length = sqrt(this.dodging_direction.x ** 2 + this.dodging_direction.y ** 2);
- this.dodging_direction_x = tap_direction_x;
- this.dodging_direction_y = tap_direction_y;
+ this.dodging_direction_x = this.dodging_direction_x / length;
+ this.dodging_direction_y = this.dodging_direction_y / length;
- // normalize the dodging_direction vector.. (unlike UT99) XD
- float length = this.dodging_direction_x * this.dodging_direction_x
- + this.dodging_direction_y * this.dodging_direction_y;
- length = sqrt(length);
-
- this.dodging_direction_x = this.dodging_direction_x * 1.0 / length;
- this.dodging_direction_y = this.dodging_direction_y * 1.0 / length;
- return true;
- }
- return false;
+ return true;
}
void PM_dodging(entity this)
{
- if (!PHYS_DODGING)
- return;
+ // can't use return value from PM_dodging_checkpressedkeys because they're called from different hooks
+ if (!this.dodging_action) return;
// when swimming or dead, no dodging allowed..
if (this.waterlevel >= WATERLEVEL_SWIMMING || IS_DEAD(this))
else
makevectors(this.angles);
+ // fraction of the force to apply each frame
// if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
// will be called ramp_time/frametime times = 2 times. so, we need to
// add 0.5 * the total speed each frame until the dodge action is done..
float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
+ common_factor /= 2; // FIXME this is WRONG but this function is called twice per frame!!!
// if ramp time is smaller than frametime we get problems ;D
common_factor = min(common_factor, 1);
- float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
+ /*float horiz_speed = determine_speed(this); // TODO kill this
+ //LOG_INFOF("velocity %f -> force %f\n", vlen(vec2(this.velocity)), horiz_speed);
float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed);
new_velocity_gain = max(0, new_velocity_gain);
- float velocity_difference = this.dodging_velocity_gain - new_velocity_gain;
+ float velocity_difference = this.dodging_velocity_gain - new_velocity_gain;*/
- // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
- if (this.dodging_action == 1)
- {
- //disable jump key during dodge accel phase
- if(this.movement_z > 0) { this.movement_z = 0; }
+ /*float velocity_increase = min(common_factor * this.dodging_force_total, this.dodging_force_remaining;
+ this.dodging_force_remaining -= velocity_increase;*/
- this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right)
- + ((this.dodging_direction_x * velocity_difference) * v_forward);
+ /*this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right)
+ + ((this.dodging_direction_x * velocity_difference) * v_forward);
- this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference;
- }
+ this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference;*/
+
+ float velocity_increase = common_factor * this.dodging_force_total;
+ LOG_INFOF("time %f velocity_increase: %f\n", time, velocity_increase);
+ this.velocity += this.dodging_direction_x * velocity_increase * v_forward
+ + this.dodging_direction_y * velocity_increase * v_right;
// the up part of the dodge is a single shot action
if (this.dodging_single_action == 1)
}
// are we done with the dodging ramp yet?
- if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
+ if((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME)
{
// reset state so next dodge can be done correctly
this.dodging_action = 0;
- this.dodging_direction_x = 0;
- this.dodging_direction_y = 0;
+ this.dodging_direction.x = 0;
+ this.dodging_direction.y = 0;
}
}
+#ifdef CSQC
void PM_dodging_GetPressedKeys(entity this)
{
-#ifdef CSQC
- if(!PHYS_DODGING) { return; }
-
PM_dodging_checkpressedkeys(this);
int keys = this.pressedkeys;
- keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0);
- keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0);
- keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0);
- keys = BITSET(keys, KEY_LEFT, this.movement.y < 0);
+ 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_RIGHT, PHYS_CS(this).movement.y > 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_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
this.pressedkeys = keys;
-#endif
}
+#endif
MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
{
- entity player = M_ARGV(0, entity);
+ entity player = M_ARGV(0, entity);
+ //LOG_INFOF("player %s, physics %f\n", player.netname, time);
- // print("dodging_PlayerPhysics\n");
+#ifdef CSQC
PM_dodging_GetPressedKeys(player);
+#endif
PM_dodging(player);
}
MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
{
entity player = M_ARGV(0, entity);
+ //LOG_INFOF("player %s, keys %f\n", player.netname, time);
PM_dodging_checkpressedkeys(player);
}