.float bouncefactor;
#endif
-#ifdef SVQC
-.bool move_qcphysics;
-#endif
-
void set_movetype(entity this, int mt);
.float move_movetype;
}
}
- if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0)
- pmove_waterjumptime = 0;
+ if (IS_ONGROUND(this) || this.velocity.z <= 0 || PHYS_WATERJUMP_TIME(this) <= 0)
+ PHYS_WATERJUMP_TIME(this) = 0;
#endif
}
#ifdef SVQC
PHYS_TELEPORT_TIME(this) = time + 2; // safety net
#elif defined(CSQC)
- pmove_waterjumptime = 2;
+ PHYS_WATERJUMP_TIME(this) = 2;
#endif
}
}
#endif
}
-void PM_fly(entity this, float maxspd_mod)
-{
- // noclipping or flying
- UNSET_ONGROUND(this);
-
- this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
- makevectors(this.v_angle);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y
- + '0 0 1' * this.movement.z;
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
- if(pmove_waterjumptime <= 0)
-#endif
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
-}
-
-void PM_swim(entity this, float maxspd_mod)
-{
- // swimming
- UNSET_ONGROUND(this);
-
- float jump = PHYS_INPUT_BUTTON_JUMP(this);
- // water jump only in certain situations
- // this mimics quakeworld code
- if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc)
- {
- vector yawangles = '0 1 0' * this.v_angle.y;
- makevectors(yawangles);
- vector forward = v_forward;
- vector spot = this.origin + 24 * forward;
- spot_z += 8;
- traceline(spot, spot, MOVE_NOMONSTERS, this);
- if (trace_startsolid)
- {
- spot_z += 24;
- traceline(spot, spot, MOVE_NOMONSTERS, this);
- if (!trace_startsolid)
- {
- this.velocity = forward * 50;
- this.velocity_z = 310;
- UNSET_ONGROUND(this);
- SET_JUMP_HELD(this);
- }
- }
- }
- makevectors(this.v_angle);
- float wishdown = this.movement.z;
- if(PHYS_INPUT_BUTTON_CROUCH(this))
- wishdown = -PHYS_MAXSPEED(this);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y
- + '0 0 1' * wishdown;
- if(this.viewloc)
- wishvel.z = -160; // drift anyway
- else if (wishvel == '0 0 0')
- wishvel = '0 0 -60'; // drift towards bottom
-
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7;
-
-// if (pmove_waterjumptime <= 0) // TODO: use
- {
- // water friction
- float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this);
- f = min(max(0, f), 1);
- this.velocity *= f;
-
- f = wishspeed - this.velocity * wishdir;
- if (f > 0)
- {
- float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f);
- this.velocity += accelspeed * wishdir;
- }
-
- // holding jump button swims upward slowly
- if (jump && !this.viewloc)
- {
-#if 0
- if (this.watertype & CONTENT_LAVA)
- this.velocity_z = 50;
- else if (this.watertype & CONTENT_SLIME)
- this.velocity_z = 80;
- else
- {
- if (IS_NEXUIZ_DERIVED(gamemode))
-#endif
- if(this.waterlevel >= WATERLEVEL_SUBMERGED)
- this.velocity_z = PHYS_MAXSPEED(this);
- else
- this.velocity_z = 200;
-#if 0
- else
- this.velocity_z = 100;
- }
-#endif
- }
- }
- if(this.viewloc)
- {
- const float addspeed = wishspeed - this.velocity * wishdir;
- if (addspeed > 0)
- {
- const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
- this.velocity += accelspeed * wishdir;
- }
- }
- else
- {
- // water acceleration
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0);
- }
-}
-
.vector oldmovement;
-void PM_ladder(entity this, float maxspd_mod)
-{
- // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water
- UNSET_ONGROUND(this);
-
- float g;
- g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH;
- if (PHYS_ENTGRAVITY(this))
- g *= PHYS_ENTGRAVITY(this);
- if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
- {
- g *= 0.5;
- this.velocity_z += g;
- }
-
- this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this));
- makevectors(this.v_angle);
- //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
- vector wishvel = v_forward * this.movement_x
- + v_right * this.movement_y
- + '0 0 1' * this.movement_z;
- if(this.viewloc)
- wishvel.z = this.oldmovement.x;
- this.velocity_z += g;
- if (this.ladder_entity.classname == "func_water")
- {
- float f = vlen(wishvel);
- if (f > this.ladder_entity.speed)
- wishvel *= (this.ladder_entity.speed / f);
-
- this.watertype = this.ladder_entity.skin;
- f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
- if ((this.origin_z + this.view_ofs_z) < f)
- this.waterlevel = WATERLEVEL_SUBMERGED;
- else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f)
- this.waterlevel = WATERLEVEL_SWIMMING;
- else if ((this.origin_z + this.mins_z + 1) < f)
- this.waterlevel = WATERLEVEL_WETFEET;
- else
- {
- this.waterlevel = WATERLEVEL_NONE;
- this.watertype = CONTENT_EMPTY;
- }
- }
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod);
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
- if(pmove_waterjumptime <= 0)
-#endif
- // water acceleration
- PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0);
-}
void PM_jetpack(entity this, float maxspd_mod)
{
}
}
-void PM_walk(entity this, float maxspd_mod)
-{
- if (!WAS_ONGROUND(this))
- {
-#ifdef SVQC
- if (autocvar_speedmeter)
- LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n"));
-#endif
- if (this.lastground < time - 0.3)
- this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
-#ifdef SVQC
- if (this.jumppadcount > 1)
- LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n"));
- this.jumppadcount = 0;
-#endif
- }
-
- // walking
- makevectors(this.v_angle.y * '0 1 0');
- const vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y;
- // acceleration
- const vector wishdir = normalize(wishvel);
- float wishspeed = vlen(wishvel);
- wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod);
- if (IS_DUCKED(this)) wishspeed *= 0.5;
-
- // apply edge friction
- const float f2 = vlen2(vec2(this.velocity));
- if (f2 > 0)
- {
- trace_dphitq3surfaceflags = 0;
- tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
- // TODO: apply edge friction
- // apply ground friction
- const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
- ? PHYS_FRICTION_SLICK(this)
- : PHYS_FRICTION(this);
-
- float f = sqrt(f2);
- f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
- f = max(0, f);
- this.velocity *= f;
- /*
- Mathematical analysis time!
-
- Our goal is to invert this mess.
-
- For the two cases we get:
- v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
- = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
- v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
- and
- v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
-
- These cases would be chosen ONLY if:
- v0 < PHYS_STOPSPEED(this)
- v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
- v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- and, respectively:
- v0 >= PHYS_STOPSPEED(this)
- v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
- v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this))
- */
- }
- const float addspeed = wishspeed - this.velocity * wishdir;
- if (addspeed > 0)
- {
- const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
- this.velocity += accelspeed * wishdir;
- }
-}
-
-void PM_air(entity this, float buttons_prev, float maxspd_mod)
-{
- makevectors(this.v_angle.y * '0 1 0');
- vector wishvel = v_forward * this.movement.x
- + v_right * this.movement.y;
- // acceleration
- vector wishdir = normalize(wishvel);
- float wishspeed = vlen(wishvel);
-
-#ifdef SVQC
- if(time >= PHYS_TELEPORT_TIME(this))
-#elif defined(CSQC)
- if(pmove_waterjumptime <= 0)
-#endif
- {
- float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1);
-
- // apply air speed limit
- float airaccelqw = PHYS_AIRACCEL_QW(this);
- float wishspeed0 = wishspeed;
- wishspeed = min(wishspeed, maxairspd);
- if (IS_DUCKED(this))
- wishspeed *= 0.5;
- float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1);
-
- float accelerating = (this.velocity * wishdir > 0);
- float wishspeed2 = wishspeed;
-
- // CPM: air control
- if (PHYS_AIRSTOPACCELERATE(this))
- {
- vector curdir = normalize(vec2(this.velocity));
- airaccel += (PHYS_AIRSTOPACCELERATE(this)*maxspd_mod - airaccel) * max(0, -(curdir * wishdir));
- }
- // note that for straight forward jumping:
- // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
- // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
- // -->
- // dv/dt = accel * maxspeed (when slow)
- // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
- // log dv/dt = logaccel + logmaxspeed (when slow)
- // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
- float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
- if (PHYS_MAXAIRSTRAFESPEED(this))
- wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod));
- if (PHYS_AIRSTRAFEACCELERATE(this))
- airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod);
- if (PHYS_AIRSTRAFEACCEL_QW(this))
- airaccelqw =
- (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
- *
- (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
- // !CPM
-
- if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0)
- PM_AirAccelerate(this, wishdir, wishspeed2);
- else {
- float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0;
- PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), sidefric, PHYS_AIRSPEEDLIMIT_NONQW(this));
- }
-
- if (PHYS_AIRCONTROL(this))
- CPM_PM_Aircontrol(this, wishdir, wishspeed2);
- }
-}
-
// used for calculating airshots
bool IsFlying(entity this)
{
return true;
}
-void PM_Main(entity this)
-{
- int buttons = PHYS_INPUT_BUTTON_MASK(this);
-#ifdef CSQC
- this.items = STAT(ITEMS);
-
- this.movement = PHYS_INPUT_MOVEVALUES(this);
-
- this.spectatorspeed = STAT(SPECTATORSPEED);
-
- this.team = myteam + 1; // is this correct?
- if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
- UNSET_JUMP_HELD(this); // canjump = true
- pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH;
-
- PM_ClientMovement_UpdateStatus(this);
-#endif
-
- this.oldmovement = this.movement;
-
-
-#ifdef SVQC
- WarpZone_PlayerPhysics_FixVAngle(this);
-#endif
- float maxspeed_mod = 1;
- maxspeed_mod *= PHYS_HIGHSPEED(this);
-
-#ifdef SVQC
- Physics_UpdateStats(this, maxspeed_mod);
-
- if (this.PlayerPhysplug)
- if (this.PlayerPhysplug(this))
- return;
-#elif defined(CSQC)
- if(hud != HUD_NORMAL)
- return; // no vehicle prediction (yet)
-#endif
-
-#ifdef SVQC
- anticheat_physics(this);
-#endif
-
- if (PM_check_specialcommand(this, buttons))
- return;
-#ifdef SVQC
- if (sv_maxidle > 0)
- {
- if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old)
- this.parm_idlesince = time;
- }
-#endif
- int buttons_prev = this.buttons_old;
- this.buttons_old = buttons;
- this.movement_old = this.movement;
- this.v_angle_old = this.v_angle;
-
- PM_check_nickspam(this);
-
- PM_check_punch(this);
-#ifdef SVQC
- if (IS_BOT_CLIENT(this))
- {
- if (playerdemo_read(this))
- return;
- bot_think(this);
- }
-#endif
-
-#ifdef SVQC
- if (IS_PLAYER(this))
- {
- const bool allowed_to_move = (time >= game_starttime);
- if (!allowed_to_move)
- {
- this.velocity = '0 0 0';
- set_movetype(this, MOVETYPE_NONE);
- this.disableclientprediction = 2;
- }
- else if (this.disableclientprediction == 2)
- {
- if (this.move_movetype == MOVETYPE_NONE)
- set_movetype(this, MOVETYPE_WALK);
- this.disableclientprediction = 0;
- }
- }
-#endif
-
-#ifdef SVQC
- if (this.move_movetype == MOVETYPE_NONE)
- return;
-
- // when we get here, disableclientprediction cannot be 2
- this.disableclientprediction = (this.move_qcphysics) ? -1 : 0;
-#endif
-
- viewloc_PlayerPhysics(this);
-
- PM_check_frozen(this);
-
- PM_check_blocked(this);
-
- maxspeed_mod = 1;
-
- if (this.in_swamp)
- maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate");
-
- // conveyors: first fix velocity
- if (this.conveyor.state)
- this.velocity -= this.conveyor.movedir;
-
- MUTATOR_CALLHOOK(PlayerPhysics, this);
-
- if (!IS_PLAYER(this))
- {
-#ifdef SVQC
- maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
- if (!this.spectatorspeed)
- this.spectatorspeed = maxspeed_mod;
- if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229))
- {
- if (this.lastclassname != STR_PLAYER)
- {
- if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209))
- this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
- else if (this.impulse == 11)
- this.spectatorspeed = maxspeed_mod;
- else if (this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229))
- this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
- else if (this.impulse >= 1 && this.impulse <= 9)
- this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
- } // otherwise just clear
- this.impulse = 0;
- }
-#endif
- maxspeed_mod = this.spectatorspeed;
- }
-#ifdef SVQC
-
- float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
- if(this.speed != spd)
- {
- this.speed = spd;
- string temps = ftos(spd);
- stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
- stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
- }
-
- if(this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min)
- {
- this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
- stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
- }
- if(this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max)
- {
- this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
- stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
- }
-#endif
-
- if(IS_DEAD(this))
- {
- // handle water here
- vector midpoint = ((this.absmin + this.absmax) * 0.5);
- if(pointcontents(midpoint) == CONTENT_WATER)
- {
- this.velocity = this.velocity * 0.5;
-
- // do we want this?
- //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
- //{ this.velocity_z = 70; }
- }
- goto end;
- }
-
-#ifdef SVQC
- if (!this.fixangle)
- this.angles = '0 1 0' * this.v_angle.y;
-#endif
-
- if (IS_PLAYER(this) && IS_ONGROUND(this))
- {
- PM_check_hitground(this);
- PM_Footsteps(this);
- }
-
-#ifdef SVQC
- if(IsFlying(this))
- this.wasFlying = 1;
-#endif
-
- if (IS_PLAYER(this))
- CheckPlayerJump(this);
-
- if (this.flags & FL_WATERJUMP)
- {
- this.velocity_x = this.movedir.x;
- this.velocity_y = this.movedir.y;
- if (this.waterlevel == WATERLEVEL_NONE
- #ifdef CSQC
- || pmove_waterjumptime <= 0
- #elif defined(SVQC)
- || time > PHYS_TELEPORT_TIME(this)
- #endif
- )
- {
- this.flags &= ~FL_WATERJUMP;
- #ifdef CSQC
- pmove_waterjumptime = 0;
- #elif defined(CSQC)
- PHYS_TELEPORT_TIME(this) = 0;
- #endif
- }
- }
-
- else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod))
- { }
-
-#ifdef SVQC
- else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#elif defined(CSQC)
- else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this))
-#endif
- PM_fly(this, maxspeed_mod);
-
- else if (this.waterlevel >= WATERLEVEL_SWIMMING)
- PM_swim(this, maxspeed_mod);
-
- else if (time < this.ladder_time)
- PM_ladder(this, maxspeed_mod);
-
- else if (ITEMS_STAT(this) & IT_USING_JETPACK)
- PM_jetpack(this, maxspeed_mod);
-
- else if (IS_ONGROUND(this))
- PM_walk(this, maxspeed_mod);
-
- else
- PM_air(this, buttons_prev, maxspeed_mod);
-
-LABEL(end)
- if (IS_ONGROUND(this))
- this.lastground = time;
-
- // conveyors: then break velocity again
- if(this.conveyor.state)
- this.velocity += this.conveyor.movedir;
-
- this.lastflags = this.flags;
-
- this.lastclassname = this.classname;
-}
+void sys_phys_update(entity this, float dt);
#if defined(SVQC)
void SV_PlayerPhysics(entity this)
#elif defined(CSQC)
void CSQC_ClientMovement_PlayerMove_Frame(entity this)
#endif
{
- PM_Main(this);
+ sys_phys_update(this, PHYS_INPUT_TIMELENGTH);
#ifdef SVQC
this.pm_frametime = frametime;
#define ITEMS_STAT(s) ((s).items)
.float teleport_time;
+#define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
+
+.float waterjump_time;
+#define PHYS_WATERJUMP_TIME(s) ((s).waterjump_time)
#ifdef CSQC
+ #define PHYS_FIXANGLE(s) ('0 0 0')
+ #define PHYS_MOVETYPE(s) ((s).move_movetype)
+
string autocvar_cl_jumpspeedcap_min;
string autocvar_cl_jumpspeedcap_max;
- noref float pmove_waterjumptime;
-
const int FL_WATERJUMP = 2048; // player jumping out of water
const int FL_JUMPRELEASED = 4096; // for jump debouncing
#define PHYS_GRAVITY(s) STAT(MOVEVARS_GRAVITY, s)
- #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
-
#define TICRATE ticrate
#define PHYS_INPUT_ANGLES(s) input_angles
#elif defined(SVQC)
+ #define PHYS_FIXANGLE(s) ((s).fixangle)
+ #define PHYS_MOVETYPE(s) ((s).movetype)
+
bool Physics_Valid(string thecvar);
.float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW);
.string jumpspeedcap_min;
.string jumpspeedcap_max;
- #define PHYS_TELEPORT_TIME(s) ((s).teleport_time)
-
#define PHYS_GRAVITY(s) autocvar_sv_gravity
#define TICRATE sys_frametime
if (toucher.flags & FL_PROJECTILE)
{
toucher.angles = vectoangles (toucher.velocity);
+ toucher.com_phys_gravity_factor = 1;
switch(toucher.move_movetype)
{
case MOVETYPE_FLY:
W_Electro_Explode(this, toucher);
}
+
+void sys_phys_update_single(entity this);
+
void W_Electro_Bolt_Think(entity this)
{
+ // sys_phys_update_single(this);
if(time >= this.ltime)
{
this.use(this, NULL, NULL);
{ this.nextthink = min(time + WEP_CVAR_PRI(electro, midaircombo_interval), this.ltime); }
}
else { this.nextthink = this.ltime; }
+ // this.nextthink = time;
}
void W_Electro_Attack_Bolt(Weapon thiswep, entity actor)
proj.projectiledeathtype = WEP_ELECTRO.m_id;
setorigin(proj, w_shotorg);
+ // if (IS_CSQC)
set_movetype(proj, MOVETYPE_FLY);
W_SetupProjVelocity_PRI(proj, electro);
proj.angles = vectoangles(proj.velocity);
CSQCProjectile(proj, true, PROJECTILE_ELECTRO_BEAM, true);
MUTATOR_CALLHOOK(EditProjectile, actor, proj);
+ // proj.com_phys_pos = proj.origin;
+ // proj.com_phys_vel = proj.velocity;
}
void W_Electro_Orb_Stick(entity this, entity to)
# SVQC
+Main loop:
+* SV_Physics()
+ * StartFrame()
+ * if (force_retouch)
+ * foreach entity:
+ * .touch()
+ * foreach client:
+ * PlayerPreThink()
+ * .think()
+ * PlayerPostThink()
+ * foreach nonclient:
+ * .think()
+ * EndFrame()
+
+
```
.entity clientcamera;
### listening
entity listener = new_pure(someListener);
- listener.evt_$event = void(entity this) { code; };
- subscribe(listener, $event);
+ subscribe(listener, $event, void(entity this) { code; });
-/** Components always interpolate from the previous state */
-#define COMPONENT(com) \
- void com_##com##_interpolate(entity it, float a); \
- .bool com_##com
-
-#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body)
-
-
-#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T
-
-#define emit(T, ...) \
- MACRO_BEGIN \
- FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \
- MACRO_END
-
-#define subscribe(listener, T) \
- MACRO_BEGIN \
- listener.evt_##T##_listener = true; \
- MACRO_END
-
-
-/**
- * framelimit 0 is no limit, interpolation does not apply
- * framerate below minfps will result in less than 100% speed
- */
-#define SYSTEM(sys, frameLimit, minfps) \
- void sys_##sys##_update(entity this, float dt); \
- float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
- float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))
-
-#define SYSTEM_UPDATE(sys) \
- MACRO_BEGIN \
- static float t = 0; \
- float dt = autocvar_xon_sys_##sys##_dt; \
- float minfps = autocvar_xon_sys_##sys##_minfps; \
- static float accumulator = 0; \
- float a = 0; \
- if (dt) { \
- accumulator += min(frametime, 1 / (minfps)); \
- } else { \
- accumulator += frametime; \
- dt = accumulator; \
- a = 1; \
- } \
- while (accumulator >= dt) \
- { \
- time = t; \
- FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \
- t += dt; \
- accumulator -= dt; \
- } \
- if (!a) a = accumulator / dt; \
- FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \
- MACRO_END
-
+#include "_lib.qh"
#include "_mod.inc"
#include "components/_mod.inc"
--- /dev/null
+#pragma once
+
+/** Components always interpolate from the previous state */
+#define COMPONENT(com) \
+ void com_##com##_interpolate(entity it, float a); \
+ .bool com_##com
+
+#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body)
+
+
+#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T
+
+#define emit(T, ...) \
+ MACRO_BEGIN \
+ FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \
+ MACRO_END
+
+#define subscribe(listener, T, fn) \
+ MACRO_BEGIN \
+ listener.evt_##T = (fn); \
+ listener.evt_##T##_listener = true; \
+ MACRO_END
+
+
+/**
+ * framelimit 0 is no limit, interpolation does not apply
+ * framerate below minfps will result in less than 100% speed
+ */
+#define SYSTEM(sys, frameLimit, minfps) \
+ void sys_##sys##_update(entity this, float dt); \
+ float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
+ float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))
+
+#define SYSTEM_UPDATE(sys) \
+ MACRO_BEGIN \
+ static float t = 0; \
+ float dt = autocvar_xon_sys_##sys##_dt; \
+ float minfps = autocvar_xon_sys_##sys##_minfps; \
+ static float accumulator = 0; \
+ float a = 0; \
+ if (dt) { \
+ accumulator += min(frametime, 1 / (minfps)); \
+ } else { \
+ accumulator += frametime; \
+ dt = accumulator; \
+ a = 1; \
+ } \
+ while (accumulator >= dt) \
+ { \
+ time = t; \
+ FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \
+ t += dt; \
+ accumulator -= dt; \
+ } \
+ if (!a) a = accumulator / dt; \
+ FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \
+ MACRO_END
#pragma once
COMPONENT(in);
-.vector com_in_move;
+.vector com_in_move, com_in_move_prev;
.vector com_in_angles;
.bool com_in_jump;
+.bool com_in_crouch;
.vector com_phys_pos, com_phys_pos_prev;
.vector com_phys_ang, com_phys_ang_prev;
.vector com_phys_vel;
+.float com_phys_vel_max;
+.float com_phys_vel_max_air;
+.float com_phys_vel_max_air_strafe;
.vector com_phys_acc;
+.float com_phys_acc_rate;
+.float com_phys_acc_rate_air;
+.float com_phys_acc_rate_air_strafe;
+.float com_phys_acc_rate_air_stop;
+.float com_phys_friction;
+
+.vector com_phys_gravity;
+.float com_phys_gravity_factor;
+// TODO: remove
+.bool com_phys_ground;
+.bool com_phys_air;
+.bool com_phys_ladder;
+.bool com_phys_vel_2d;
+.bool com_phys_water;
+.bool com_phys_friction_air;
+.bool move_qcphysics;
// generated file; do not modify
+#include <ecs/events/physics.qc>
// generated file; do not modify
+#include <ecs/events/physics.qh>
--- /dev/null
+#include "physics.qh"
--- /dev/null
+#pragma once
+
+EVENT(phys_land, (entity this));
// generated file; do not modify
+#include <ecs/systems/input.qc>
#include <ecs/systems/physics.qc>
+#ifdef CSQC
+ #include <ecs/systems/cl_physics.qc>
+#endif
+#ifdef SVQC
+ #include <ecs/systems/sv_physics.qc>
+#endif
// generated file; do not modify
+#include <ecs/systems/input.qh>
#include <ecs/systems/physics.qh>
--- /dev/null
+#include "physics.qh"
+
+void sys_phys_fix(entity this, float dt)
+{
+ this.team = myteam + 1; // is this correct?
+ PHYS_WATERJUMP_TIME(this) -= dt;
+ this.oldmovement = this.movement;
+ this.movement = PHYS_INPUT_MOVEVALUES(this);
+ this.items = STAT(ITEMS, this);
+ this.spectatorspeed = STAT(SPECTATORSPEED, this);
+ if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump
+ UNSET_JUMP_HELD(this); // canjump = true
+ PM_ClientMovement_UpdateStatus(this);
+}
+
+bool sys_phys_override(entity this)
+{
+ // no vehicle prediction
+ return hud != HUD_NORMAL;
+}
+
+void sys_phys_monitor(entity this) {}
+
+void sys_phys_ai(entity this) {}
+
+void sys_phys_pregame_hold(entity this) {}
+
+void sys_phys_spectator_control(entity this) {}
+
+void sys_phys_fixspeed(entity this, float maxspeed_mod) {}
--- /dev/null
+#include "input.qh"
+
+void sys_in_update(entity this, float dt)
+{
+ this.com_in_jump = PHYS_INPUT_BUTTON_JUMP(this);
+ this.com_in_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
+}
--- /dev/null
+#pragma once
+
+SYSTEM(in, 30, 10);
#include "physics.qh"
+#include "input.qh"
+
+.int disableclientprediction;
+
+void sys_phys_simulate(entity this, float dt);
+void sys_phys_simulate_simple(entity this, float dt);
void sys_phys_update(entity this, float dt)
{
- PM_Main(this);
+ if (!IS_CLIENT(this)) {
+ sys_phys_simulate_simple(this, dt);
+ return;
+ }
+ sys_in_update(this, dt);
+
+ sys_phys_fix(this, dt);
+ if (sys_phys_override(this)) { return; } sys_phys_monitor(this);
+
+ this.buttons_old = PHYS_INPUT_BUTTON_MASK(this);
+ this.movement_old = this.movement;
+ this.v_angle_old = this.v_angle;
+
+ sys_phys_ai(this);
+
+ sys_phys_pregame_hold(this);
+
+ if (IS_SVQC) {
+ if (PHYS_MOVETYPE(this) == MOVETYPE_NONE) { return; }
+ // when we get here, disableclientprediction cannot be 2
+ this.disableclientprediction = (this.move_qcphysics) ? -1 : 0;
+ }
+
+ viewloc_PlayerPhysics(this);
+
+ PM_check_frozen(this);
+
+ PM_check_blocked(this);
+
+ float maxspeed_mod = (!this.in_swamp) ? 1 : this.swamp_slowdown; // cvar("g_balance_swamp_moverate");
+
+// conveyors: first fix velocity
+ if (this.conveyor.state) { this.velocity -= this.conveyor.movedir; }
+ MUTATOR_CALLHOOK(PlayerPhysics, this);
+
+ if (!IS_PLAYER(this)) {
+ sys_phys_spectator_control(this);
+ maxspeed_mod = this.spectatorspeed;
+ }
+ sys_phys_fixspeed(this, maxspeed_mod);
+
+ if (IS_DEAD(this)) {
+ // handle water here
+ vector midpoint = ((this.absmin + this.absmax) * 0.5);
+ if (pointcontents(midpoint) == CONTENT_WATER) {
+ this.velocity = this.velocity * 0.5;
+
+ // do we want this?
+ // if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER)
+ // { this.velocity_z = 70; }
+ }
+ goto end;
+ }
+
+ if (IS_SVQC && !PHYS_FIXANGLE(this)) { this.angles = '0 1 0' * this.v_angle.y; }
+ if (IS_PLAYER(this)) {
+ if (IS_ONGROUND(this)) {
+ PM_check_hitground(this);
+ PM_Footsteps(this);
+ } else if (IsFlying(this)) {
+ this.wasFlying = true;
+ }
+ CheckPlayerJump(this);
+ }
+
+ if (this.flags & FL_WATERJUMP) {
+ this.velocity_x = this.movedir.x;
+ this.velocity_y = this.movedir.y;
+ if (this.waterlevel == WATERLEVEL_NONE
+ || time > PHYS_TELEPORT_TIME(this)
+ || PHYS_WATERJUMP_TIME(this) <= 0
+ ) {
+ this.flags &= ~FL_WATERJUMP;
+ PHYS_TELEPORT_TIME(this) = 0;
+ PHYS_WATERJUMP_TIME(this) = 0;
+ }
+ } else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod)) {
+ // handled
+ } else if (PHYS_MOVETYPE(this) == MOVETYPE_NOCLIP
+ || PHYS_MOVETYPE(this) == MOVETYPE_FLY
+ || PHYS_MOVETYPE(this) == MOVETYPE_FLY_WORLDONLY
+ || MUTATOR_CALLHOOK(IsFlying, this)) {
+ this.com_phys_friction = PHYS_FRICTION(this);
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+ this.com_phys_friction_air = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_friction_air = false;
+ } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+ this.com_phys_water = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_water = false;
+ } else if (time < this.ladder_time) {
+ this.com_phys_friction = PHYS_FRICTION(this);
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod;
+ this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt;
+ if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); }
+ this.com_phys_ladder = true;
+ this.com_phys_friction_air = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_friction_air = false;
+ this.com_phys_ladder = false;
+ this.com_phys_gravity = '0 0 0';
+ } else if (ITEMS_STAT(this) & IT_USING_JETPACK) {
+ PM_jetpack(this, maxspeed_mod);
+ } else if (IS_ONGROUND(this)) {
+ if (!WAS_ONGROUND(this)) {
+ emit(phys_land, this);
+ if (this.lastground < time - 0.3) {
+ this.velocity *= (1 - PHYS_FRICTION_ONLAND(this));
+ }
+ }
+ this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod;
+ this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt;
+ if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); }
+ this.com_phys_ground = true;
+ this.com_phys_vel_2d = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_vel_2d = false;
+ this.com_phys_ground = false;
+ this.com_phys_gravity = '0 0 0';
+ } else {
+ this.com_phys_acc_rate_air = PHYS_AIRACCELERATE(this) * min(maxspeed_mod, 1);
+ this.com_phys_acc_rate_air_stop = PHYS_AIRSTOPACCELERATE(this) * maxspeed_mod;
+ this.com_phys_acc_rate_air_strafe = PHYS_AIRSTRAFEACCELERATE(this) * maxspeed_mod;
+ this.com_phys_vel_max_air_strafe = PHYS_MAXAIRSTRAFESPEED(this) * maxspeed_mod;
+ this.com_phys_vel_max_air = PHYS_MAXAIRSPEED(this) * maxspeed_mod;
+ this.com_phys_vel_max = PHYS_MAXAIRSPEED(this) * min(maxspeed_mod, 1);
+ this.com_phys_air = true;
+ this.com_phys_vel_2d = true;
+ sys_phys_simulate(this, dt);
+ this.com_phys_vel_2d = false;
+ this.com_phys_air = false;
+ }
+
+ LABEL(end)
+ if (IS_ONGROUND(this)) { this.lastground = time; }
+// conveyors: then break velocity again
+ if (this.conveyor.state) { this.velocity += this.conveyor.movedir; }
+ this.lastflags = this.flags;
+
+ this.lastclassname = this.classname;
+}
+
+/** for players */
+void sys_phys_simulate(entity this, float dt)
+{
+ const vector g = -this.com_phys_gravity;
+ const bool jump = this.com_in_jump;
+
+ if (!this.com_phys_ground && !this.com_phys_air) {
+ // noclipping
+ // flying
+ // on a spawnfunc_func_ladder
+ // swimming in spawnfunc_func_water
+ // swimming
+ UNSET_ONGROUND(this);
+
+ if (this.com_phys_friction_air) {
+ this.velocity_z += g.z / 2;
+ this.velocity = this.velocity * (1 - dt * this.com_phys_friction);
+ this.velocity_z += g.z / 2;
+ }
+ }
+
+ if (this.com_phys_water) {
+ // water jump only in certain situations
+ // this mimics quakeworld code
+ if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc) {
+ vector yawangles = '0 1 0' * this.v_angle.y;
+ makevectors(yawangles);
+ vector forward = v_forward;
+ vector spot = this.origin + 24 * forward;
+ spot_z += 8;
+ traceline(spot, spot, MOVE_NOMONSTERS, this);
+ if (trace_startsolid) {
+ spot_z += 24;
+ traceline(spot, spot, MOVE_NOMONSTERS, this);
+ if (!trace_startsolid) {
+ this.velocity = forward * 50;
+ this.velocity_z = 310;
+ UNSET_ONGROUND(this);
+ SET_JUMP_HELD(this);
+ }
+ }
+ }
+ }
+ makevectors(vmul(this.v_angle, (this.com_phys_vel_2d ? '0 1 0' : '1 1 1')));
+ // wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z;
+ vector wishvel = v_forward * this.movement.x
+ + v_right * this.movement.y
+ + '0 0 1' * this.movement.z * (this.com_phys_vel_2d ? 0 : 1);
+ if (this.com_phys_water) {
+ if (PHYS_INPUT_BUTTON_CROUCH(this)) {
+ wishvel.z = -PHYS_MAXSPEED(this);
+ }
+ if (this.viewloc) {
+ wishvel.z = -160; // drift anyway
+ } else if (wishvel == '0 0 0') {
+ wishvel = '0 0 -60'; // drift towards bottom
+ }
+ }
+ if (this.com_phys_ladder) {
+ if (this.viewloc) {
+ wishvel.z = this.oldmovement.x;
+ }
+ if (this.ladder_entity.classname == "func_water") {
+ float f = vlen(wishvel);
+ if (f > this.ladder_entity.speed) {
+ wishvel *= (this.ladder_entity.speed / f);
+ }
+
+ this.watertype = this.ladder_entity.skin;
+ f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z;
+ if ((this.origin_z + this.view_ofs_z) < f) {
+ this.waterlevel = WATERLEVEL_SUBMERGED;
+ } else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f) {
+ this.waterlevel = WATERLEVEL_SWIMMING;
+ } else if ((this.origin_z + this.mins_z + 1) < f) {
+ this.waterlevel = WATERLEVEL_WETFEET;
+ } else {
+ this.waterlevel = WATERLEVEL_NONE;
+ this.watertype = CONTENT_EMPTY;
+ }
+ }
+ }
+ // acceleration
+ const vector wishdir = normalize(wishvel);
+ float wishspeed = min(vlen(wishvel), this.com_phys_vel_max);
+
+ if (this.com_phys_air) {
+ if ((IS_SVQC && time >= PHYS_TELEPORT_TIME(this))
+ || (IS_CSQC && PHYS_WATERJUMP_TIME(this) <= 0)) {
+ // apply air speed limit
+ float airaccelqw = PHYS_AIRACCEL_QW(this);
+ float wishspeed0 = wishspeed;
+ const float maxairspd = this.com_phys_vel_max;
+ wishspeed = min(wishspeed, maxairspd);
+ if (IS_DUCKED(this)) {
+ wishspeed *= 0.5;
+ }
+ float airaccel = this.com_phys_acc_rate_air;
+
+ float accelerating = (this.velocity * wishdir > 0);
+ float wishspeed2 = wishspeed;
+
+ // CPM: air control
+ if (PHYS_AIRSTOPACCELERATE(this)) {
+ vector curdir = normalize(vec2(this.velocity));
+ airaccel += (this.com_phys_acc_rate_air_stop - airaccel) * max(0, -(curdir * wishdir));
+ }
+ // note that for straight forward jumping:
+ // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0;
+ // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw);
+ // -->
+ // dv/dt = accel * maxspeed (when slow)
+ // dv/dt = accel * maxspeed * (1 - accelqw) (when fast)
+ // log dv/dt = logaccel + logmaxspeed (when slow)
+ // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast)
+ float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero
+ if (PHYS_MAXAIRSTRAFESPEED(this)) {
+ wishspeed =
+ min(wishspeed,
+ GeomLerp(this.com_phys_vel_max_air, strafity, this.com_phys_vel_max_air_strafe));
+ }
+ if (PHYS_AIRSTRAFEACCELERATE(this)) {
+ airaccel = GeomLerp(airaccel, strafity, this.com_phys_acc_rate_air_strafe);
+ }
+ if (PHYS_AIRSTRAFEACCEL_QW(this)) {
+ airaccelqw =
+ (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1)
+ *
+ (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this))));
+ }
+ // !CPM
+
+ if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0) {
+ PM_AirAccelerate(this, wishdir, wishspeed2);
+ } else {
+ float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0;
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw,
+ PHYS_AIRACCEL_QW_STRETCHFACTOR(this), sidefric, PHYS_AIRSPEEDLIMIT_NONQW(this));
+ }
+
+ if (PHYS_AIRCONTROL(this)) {
+ CPM_PM_Aircontrol(this, wishdir, wishspeed2);
+ }
+ }
+ } else {
+ if (this.com_phys_ground && IS_DUCKED(this)) { wishspeed *= 0.5; }
+ if (this.com_phys_water) {
+ wishspeed *= 0.7;
+
+ // if (PHYS_WATERJUMP_TIME(this) <= 0) // TODO: use
+ {
+ // water friction
+ float f = 1 - dt * PHYS_FRICTION(this);
+ f = min(max(0, f), 1);
+ this.velocity *= f;
+
+ f = wishspeed - this.velocity * wishdir;
+ if (f > 0) {
+ float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, f);
+ this.velocity += accelspeed * wishdir;
+ }
+
+ // holding jump button swims upward slowly
+ if (jump && !this.viewloc) {
+ // was:
+ // lava: 50
+ // slime: 80
+ // water: 100
+ // idea: double those
+ this.velocity_z = 200;
+ if (this.waterlevel >= WATERLEVEL_SUBMERGED) {
+ this.velocity_z = PHYS_MAXSPEED(this) * 0.7;
+ }
+ }
+ }
+ if (this.viewloc) {
+ const float addspeed = wishspeed - this.velocity * wishdir;
+ if (addspeed > 0) {
+ const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed);
+ this.velocity += accelspeed * wishdir;
+ }
+ } else {
+ // water acceleration
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
+ }
+ return;
+ }
+ if (this.com_phys_ground) {
+ // apply edge friction
+ const float f2 = vlen2(vec2(this.velocity));
+ if (f2 > 0) {
+ trace_dphitq3surfaceflags = 0;
+ tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this);
+ // TODO: apply edge friction
+ // apply ground friction
+ const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK)
+ ? PHYS_FRICTION_SLICK(this)
+ : PHYS_FRICTION(this);
+
+ float f = sqrt(f2);
+ f = 1 - dt * realfriction
+ * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1);
+ f = max(0, f);
+ this.velocity *= f;
+ /*
+ Mathematical analysis time!
+
+ Our goal is to invert this mess.
+
+ For the two cases we get:
+ v = v0 * (1 - dt * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this))
+ = v0 - dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+ v0 = v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this)
+ and
+ v = v0 * (1 - dt * PHYS_FRICTION(this))
+ v0 = v / (1 - dt * PHYS_FRICTION(this))
+
+ These cases would be chosen ONLY if:
+ v0 < PHYS_STOPSPEED(this)
+ v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this)
+ v < PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this))
+ and, respectively:
+ v0 >= PHYS_STOPSPEED(this)
+ v / (1 - dt * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this)
+ v >= PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this))
+ */
+ }
+ const float addspeed = wishspeed - this.velocity * wishdir;
+ if (addspeed > 0) {
+ const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed);
+ this.velocity += accelspeed * wishdir;
+ }
+ return;
+ }
+
+ if (IS_CSQC ? PHYS_WATERJUMP_TIME(this) <= 0 : time >= PHYS_TELEPORT_TIME(this)) {
+ PM_Accelerate(this, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0);
+ }
+ }
+}
+
+.entity groundentity;
+/** for other entities */
+void sys_phys_simulate_simple(entity this, float dt)
+{
+ vector mn = this.mins;
+ vector mx = this.maxs;
+
+ vector g = '0 0 0';
+ if (this.com_phys_gravity_factor && !g) g = '0 0 -1' * PHYS_GRAVITY(NULL);
+
+ vector acc = this.com_phys_acc;
+ vector vel = this.com_phys_vel;
+ vector pos = this.com_phys_pos;
+
+ // SV_Physics_Toss
+
+ vel += g * dt;
+
+ this.angles += dt * this.avelocity;
+ float movetime = dt;
+ for (int i = 0; i < MAX_CLIP_PLANES && movetime > 0; i++) {
+ vector push = vel * movetime;
+ vector p0 = pos;
+ vector p1 = p0 + push;
+ // SV_PushEntity
+ tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+ if (!trace_startsolid) {
+ bool hit = trace_fraction < 1;
+ pos = trace_endpos;
+ entity ent = trace_ent;
+ // SV_LinkEdict_TouchAreaGrid
+ if (this.solid != SOLID_NOT) {
+ FOREACH_ENTITY_RADIUS_ORDERED(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
+ if (it.solid != SOLID_TRIGGER || it == this) continue;
+ if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax)) {
+ // SV_LinkEdict_TouchAreaGrid_Call
+ trace_allsolid = false;
+ trace_startsolid = false;
+ trace_fraction = 1;
+ trace_inwater = false;
+ trace_inopen = true;
+ trace_endpos = it.origin;
+ trace_plane_normal = '0 0 1';
+ trace_plane_dist = 0;
+ trace_ent = this;
+ trace_dpstartcontents = 0;
+ trace_dphitcontents = 0;
+ trace_dphitq3surfaceflags = 0;
+ trace_dphittexturename = string_null;
+ gettouch(it)(this, it);
+ vel = this.velocity;
+ }
+ });
+ }
+ if (hit && this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || this.groundentity != ent)) {
+ // SV_Impact (ent, trace);
+ tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+ void(entity, entity) touched = gettouch(this);
+ if (touched && this.solid != SOLID_NOT) {
+ touched(ent, this);
+ }
+ void(entity, entity) touched2 = gettouch(ent);
+ if (this && ent && touched2 && ent.solid != SOLID_NOT) {
+ trace_endpos = ent.origin;
+ trace_plane_normal *= -1;
+ trace_plane_dist *= -1;
+ trace_ent = this;
+ trace_dpstartcontents = 0;
+ trace_dphitcontents = 0;
+ trace_dphitq3surfaceflags = 0;
+ trace_dphittexturename = string_null;
+ touched2(this, ent);
+ }
+ }
+ }
+ // end SV_PushEntity
+ if (wasfreed(this)) { return; }
+ tracebox(p0, mn, mx, p1, MOVE_NORMAL, this);
+ if (trace_fraction == 1) { break; }
+ movetime *= 1 - min(1, trace_fraction);
+ ClipVelocity(vel, trace_plane_normal, vel, 1);
+ }
+
+ this.com_phys_acc = acc;
+ this.com_phys_vel = vel;
+ this.com_phys_pos = pos;
+ setorigin(this, this.com_phys_pos);
+}
+
+void sys_phys_update_single(entity this)
+{
+ sys_phys_simulate_simple(this, frametime);
}
#pragma once
SYSTEM(phys, 30, 10);
+
+void sys_phys_fix(entity this, float dt);
+bool sys_phys_override(entity this);
+void sys_phys_monitor(entity this);
+void sys_phys_pregame_hold(entity this);
+void sys_phys_ai(entity this);
+void sys_phys_spectator_control(entity this);
+void sys_phys_fixspeed(entity this, float maxspeed_mod);
--- /dev/null
+#include "physics.qh"
+
+void sys_phys_fix(entity this, float dt)
+{
+ WarpZone_PlayerPhysics_FixVAngle(this);
+ Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
+}
+
+bool sys_phys_override(entity this)
+{
+ int buttons = PHYS_INPUT_BUTTON_MASK(this);
+ if (PM_check_specialcommand(this, buttons)) { return true; }
+ if (this.PlayerPhysplug && this.PlayerPhysplug(this)) { return true; }
+ return false;
+}
+
+void sys_phys_monitor(entity this)
+{
+ int buttons = PHYS_INPUT_BUTTON_MASK(this);
+ anticheat_physics(this);
+ if (sv_maxidle > 0) {
+ if (buttons != this.buttons_old
+ || this.movement != this.movement_old
+ || this.v_angle != this.v_angle_old) { this.parm_idlesince = time; }
+ }
+ PM_check_nickspam(this);
+ PM_check_punch(this);
+}
+
+void sys_phys_ai(entity this)
+{
+ if (!IS_BOT_CLIENT(this)) { return; }
+ if (playerdemo_read(this)) { return; }
+ bot_think(this);
+}
+
+void sys_phys_pregame_hold(entity this)
+{
+ if (!IS_PLAYER(this)) { return; }
+ const bool allowed_to_move = (time >= game_starttime);
+ if (!allowed_to_move) {
+ this.velocity = '0 0 0';
+ this.movetype = MOVETYPE_NONE;
+ this.disableclientprediction = 2;
+ } else if (this.disableclientprediction == 2) {
+ if (this.movetype == MOVETYPE_NONE) { this.movetype = MOVETYPE_WALK; }
+ this.disableclientprediction = 0;
+ }
+}
+
+void sys_phys_spectator_control(entity this)
+{
+ float maxspeed_mod = autocvar_sv_spectator_speed_multiplier;
+ if (!this.spectatorspeed) { this.spectatorspeed = maxspeed_mod; }
+ if ((this.impulse >= 1 && this.impulse <= 19)
+ || (this.impulse >= 200 && this.impulse <= 209)
+ || (this.impulse >= 220 && this.impulse <= 229)
+ ) {
+ if (this.lastclassname != STR_PLAYER) {
+ if (this.impulse == 10
+ || this.impulse == 15
+ || this.impulse == 18
+ || (this.impulse >= 200 && this.impulse <= 209)
+ ) { this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5); } else if (this.impulse == 11) {
+ this.spectatorspeed = maxspeed_mod;
+ } else if (this.impulse == 12
+ || this.impulse == 16
+ || this.impulse == 19
+ || (this.impulse >= 220 && this.impulse <= 229)
+ ) {
+ this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5);
+ } else if (this.impulse >= 1 && this.impulse <= 9) {
+ this.spectatorspeed = 1 + 0.5 * (this.impulse - 1);
+ }
+ } // otherwise just clear
+ this.impulse = 0;
+ }
+}
+
+void sys_phys_fixspeed(entity this, float maxspeed_mod)
+{
+ float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod;
+ if (this.speed != spd) {
+ this.speed = spd;
+ string temps = ftos(spd);
+ stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_backspeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_sidespeed ", temps, "\n"));
+ stuffcmd(this, strcat("cl_upspeed ", temps, "\n"));
+ }
+
+ if (this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min) {
+ this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min;
+ stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
+ }
+ if (this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max) {
+ this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max;
+ stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
+ }
+}
+
+void sys_phys_land(entity this)
+{
+ if (autocvar_speedmeter) {
+ LOG_TRACEF("landing velocity: %v (abs: %f)", this.velocity, vlen(this.velocity));
+ }
+ if (this.jumppadcount > 1) {
+ LOG_TRACEF("%dx jumppad combo", this.jumppadcount);
+ }
+ this.jumppadcount = 0;
+}
+
+STATIC_INIT(sys_phys)
+{
+ entity listener = new_pure(sys_phys);
+ subscribe(listener, phys_land, sys_phys_land);
+}
#define COMPAT_NO_MOD_IS_XONOTIC
#endif
+#ifdef CSQC
+#define IS_CSQC 1
+#else
+#define IS_CSQC 0
+#endif
+
+#ifdef SVQC
+#define IS_SVQC 1
+#else
+#define IS_SVQC 0
+#endif
+
#include "compiler.qh"
#ifndef QCC_SUPPORT_INT
#define CSQC_Ent_Remove _CSQC_Ent_Remove
#endif
#undef ENGINE_EVENT
+
+#ifndef MENUQC
+ #include <ecs/_lib.qh>
+ #include <ecs/components/_mod.qh>
+#endif
if (cond) LAMBDA(body) \
} \
} MACRO_END
+#define MUTEX_LOCK(this) MACRO_BEGIN \
+ if (this) LOG_SEVEREF("Loop mutex held by %s", this); \
+ this = __FUNC__; \
+MACRO_END
+#define MUTEX_UNLOCK(this) MACRO_BEGIN \
+ this = string_null; \
+MACRO_END
#define _FOREACH_ENTITY_FIND_UNORDERED(id, T, fld, match, cond, body) \
MACRO_BEGIN { \
- if (_FOREACH_ENTITY_FIND_##T##_##id##mutex) LOG_SEVEREF("Loop mutex held by %s", _FOREACH_ENTITY_FIND_##T##_##id##mutex); \
- _FOREACH_ENTITY_FIND_##T##_##id##mutex = __FUNC__; \
+ MUTEX_LOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \
entity _foundchain_first = _findchain##T##_tofield(fld, match, _FOREACH_ENTITY_FIND_##T##_next##id); \
FOREACH_LIST(_foundchain, _FOREACH_ENTITY_FIND_##T##_next##id, cond, body); \
- _FOREACH_ENTITY_FIND_##T##_##id##mutex = string_null; \
+ MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \
} MACRO_END
#define FOREACH_ENTITY(cond, body) ORDERED(FOREACH_ENTITY)(cond, body)
#ifndef MENUQC
entity(vector org, float rad, .entity tofield) _findchainradius_tofield = #22;
-#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body)
+#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) ORDERED(FOREACH_ENTITY_RADIUS)(org, dist, cond, body)
.entity _FOREACH_ENTITY_FIND_radius_next; noref string _FOREACH_ENTITY_FIND_radius_mutex;
#define FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body) _FOREACH_ENTITY_FIND_UNORDERED(, radius, org, dist, cond, body)
+.entity _FOREACH_ENTITY_FIND_radius_nexttmp; noref string _FOREACH_ENTITY_FIND_radius_tmpmutex;
+#define FOREACH_ENTITY_RADIUS_ORDERED(org, dist, cond, body) \
+MACRO_BEGIN \
+ entity _rev_first = NULL; \
+ _FOREACH_ENTITY_FIND_UNORDERED(tmp, radius, org, dist, cond, (it._FOREACH_ENTITY_FIND_radius_nexttmp = _rev_first, _rev_first = it)); \
+ MUTEX_LOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \
+ FOREACH_LIST(_rev, _FOREACH_ENTITY_FIND_radius_nexttmp, true, body); \
+ MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \
+MACRO_END
#endif
#define FOREACH_ENTITY_FLOAT(fld, match, body) ORDERED(FOREACH_ENTITY_FLOAT)(fld, match, body)
}
#endif
+noref vector _vmul_a, _vmul_b;
+#define vmul(a, b) \
+ (_vmul_a = (a), _vmul_b = (b), \
+ '1 0 0' * (_vmul_a.x * _vmul_b.x) \
+ + '0 1 0' * (_vmul_a.y * _vmul_b.y) \
+ + '0 0 1' * (_vmul_a.z * _vmul_b.z))
+
const vector eX = '1 0 0';
const vector eY = '0 1 0';
const vector eZ = '0 0 1';
return vel - (1 + bounce) * (vel * norm) * norm;
}
+vector vec_epsilon(vector this, float eps)
+{
+ if (this.x > -eps && this.x < eps) this.x = 0;
+ if (this.y > -eps && this.y < eps) this.y = 0;
+ if (this.z > -eps && this.z < eps) this.z = 0;
+ return this;
+}
+
+#define ClipVelocity(in, normal, out, overbounce) \
+ (out = vec_epsilon(vec_reflect(in, normal, (overbounce) - 1), 0.1))
+
#ifndef MENUQC
vector get_corner_position(entity box, int corner)
{
it.movetype = mt;
continue;
}
-
+
it.movetype = ((it.move_qcphysics) ? MOVETYPE_NONE : it.move_movetype);
if(it.move_movetype == MOVETYPE_NONE)
});
}
+void systems_update();
void EndFrame()
{
anticheat_endframe();
PlayerState s = PS(it);
s.ps_push(s, it);
});
+ systems_update();
IL_ENDFRAME();
}
// these stop the projectile from moving, so...
if(trace_dphitcontents == 0)
{
- //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
- LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %d, classname: %s, origin: %s)\n", etof(this), this.classname, vtos(this.origin));
+ LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin);
checkclient(this);
}
if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
bool autocvar_sv_autopause = false;
float RedirectionThink();
-void PM_Main(Client this);
+void systems_update();
+void sys_phys_update(entity this, float dt);
void StartFrame()
{
// TODO: if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
- FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), PM_Main(it));
+ FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), sys_phys_update(it, frametime));
FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), PlayerPreThink(it));
execute_next_frame();
sp_permit_cpp11_shift = false # false/true
# Add or remove space before '(' of 'if', 'for', 'switch', and 'while'
-sp_before_sparen = add # ignore/add/remove/force #force
+sp_before_sparen = force # ignore/add/remove/force #force
# Add or remove space inside if-condition '(' and ')'
# NOTE: is 68 worse than ignore
sp_inside_sparen_open = ignore # ignore/add/remove/force #force
# Add or remove space after ')' of 'if', 'for', 'switch', and 'while'
-sp_after_sparen = add # ignore/add/remove/force
+sp_after_sparen = force # ignore/add/remove/force
# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
-sp_sparen_brace = add # ignore/add/remove/force
+sp_sparen_brace = force # ignore/add/remove/force
# Add or remove space between 'invariant' and '(' in the D language.
sp_invariant_paren = ignore # ignore/add/remove/force #ignore
sp_macro_func = remove # ignore/add/remove/force #force
# Add or remove space between 'else' and '{' if on the same line
-sp_else_brace = add # ignore/add/remove/force
+sp_else_brace = force # ignore/add/remove/force
# Add or remove space between '}' and 'else' if on the same line
# WARNING: Code doesn't seem to use this feature - delete from the config?
-sp_brace_else = ignore # ignore/add/remove/force
+sp_brace_else = force # ignore/add/remove/force
# Add or remove space between '}' and the name of a typedef on the same line
sp_brace_typedef = add # ignore/add/remove/force
# Add or remove newline between 'if' and '{'
# NOTE: is 136 worse than ignore
-nl_if_brace = add # ignore/add/remove/force
+nl_if_brace = remove # ignore/add/remove/force
# Add or remove newline between '}' and 'else'
-nl_brace_else = add # ignore/add/remove/force
+nl_brace_else = remove # ignore/add/remove/force
# Add or remove newline between 'else if' and '{'
# If set to ignore, nl_if_brace is used instead
-nl_elseif_brace = add # ignore/add/remove/force
+nl_elseif_brace = remove # ignore/add/remove/force
# Add or remove newline between 'else' and '{'
-nl_else_brace = add # ignore/add/remove/force
+nl_else_brace = remove # ignore/add/remove/force
# Add or remove newline between 'else' and 'if'
nl_else_if = remove # ignore/add/remove/force
# Add or remove newline between 'finally' and '{'
# WARNING: Code doesn't seem to use this feature - delete from the config?
-nl_finally_brace = ignore # ignore/add/remove/force
+nl_finally_brace = remove # ignore/add/remove/force
# Add or remove newline between 'try' and '{'
# WARNING: Code doesn't seem to use this feature - delete from the config?
# Add or remove newline between 'for' and '{'
# NOTE: is 109 worse than ignore
-nl_for_brace = add # ignore/add/remove/force
+nl_for_brace = remove # ignore/add/remove/force
# Add or remove newline between 'catch' and '{'
# WARNING: Code doesn't seem to use this feature - delete from the config?
# Add or remove newline between 'while' and '{'
# NOTE: is 22 worse than ignore
-nl_while_brace = add # ignore/add/remove/force
+nl_while_brace = remove # ignore/add/remove/force
# Add or remove newline between 'scope (x)' and '{' (D)
# WARNING: Code doesn't seem to use this feature - delete from the config?
#
# Add or remove braces on single-line 'do' statement
-mod_full_brace_do = add # ignore/add/remove/force
+mod_full_brace_do = force # ignore/add/remove/force
# Add or remove braces on single-line 'for' statement
# NOTE: is 3 worse than ignore
-mod_full_brace_for = remove # ignore/add/remove/force
+mod_full_brace_for = force # ignore/add/remove/force
# Add or remove braces on single-line function definitions. (Pawn)
mod_full_brace_function = ignore # ignore/add/remove/force #ignore
# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'.
-mod_full_brace_if = add # ignore/add/remove/force
+mod_full_brace_if = force # ignore/add/remove/force
# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if.
# If any must be braced, they are all braced. If all can be unbraced, then the braces are removed.
-mod_full_brace_if_chain = true # false/true #force
+mod_full_brace_if_chain = false # false/true #force
# Don't remove braces around statements that span N newlines
mod_full_brace_nl = 2 # number #force
# Add or remove braces on single-line 'while' statement
-mod_full_brace_while = remove # ignore/add/remove/force
+mod_full_brace_while = force # ignore/add/remove/force
# Add or remove braces on single-line 'using ()' statement
# WARNING: Code doesn't seem to use this feature - delete from the config?
mod_full_paren_if_bool = false # false/true
# Whether to remove superfluous semicolons
-mod_remove_extra_semicolon = false # false/true
+mod_remove_extra_semicolon = true # false/true
# If a function body exceeds the specified number of newlines and doesn't have a comment after
# the close brace, a comment will be added.