From: Mario Date: Wed, 23 Dec 2015 08:45:24 +0000 (+1000) Subject: Move some stuff around, kill a server file included from client X-Git-Tag: xonotic-v0.8.2~1454 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=dd7198099d8a20bb5c3fc9fe8366d537f5ca1d15;p=xonotic%2Fxonotic-data.pk3dir.git Move some stuff around, kill a server file included from client --- diff --git a/qcsrc/client/movelib.qc b/qcsrc/client/movelib.qc deleted file mode 100644 index 074f146dc..000000000 --- a/qcsrc/client/movelib.qc +++ /dev/null @@ -1 +0,0 @@ -#include "../server/movelib.qc" diff --git a/qcsrc/client/movelib.qh b/qcsrc/client/movelib.qh deleted file mode 100644 index a0634f6de..000000000 --- a/qcsrc/client/movelib.qh +++ /dev/null @@ -1 +0,0 @@ -#include "../server/movelib.qh" diff --git a/qcsrc/client/progs.inc b/qcsrc/client/progs.inc index 75ca9ac13..70bf45e5c 100644 --- a/qcsrc/client/progs.inc +++ b/qcsrc/client/progs.inc @@ -11,7 +11,6 @@ #include "main.qc" #include "mapvoting.qc" #include "miscfunctions.qc" -#include "movelib.qc" #include "player_skeleton.qc" #include "scoreboard.qc" #include "shownames.qc" diff --git a/qcsrc/common/_all.inc b/qcsrc/common/_all.inc index f955949ae..a27202962 100644 --- a/qcsrc/common/_all.inc +++ b/qcsrc/common/_all.inc @@ -15,8 +15,7 @@ #endif #ifndef MENUQC -#include "physics.qc" -#include "movetypes/include.qc" +#include "physics/all.inc" #include "triggers/include.qc" #include "viewloc.qc" #endif diff --git a/qcsrc/common/movetypes/follow.qc b/qcsrc/common/movetypes/follow.qc deleted file mode 100644 index 2d3e24f44..000000000 --- a/qcsrc/common/movetypes/follow.qc +++ /dev/null @@ -1,31 +0,0 @@ -void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow -{ - entity e = this.move_aiment; // TODO: networking? - - // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects - if(this.move_angles == this.move_punchangle) - { - this.move_origin = e.move_origin + this.view_ofs; - } - else - { - vector ang, v; - ang_x = -this.move_punchangle_x; - ang_y = this.move_punchangle_y; - ang_z = this.move_punchangle_z; - makevectors(ang); - v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x; - v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y; - v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.view_ofs_z * v_up_z; - ang_x = -e.move_angles_x; - ang_y = e.move_angles_y; - ang_z = e.move_angles_z; - makevectors(ang); - this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x; - this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y; - this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z; - } - - this.move_angles = e.move_angles + this.v_angle; - _Movetype_LinkEdict(this, false); -} diff --git a/qcsrc/common/movetypes/include.qc b/qcsrc/common/movetypes/include.qc deleted file mode 100644 index 322b3c4de..000000000 --- a/qcsrc/common/movetypes/include.qc +++ /dev/null @@ -1,7 +0,0 @@ -#include "push.qc" -#include "toss.qc" -#include "walk.qc" -#include "step.qc" -#include "follow.qc" - -#include "movetypes.qc" diff --git a/qcsrc/common/movetypes/include.qh b/qcsrc/common/movetypes/include.qh deleted file mode 100644 index a96e5957a..000000000 --- a/qcsrc/common/movetypes/include.qh +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef MOVETYPES_INCLUDE_H -#define MOVETYPES_INCLUDE_H - -#include "push.qh" -#include "toss.qh" -#include "walk.qh" - -#endif diff --git a/qcsrc/common/movetypes/movetypes.qc b/qcsrc/common/movetypes/movetypes.qc deleted file mode 100644 index fc9396b30..000000000 --- a/qcsrc/common/movetypes/movetypes.qc +++ /dev/null @@ -1,717 +0,0 @@ -#include "include.qh" -#include "../physics.qh" - -#if defined(CSQC) - #include "../../client/defs.qh" - #include "../stats.qh" - #include "../util.qh" - #include "movetypes.qh" - #include "../../lib/csqcmodel/common.qh" - #include "../../server/t_items.qh" -#elif defined(MENUQC) -#elif defined(SVQC) - #include "../../server/autocvars.qh" -#endif - -void _Movetype_WallFriction(entity this, vector stepnormal) // SV_WallFriction -{ - /*float d, i; - vector into, side; - makevectors(this.v_angle); - d = (stepnormal * v_forward) + 0.5; - - if(d < 0) - { - i = (stepnormal * this.move_velocity); - into = i * stepnormal; - side = this.move_velocity - into; - this.move_velocity_x = side.x * (1 * d); - this.move_velocity_y = side.y * (1 * d); - }*/ -} - -vector planes[MAX_CLIP_PLANES]; -int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight) // SV_FlyMove -{ - int blocked = 0, bumpcount; - int i, j, numplanes = 0; - float time_left = dt, grav = 0; - vector push; - vector primal_velocity, original_velocity, restore_velocity; - - for(i = 0; i < MAX_CLIP_PLANES; ++i) - planes[i] = '0 0 0'; - - if(applygravity) - { - this.move_didgravity = 1; - grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this); - - if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND)) - { - if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.move_velocity_z -= grav * 0.5; - else - this.move_velocity_z -= grav; - } - } - - original_velocity = primal_velocity = restore_velocity = this.move_velocity; - - for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++) - { - if(this.move_velocity == '0 0 0') - break; - - push = this.move_velocity * time_left; - _Movetype_PushEntity(this, push, true); - if(trace_startsolid) - { - // we got teleported by a touch function - // let's abort the move - blocked |= 8; - break; - } - - // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity - // abort move if we're stuck in the world (and didn't make it out) - if(trace_startsolid && trace_allsolid) - { - this.move_velocity = restore_velocity; - return 3; - } - - if(trace_fraction == 1) - break; - - float my_trace_fraction = trace_fraction; - vector my_trace_plane_normal = trace_plane_normal; - - if(trace_plane_normal.z) - { - if(trace_plane_normal.z > 0.7) - { - // floor - blocked |= 1; - - if(!trace_ent) - { - //dprint("_Movetype_FlyMove: !trace_ent\n"); - trace_ent = world; - } - - this.move_flags |= FL_ONGROUND; - this.move_groundentity = trace_ent; - } - } - else if(stepheight) - { - // step - handle it immediately - vector org = this.move_origin; - vector steppush = '0 0 1' * stepheight; - - _Movetype_PushEntity(this, steppush, true); - if(trace_startsolid) - { - blocked |= 8; - break; - } - _Movetype_PushEntity(this, push, true); - if(trace_startsolid) - { - blocked |= 8; - break; - } - float trace2_fraction = trace_fraction; - steppush = '0 0 1' * (org_z - this.move_origin_z); - _Movetype_PushEntity(this, steppush, true); - if(trace_startsolid) - { - blocked |= 8; - break; - } - - // accept the new position if it made some progress... - if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125) - { - trace_endpos = this.move_origin; - time_left *= 1 - trace2_fraction; - numplanes = 0; - continue; - } - else - this.move_origin = org; - } - else - { - // step - return it to caller - blocked |= 2; - // save the trace for player extrafriction - if(stepnormal) - stepnormal = trace_plane_normal; - } - - if(my_trace_fraction >= 0.001) - { - // actually covered some distance - original_velocity = this.move_velocity; - numplanes = 0; - } - - time_left *= 1 - my_trace_fraction; - - // clipped to another plane - if(numplanes >= MAX_CLIP_PLANES) - { - // this shouldn't really happen - this.move_velocity = '0 0 0'; - blocked = 3; - break; - } - - planes[numplanes] = my_trace_plane_normal; - numplanes++; - - // modify original_velocity so it parallels all of the clip planes - vector new_velocity = '0 0 0'; - for (i = 0;i < numplanes;i++) - { - new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1); - for (j = 0;j < numplanes;j++) - { - if(j != i) - { - // not ok - if((new_velocity * planes[j]) < 0) - break; - } - } - if(j == numplanes) - break; - } - - if(i != numplanes) - { - // go along this plane - this.move_velocity = new_velocity; - } - else - { - // go along the crease - if(numplanes != 2) - { - this.move_velocity = '0 0 0'; - blocked = 7; - break; - } - vector dir = cross(planes[0], planes[1]); - // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners - float ilength = sqrt((dir * dir)); - if(ilength) - ilength = 1.0 / ilength; - dir.x *= ilength; - dir.y *= ilength; - dir.z *= ilength; - float d = (dir * this.move_velocity); - this.move_velocity = dir * d; - } - - // if current velocity is against the original velocity, - // stop dead to avoid tiny occilations in sloping corners - if((this.move_velocity * primal_velocity) <= 0) - { - this.move_velocity = '0 0 0'; - break; - } - } - - // LordHavoc: this came from QW and allows you to get out of water more easily - if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8)) - this.move_velocity = primal_velocity; - - if(applygravity) - { - if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND)) - { - if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.move_velocity_z -= grav * 0.5f; - } - } - - return blocked; -} - -void _Movetype_CheckVelocity(entity this) // SV_CheckVelocity -{ - // if(vlen(this.move_velocity) < 0.0001) - // this.move_velocity = '0 0 0'; -} - -bool _Movetype_CheckWater(entity this) // SV_CheckWater -{ - vector point = this.move_origin; - point.z += this.mins.z + 1; - - int nativecontents = pointcontents(point); - if(this.move_watertype && this.move_watertype != nativecontents) - { - // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents); - if(this.contentstransition) - this.contentstransition(this.move_watertype, nativecontents); - } - - this.move_waterlevel = WATERLEVEL_NONE; - this.move_watertype = CONTENT_EMPTY; - - int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents); - if(supercontents & DPCONTENTS_LIQUIDSMASK) - { - this.move_watertype = nativecontents; - this.move_waterlevel = WATERLEVEL_WETFEET; - point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5; - if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK) - { - this.move_waterlevel = WATERLEVEL_SWIMMING; - point.z = this.move_origin.z + this.view_ofs.z; - if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK) - this.move_waterlevel = WATERLEVEL_SUBMERGED; - } - } - - return this.move_waterlevel > 1; -} - -void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition -{ - int contents = pointcontents(ent.move_origin); - - if(!ent.move_watertype) - { - // just spawned here - if(!autocvar_cl_gameplayfix_fixedcheckwatertransition) - { - ent.move_watertype = contents; - ent.move_waterlevel = 1; - return; - } - } - else if(ent.move_watertype != contents) - { - // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents); - if(ent.contentstransition) - ent.contentstransition(ent.move_watertype, contents); - } - - if(contents <= CONTENT_WATER) - { - ent.move_watertype = contents; - ent.move_waterlevel = 1; - } - else - { - ent.move_watertype = CONTENT_EMPTY; - ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents); - } -} - -void _Movetype_Impact(entity this, entity oth) // SV_Impact -{ - entity oldother = other; - - if(this.move_touch) - { - other = oth; - - WITH(entity, self, this, this.move_touch()); - - other = oldother; - } - - if(oth.move_touch) - { - other = this; - - WITH(entity, self, oth, oth.move_touch()); - - other = oldother; - } -} - -void _Movetype_LinkEdict_TouchAreaGrid(entity this) // SV_LinkEdict_TouchAreaGrid -{ - entity oldother = other; - - for (entity e = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); e; e = e.chain) - { - if(e.move_nomonsters != MOVE_NOMONSTERS && e.move_nomonsters != MOVE_WORLDONLY) - if(e.move_touch && boxesoverlap(e.absmin, e.absmax, this.absmin, this.absmax)) - { - other = this; - - trace_allsolid = false; - trace_startsolid = false; - trace_fraction = 1; - trace_inwater = false; - trace_inopen = true; - trace_endpos = e.move_origin; - trace_plane_normal = '0 0 1'; - trace_plane_dist = 0; - trace_ent = this; - - WITH(entity, self, e, e.move_touch()); - } - } - - other = oldother; -} - -void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict -{ - vector mi, ma; - if(this.solid == SOLID_BSP) - { - // TODO set the absolute bbox - mi = this.mins; - ma = this.maxs; - } - else - { - mi = this.mins; - ma = this.maxs; - } - mi += this.move_origin; - ma += this.move_origin; - - if(this.move_flags & FL_ITEM) - { - mi.x -= 15; - mi.y -= 15; - mi.z -= 1; - ma.x += 15; - ma.y += 15; - ma.z += 1; - } - else - { - mi.x -= 1; - mi.y -= 1; - mi.z -= 1; - ma.x += 1; - ma.y += 1; - ma.z += 1; - } - - this.absmin = mi; - this.absmax = ma; - - if(touch_triggers) - _Movetype_LinkEdict_TouchAreaGrid(this); -} - -bool _Movetype_TestEntityPosition(entity this, vector ofs) // SV_TestEntityPosition -{ -// vector org = this.move_origin + ofs; - - int cont = this.dphitcontentsmask; - this.dphitcontentsmask = DPCONTENTS_SOLID; - tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this); - this.dphitcontentsmask = cont; - - if(trace_startsolid) - return true; - - if(vlen(trace_endpos - this.move_origin) > 0.0001) - this.move_origin = trace_endpos; - return false; -} - -bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity -{ - if(!_Movetype_TestEntityPosition(this, '0 0 0')) return true; - if(!_Movetype_TestEntityPosition(this, '-1 0 0')) goto success; - if(!_Movetype_TestEntityPosition(this, '1 0 0')) goto success; - if(!_Movetype_TestEntityPosition(this, '0 -1 0')) goto success; - if(!_Movetype_TestEntityPosition(this, '0 1 0')) goto success; - if(!_Movetype_TestEntityPosition(this, '-1 -1 0')) goto success; - if(!_Movetype_TestEntityPosition(this, '1 -1 0')) goto success; - if(!_Movetype_TestEntityPosition(this, '-1 1 0')) goto success; - if(!_Movetype_TestEntityPosition(this, '1 1 0')) goto success; - for (int i = 1; i <= 17; ++i) - { - if(!_Movetype_TestEntityPosition(this, '0 0 -1' * i)) goto success; - if(!_Movetype_TestEntityPosition(this, '0 0 1' * i)) goto success; - } - LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", - etof(this), this.classname, vtos(this.move_origin)); - return false; - : success; - LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", - etof(this), this.classname, vtos(this.move_origin)); - _Movetype_LinkEdict(this, true); - return true; -} - -vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity -{ - vel -= ((vel * norm) * norm) * f; - - if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0; - if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0; - if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0; - - return vel; -} - -void _Movetype_PushEntityTrace(entity this, vector push) -{ - vector end = this.move_origin + push; - int type; - if(this.move_nomonsters) - type = max(0, this.move_nomonsters); - else if(this.move_movetype == MOVETYPE_FLYMISSILE) - type = MOVE_MISSILE; - else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY) - type = MOVE_WORLDONLY; - else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT) - type = MOVE_NOMONSTERS; - else - type = MOVE_NORMAL; - - tracebox(this.move_origin, this.mins, this.maxs, end, type, this); -} - -float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid) // SV_PushEntity -{ - _Movetype_PushEntityTrace(this, push); - - if(trace_startsolid && failonstartsolid) - return trace_fraction; - - this.move_origin = trace_endpos; - - if(trace_fraction < 1) - if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent))) - _Movetype_Impact(this, trace_ent); - - return trace_fraction; -} - - -.float ltime; -.void() blocked; -// matrix version of makevectors, sets v_forward, v_right and v_up -void makevectors_matrix(vector myangles) // AngleVectorsFLU -{ - v_forward = v_right = v_up = '0 0 0'; - - float y = myangles.y * (M_PI * 2 / 360); - float sy = sin(y); - float cy = cos(y); - float p = myangles.x * (M_PI * 2 / 360); - float sp = sin(p); - float cp = cos(p); - if(v_forward) - { - v_forward.x = cp * cy; - v_forward.y = cp * sy; - v_forward.z = -sp; - } - if(v_right || v_up) - { - if(myangles.z) - { - float r = myangles.z * (M_PI * 2 / 360); - float sr = sin(r); - float cr = cos(r); - if(v_right) - { - v_right.x = sr * sp * cy + cr * -sy; - v_right.y = sr * sp * sy + cr * cy; - v_right.z = sr * cp; - } - if(v_up) - { - v_up.x = cr * sp * cy + -sr * -sy; - v_up.y = cr * sp * sy + -sr * cy; - v_up.z = cr * cp; - } - } - else - { - if(v_right) - { - v_right.x = -sy; - v_right.y = cy; - v_right.z = 0; - } - if(v_up) - { - v_up.x = sp * cy; - v_up.y = sp * sy; - v_up.z = cp; - } - } - } -} - -void _Movetype_Physics_Frame(entity this, float movedt) -{ - this.move_didgravity = -1; - switch (this.move_movetype) - { - case MOVETYPE_PUSH: - case MOVETYPE_FAKEPUSH: - _Movetype_Physics_Pusher(this, movedt); - break; - case MOVETYPE_NONE: - break; - case MOVETYPE_FOLLOW: - _Movetype_Physics_Follow(this); - break; - case MOVETYPE_NOCLIP: - _Movetype_CheckWater(this); - this.move_origin = this.move_origin + TICRATE * this.move_velocity; - this.move_angles = this.move_angles + TICRATE * this.move_avelocity; - _Movetype_LinkEdict(this, false); - break; - case MOVETYPE_STEP: - _Movetype_Physics_Step(this, movedt); - break; - case MOVETYPE_WALK: - _Movetype_Physics_Walk(this, movedt); - break; - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - case MOVETYPE_BOUNCEMISSILE: - case MOVETYPE_FLYMISSILE: - case MOVETYPE_FLY: - case MOVETYPE_FLY_WORLDONLY: - _Movetype_Physics_Toss(this, movedt); - _Movetype_LinkEdict(this, true); - break; - case MOVETYPE_PHYSICS: - break; - } -} - -void _Movetype_Physics_ClientFrame(entity this, float movedt) -{ - this.move_didgravity = -1; - switch (this.move_movetype) - { - case MOVETYPE_PUSH: - case MOVETYPE_FAKEPUSH: - _Movetype_Physics_Pusher(this, movedt); - break; - case MOVETYPE_NONE: - break; - case MOVETYPE_FOLLOW: - _Movetype_Physics_Follow(this); - break; - case MOVETYPE_NOCLIP: - _Movetype_CheckWater(this); - this.move_origin = this.move_origin + TICRATE * this.move_velocity; - this.move_angles = this.move_angles + TICRATE * this.move_avelocity; - _Movetype_LinkEdict(this, false); - break; - case MOVETYPE_STEP: - _Movetype_Physics_Step(this, movedt); - break; - case MOVETYPE_WALK: - case MOVETYPE_FLY: - case MOVETYPE_FLY_WORLDONLY: - _Movetype_Physics_Walk(this, movedt); - break; - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - case MOVETYPE_BOUNCEMISSILE: - case MOVETYPE_FLYMISSILE: - _Movetype_Physics_Toss(this, movedt); - break; - case MOVETYPE_PHYSICS: - break; - } -} - -void Movetype_Physics_NoMatchServer(entity this) // optimized -{ - float movedt = time - this.move_time; - this.move_time = time; - - _Movetype_Physics_Frame(this, movedt); - if(wasfreed(this)) - return; - - this.avelocity = this.move_avelocity; - this.velocity = this.move_velocity; - this.angles = this.move_angles; - setorigin(this, this.move_origin); -} - -void Movetype_Physics_MatchServer(entity this, bool sloppy) -{ - Movetype_Physics_MatchTicrate(this, TICRATE, sloppy); -} - -void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Physics_Entity -{ - if(tr <= 0) - { - Movetype_Physics_NoMatchServer(this); - return; - } - - float dt = time - this.move_time; - - int n = max(0, floor(dt / tr)); - dt -= n * tr; - this.move_time += n * tr; - - if(!this.move_didgravity) - this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND)); - - for (int i = 0; i < n; ++i) - { - _Movetype_Physics_Frame(this, tr); - if(wasfreed(this)) - return; - } - - this.avelocity = this.move_avelocity; - - if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND)) - { - // now continue the move from move_time to time - this.velocity = this.move_velocity; - - if(this.move_didgravity > 0) - { - this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1) - * dt - * (this.gravity ? this.gravity : 1) - * PHYS_GRAVITY(this); - } - - this.angles = this.move_angles + dt * this.avelocity; - - if(sloppy || this.move_movetype == MOVETYPE_NOCLIP) - { - setorigin(this, this.move_origin + dt * this.velocity); - } - else - { - _Movetype_PushEntityTrace(this, dt * this.velocity); - if(!trace_startsolid) - setorigin(this, trace_endpos); - } - - if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); - } - else - { - this.velocity = this.move_velocity; - this.angles = this.move_angles; - setorigin(this, this.move_origin); - } -} diff --git a/qcsrc/common/movetypes/movetypes.qh b/qcsrc/common/movetypes/movetypes.qh deleted file mode 100644 index 7578ae2fa..000000000 --- a/qcsrc/common/movetypes/movetypes.qh +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef MOVETYPES_H -#define MOVETYPES_H - -.float move_ltime; -.void()move_think; -.float move_nextthink; -.void()move_blocked; - -.float move_movetype; -.float move_time; -.vector move_origin; -.vector move_angles; -.vector move_velocity; -.vector move_avelocity; -.int move_flags; -.int move_watertype; -.int move_waterlevel; -.void()move_touch; -.void(float, float)contentstransition; -.float move_bounce_factor; -.float move_bounce_stopspeed; -.float move_nomonsters; // -1 for MOVE_NORMAL, otherwise a MOVE_ constant - -.entity move_aiment; -.vector move_punchangle; - -// should match sv_gameplayfix_fixedcheckwatertransition -float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1; - -.entity move_groundentity; // FIXME add move_groundnetworkentity? -.float move_suspendedinair; -.float move_didgravity; - -void _Movetype_WallFriction(entity this, vector stepnormal); -int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight); -void _Movetype_CheckVelocity(entity this); -void _Movetype_CheckWaterTransition(entity ent); -float _Movetype_CheckWater(entity ent); -void _Movetype_LinkEdict_TouchAreaGrid(entity this); -void _Movetype_LinkEdict(entity this, float touch_triggers); -float _Movetype_TestEntityPosition(entity this, vector ofs); -float _Movetype_UnstickEntity(entity this); -vector _Movetype_ClipVelocity(vector vel, vector norm, float f); -void _Movetype_PushEntityTrace(entity this, vector push); -float _Movetype_PushEntity(entity this, vector push, float failonstartsolid); -void makevectors_matrix(vector myangles); - -void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy); -void Movetype_Physics_MatchServer(entity this, bool sloppy); -void Movetype_Physics_NoMatchServer(entity this); -void _Movetype_LinkEdict(entity this, float touch_triggers); -void _Movetype_LinkEdict_TouchAreaGrid(entity this); - -float _Movetype_UnstickEntity(entity this); - -const int MAX_CLIP_PLANES = 5; - -#ifdef CSQC -const int MOVETYPE_NONE = 0; -const int MOVETYPE_ANGLENOCLIP = 1; -const int MOVETYPE_ANGLECLIP = 2; -const int MOVETYPE_WALK = 3; -const int MOVETYPE_STEP = 4; -const int MOVETYPE_FLY = 5; -const int MOVETYPE_TOSS = 6; -const int MOVETYPE_PUSH = 7; -const int MOVETYPE_NOCLIP = 8; -const int MOVETYPE_FLYMISSILE = 9; -const int MOVETYPE_BOUNCE = 10; -const int MOVETYPE_BOUNCEMISSILE = 11; // Like bounce but doesn't lose speed on bouncing -const int MOVETYPE_FOLLOW = 12; -const int MOVETYPE_PHYSICS = 32; -const int MOVETYPE_FLY_WORLDONLY = 33; - -const int FL_ITEM = 256; -const int FL_ONGROUND = 512; -#endif - -const int MOVETYPE_FAKEPUSH = 13; - -const int MOVEFLAG_VALID = BIT(23); -const int MOVEFLAG_Q2AIRACCELERATE = BIT(0); -const int MOVEFLAG_NOGRAVITYONGROUND = BIT(1); -const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2); - -#ifdef CSQC -#define moveflags STAT(MOVEFLAGS) -#endif - -#endif diff --git a/qcsrc/common/movetypes/push.qc b/qcsrc/common/movetypes/push.qc deleted file mode 100644 index b83246546..000000000 --- a/qcsrc/common/movetypes/push.qc +++ /dev/null @@ -1,157 +0,0 @@ -void _Movetype_PushMove(entity this, float dt) // SV_PushMove -{ - if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0') - { - this.move_ltime += dt; - return; - } - - switch (this.solid) - { - // LordHavoc: valid pusher types - case SOLID_BSP: - case SOLID_BBOX: - case SOLID_SLIDEBOX: - case SOLID_CORPSE: // LordHavoc: this would be weird... - break; - // LordHavoc: no collisions - case SOLID_NOT: - case SOLID_TRIGGER: - this.move_origin = this.move_origin + dt * this.move_velocity; - this.move_angles = this.move_angles + dt * this.move_avelocity; - this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0)); - this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0)); - this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0)); - this.move_ltime += dt; - _Movetype_LinkEdict(this, true); - return; - default: - LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid); - return; - } - - bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0; - - vector move1 = this.move_velocity * dt; - vector moveangle = this.move_avelocity * dt; - - makevectors_matrix(-moveangle); - -// vector pushorig = this.move_origin; -// vector pushang = this.move_angles; -// float pushltime = this.move_ltime; - -// move the pusher to its final position - - this.move_origin = this.move_origin + dt * this.move_velocity; - this.move_angles = this.move_angles + dt * this.move_avelocity; - - this.move_ltime += dt; - _Movetype_LinkEdict(this, true); - - int savesolid = this.solid; - - if (this.move_movetype != MOVETYPE_FAKEPUSH) - { - for (entity check = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); check; check = check.chain) - { - switch (check.move_movetype) - { - case MOVETYPE_NONE: - case MOVETYPE_PUSH: - case MOVETYPE_FOLLOW: - case MOVETYPE_NOCLIP: - case MOVETYPE_FLY_WORLDONLY: - continue; - default: - break; - } - - if (check.owner == this) - continue; - - if (this.owner == check) - continue; - - vector pivot = check.mins + 0.5 * (check.maxs - check.mins); - vector move; - if (rotated) - { - vector org = (check.move_origin - this.move_origin) + pivot; - vector org2; - org2.x = org * v_forward; - org2.y = org * v_right; - org2.z = org * v_up; - move = (org2 - org) + move1; - } - else - { - move = move1; - } - - // physics objects need better collisions than this code can do - if (check.move_movetype == 32) // MOVETYPE_PHYSICS - { - check.move_origin = check.move_origin + move; - WITH(entity, this, check, _Movetype_LinkEdict(this, true)); - continue; - } - - // try moving the contacted entity - this.solid = SOLID_NOT; - bool flag = false; - WITH(entity, this, check, { - flag = _Movetype_PushEntity(this, move, true); - }); - if (!flag) - { - // entity "check" got teleported - check.move_angles_y += trace_fraction * moveangle.y; - this.solid = savesolid; - continue; // pushed enough - } - // FIXME: turn players specially - check.move_angles_y += trace_fraction * moveangle.y; - this.solid = savesolid; - - // this trace.fraction < 1 check causes items to fall off of pushers - // if they pass under or through a wall - // the groundentity check causes items to fall off of ledges - if (check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != this)) - check.move_flags &= ~FL_ONGROUND; - } - } - - this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0)); - this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0)); - this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0)); -} - -void _Movetype_Physics_Pusher(entity this, float dt) // SV_Physics_Pusher -{ - float oldltime = this.move_ltime; - float thinktime = this.move_nextthink; - float movetime; - if (thinktime < this.move_ltime + dt) - { - movetime = thinktime - this.move_ltime; - if (movetime < 0) - movetime = 0; - } - else - { - movetime = dt; - } - - if (movetime) - // advances this.move_ltime if not blocked - _Movetype_PushMove(this, movetime); - - if (thinktime > oldltime && thinktime <= this.move_ltime) - { - this.move_nextthink = 0; - this.move_time = time; - other = world; - WITH(entity, self, this, this.move_think()); - } -} diff --git a/qcsrc/common/movetypes/push.qh b/qcsrc/common/movetypes/push.qh deleted file mode 100644 index d0c8493b7..000000000 --- a/qcsrc/common/movetypes/push.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MOVETYPE_PUSH_H -#define MOVETYPE_PUSH_H - -void _Movetype_Physics_Pusher(entity this, float dt); - -#endif diff --git a/qcsrc/common/movetypes/step.qc b/qcsrc/common/movetypes/step.qc deleted file mode 100644 index d7a2d5627..000000000 --- a/qcsrc/common/movetypes/step.qc +++ /dev/null @@ -1,23 +0,0 @@ -void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step -{ - if(this.move_flags & FL_ONGROUND) - { - if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this)) - { - this.move_flags &= ~FL_ONGROUND; - _Movetype_CheckVelocity(this); - _Movetype_FlyMove(this, dt, true, '0 0 0', 0); - _Movetype_LinkEdict(this, true); - } - } - else - { - _Movetype_CheckVelocity(this); - _Movetype_FlyMove(this, dt, true, '0 0 0', 0); - _Movetype_LinkEdict(this, true); - - // TODO? movetypesteplandevent - } - - _Movetype_CheckWaterTransition(this); -} diff --git a/qcsrc/common/movetypes/toss.qc b/qcsrc/common/movetypes/toss.qc deleted file mode 100644 index 43b5a8a1a..000000000 --- a/qcsrc/common/movetypes/toss.qc +++ /dev/null @@ -1,115 +0,0 @@ -#include "../physics.qh" - -void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss -{ - if (this.move_flags & FL_ONGROUND) - { - if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this)) - { - this.move_flags &= ~FL_ONGROUND; - } - else if (!this.move_groundentity) - { - return; - } - else if (this.move_suspendedinair && wasfreed(this.move_groundentity)) - { - this.move_groundentity = world; - return; - } - } - - this.move_suspendedinair = false; - - _Movetype_CheckVelocity(this); - - if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) - { - this.move_didgravity = 1; - this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1) - * dt - * (this.gravity ? this.gravity : 1) - * PHYS_GRAVITY(this); - } - - this.move_angles = this.move_angles + this.move_avelocity * dt; - - float movetime = dt; - for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump) - { - vector move = this.move_velocity * movetime; - _Movetype_PushEntity(this, move, true); - if (wasfreed(this)) - return; - - if (trace_startsolid) - { - _Movetype_UnstickEntity(this); - _Movetype_PushEntity(this, move, false); - if (wasfreed(this)) - return; - } - - if (trace_fraction == 1) - break; - - movetime *= 1 - min(1, trace_fraction); - - if (this.move_movetype == MOVETYPE_BOUNCEMISSILE) - { - this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0); - this.move_flags &= ~FL_ONGROUND; - } - else if (this.move_movetype == MOVETYPE_BOUNCE) - { - float bouncefac = this.move_bounce_factor; if (!bouncefac) bouncefac = 0.5; - float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800; - bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); - - this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac); - - float d = trace_plane_normal * this.move_velocity; - if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop) - { - this.move_flags |= FL_ONGROUND; - this.move_groundentity = trace_ent; - this.move_velocity = '0 0 0'; - this.move_avelocity = '0 0 0'; - } - else - { - this.move_flags &= ~FL_ONGROUND; - } - } - else - { - this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0); - if (trace_plane_normal.z > 0.7) - { - this.move_flags |= FL_ONGROUND; - this.move_groundentity = trace_ent; - if (trace_ent.solid == SOLID_BSP) - this.move_suspendedinair = true; - this.move_velocity = '0 0 0'; - this.move_avelocity = '0 0 0'; - } - else - { - this.move_flags &= ~FL_ONGROUND; - } - } - - // DP revision 8905 (just, WHY...) - if (this.move_movetype == MOVETYPE_BOUNCEMISSILE) - break; - - // DP revision 8918 (WHY...) - if (this.move_flags & FL_ONGROUND) - break; - } - - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND)) - this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); - - _Movetype_CheckWaterTransition(this); -} diff --git a/qcsrc/common/movetypes/toss.qh b/qcsrc/common/movetypes/toss.qh deleted file mode 100644 index cf5cf49bb..000000000 --- a/qcsrc/common/movetypes/toss.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MOVETYPE_TOSS_H -#define MOVETYPE_TOSS_H - -void _Movetype_Physics_Toss(entity this, float dt); - -#endif diff --git a/qcsrc/common/movetypes/walk.qc b/qcsrc/common/movetypes/walk.qc deleted file mode 100644 index e92624626..000000000 --- a/qcsrc/common/movetypes/walk.qc +++ /dev/null @@ -1,176 +0,0 @@ -void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove -{ - vector stepnormal = '0 0 0'; - - // if frametime is 0 (due to client sending the same timestamp twice), don't move - if (dt <= 0) - return; - - if (GAMEPLAYFIX_UNSTICKPLAYERS(this)) - _Movetype_UnstickEntity(this); - - bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP)); - - _Movetype_CheckVelocity(this); - - // do a regular slide move unless it looks like you ran into a step - bool oldonground = (this.move_flags & FL_ONGROUND); - - vector start_origin = this.move_origin; - vector start_velocity = this.move_velocity; - - int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0); - - if (GAMEPLAYFIX_DOWNTRACEONGROUND(this) && !(clip & 1)) - { - // only try this if there was no floor in the way in the trace (no, - // this check seems to be not REALLY necessary, because if clip & 1, - // our trace will hit that thing too) - vector upmove = this.move_origin + '0 0 1'; - vector downmove = this.move_origin - '0 0 1'; - int type; - if (this.move_movetype == MOVETYPE_FLYMISSILE) - type = MOVE_MISSILE; - else if (this.move_movetype == MOVETYPE_FLY_WORLDONLY) - type = MOVE_WORLDONLY; - else if (this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT) - type = MOVE_NOMONSTERS; - else type = MOVE_NORMAL; - tracebox(upmove, this.mins, this.maxs, downmove, type, this); - if (trace_fraction < 1 && trace_plane_normal.z > 0.7) - clip |= 1; // but we HAVE found a floor - } - - // if the move did not hit the ground at any point, we're not on ground - if (!(clip & 1)) - this.move_flags &= ~FL_ONGROUND; - - _Movetype_CheckVelocity(this); - _Movetype_LinkEdict(this, true); - - if (clip & 8) // teleport - return; - - if (this.move_flags & FL_WATERJUMP) - return; - - if (PHYS_NOSTEP(this)) - return; - - vector originalmove_origin = this.move_origin; - vector originalmove_velocity = this.move_velocity; - // originalmove_clip = clip; - int originalmove_flags = this.move_flags; - entity originalmove_groundentity = this.move_groundentity; - - // if move didn't block on a step, return - if (clip & 2) - { - // if move was not trying to move into the step, return - if (fabs(start_velocity.x) < 0.03125 && fabs(start_velocity.y) < 0.03125) - return; - - if (this.move_movetype != MOVETYPE_FLY) - { - // return if gibbed by a trigger - if (this.move_movetype != MOVETYPE_WALK) - return; - - // return if attempting to jump while airborn (unless sv_jumpstep) - if (!PHYS_JUMPSTEP(this)) - if (!oldonground && this.move_waterlevel == 0) - return; - } - - // try moving up and forward to go up a step - // back to start pos - this.move_origin = start_origin; - this.move_velocity = start_velocity; - - // move up - vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this); - _Movetype_PushEntity(this, upmove, true); - if(wasfreed(this)) - return; - if(trace_startsolid) - { - // we got teleported when upstepping... must abort the move - return; - } - - // move forward - this.move_velocity_z = 0; - clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0); - this.move_velocity_z += start_velocity.z; - if (clip & 8) - { - // we got teleported when upstepping... must abort the move - // note that z velocity handling may not be what QC expects here, but we cannot help it - return; - } - - _Movetype_CheckVelocity(this); - _Movetype_LinkEdict(this, true); - - // check for stuckness, possibly due to the limited precision of floats - // in the clipping hulls - if (clip - && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125 - && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125) - { - // Con_Printf("wall\n"); - // stepping up didn't make any progress, revert to original move - this.move_origin = originalmove_origin; - this.move_velocity = originalmove_velocity; - // clip = originalmove_clip; - this.move_flags = originalmove_flags; - this.move_groundentity = originalmove_groundentity; - // now try to unstick if needed - // clip = SV_TryUnstick (ent, oldvel); - return; - } - - // Con_Printf("step - "); - - // extra friction based on view angle - if ((clip & 2) && PHYS_WALLFRICTION(this)) - _Movetype_WallFriction(this, stepnormal); - } - // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground - else if (!GAMEPLAYFIX_STEPDOWN(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND)) - { - return; - } - - // move down - vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT(this) + start_velocity.z * dt); - _Movetype_PushEntity(this, downmove, true); - if(wasfreed(this)) - return; - - if(trace_startsolid) - { - // we got teleported when downstepping... must abort the move - return; - } - - if (trace_fraction < 1 && trace_plane_normal.z > 0.7) - { - // this has been disabled so that you can't jump when you are stepping - // up while already jumping (also known as the Quake2 double jump bug) - } - else - { - // Con_Printf("slope\n"); - // if the push down didn't end up on good ground, use the move without - // the step up. This happens near wall / slope combinations, and can - // cause the player to hop up higher on a slope too steep to climb - this.move_origin = originalmove_origin; - this.move_velocity = originalmove_velocity; - this.move_flags = originalmove_flags; - this.move_groundentity = originalmove_groundentity; - } - - _Movetype_CheckVelocity(this); - _Movetype_LinkEdict(this, true); -} diff --git a/qcsrc/common/movetypes/walk.qh b/qcsrc/common/movetypes/walk.qh deleted file mode 100644 index a920c7a4f..000000000 --- a/qcsrc/common/movetypes/walk.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MOVETYPE_WALK_H -#define MOVETYPE_WALK_H - -void _Movetype_Physics_Walk(entity this, float dt); - -#endif diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc deleted file mode 100644 index 32c2fbfa8..000000000 --- a/qcsrc/common/physics.qc +++ /dev/null @@ -1,1545 +0,0 @@ -#include "physics.qh" -#include "triggers/include.qh" -#include "viewloc.qh" - -#ifdef SVQC - -#include "../server/miscfunctions.qh" -#include "triggers/trigger/viewloc.qh" - -// client side physics -bool Physics_Valid(string thecvar) -{ - return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar); -} - -float Physics_ClientOption(entity this, string option) -{ - if(Physics_Valid(this.cvar_cl_physics)) - { - string s = sprintf("g_physics_%s_%s", this.cvar_cl_physics, option); - if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS) - return cvar(s); - } - if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default) - { - string s = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option); - if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS) - return cvar(s); - } - return cvar(strcat("sv_", option)); -} - -void Physics_UpdateStats(entity this, float maxspd_mod) -{ - STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod); - STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw")) - ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod) - : 0; - STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod; - STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking - - // old stats - // fix some new settings - STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor"); - STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed"); - STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed"); - STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate"); - STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel"); - STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction"); - STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol"); - STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power"); - STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty"); - STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel"); - STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed"); - STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel"); - STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio"); - STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction"); - STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate"); - STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed"); - STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate"); - STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate"); - STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity"); - STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump"); -} -#endif - -float IsMoveInDirection(vector mv, float ang) // key mix factor -{ - if (mv_x == 0 && mv_y == 0) - return 0; // avoid division by zero - ang -= RAD2DEG * atan2(mv_y, mv_x); - ang = remainder(ang, 360) / 45; - return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang); -} - -float GeomLerp(float a, float lerp, float b) -{ - return a == 0 ? (lerp < 1 ? 0 : b) - : b == 0 ? (lerp > 0 ? 0 : a) - : a * pow(fabs(b / a), lerp); -} - -#define unstick_offsets(X) \ -/* 1 no nudge (just return the original if this test passes) */ \ - X(' 0.000 0.000 0.000') \ -/* 6 simple nudges */ \ - X(' 0.000 0.000 0.125') X('0.000 0.000 -0.125') \ - X('-0.125 0.000 0.000') X('0.125 0.000 0.000') \ - X(' 0.000 -0.125 0.000') X('0.000 0.125 0.000') \ -/* 4 diagonal flat nudges */ \ - X('-0.125 -0.125 0.000') X('0.125 -0.125 0.000') \ - X('-0.125 0.125 0.000') X('0.125 0.125 0.000') \ -/* 8 diagonal upward nudges */ \ - X('-0.125 0.000 0.125') X('0.125 0.000 0.125') \ - X(' 0.000 -0.125 0.125') X('0.000 0.125 0.125') \ - X('-0.125 -0.125 0.125') X('0.125 -0.125 0.125') \ - X('-0.125 0.125 0.125') X('0.125 0.125 0.125') \ -/* 8 diagonal downward nudges */ \ - X('-0.125 0.000 -0.125') X('0.125 0.000 -0.125') \ - X(' 0.000 -0.125 -0.125') X('0.000 0.125 -0.125') \ - X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \ - X('-0.125 0.125 -0.125') X('0.125 0.125 -0.125') \ -/**/ - -void PM_ClientMovement_Unstick(entity this) -{ - #define X(unstick_offset) \ - { \ - vector neworigin = unstick_offset + this.origin; \ - tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, this); \ - if (!trace_startsolid) \ - { \ - setorigin(this, neworigin); \ - return; \ - } \ - } - unstick_offsets(X); - #undef X -} - -void PM_ClientMovement_UpdateStatus(entity this, bool ground) -{ -#ifdef CSQC - if(!IS_PLAYER(this)) - return; - // make sure player is not stuck - if(autocvar_cl_movement == 3) - PM_ClientMovement_Unstick(this); - - // set crouched - if (PHYS_INPUT_BUTTON_CROUCH(this)) - { - // wants to crouch, this always works - if (!IS_DUCKED(this)) SET_DUCKED(this); - } - else - { - // wants to stand, if currently crouching we need to check for a low ceiling first - if (IS_DUCKED(this)) - { - tracebox(this.origin, PL_MIN, PL_MAX, this.origin, MOVE_NORMAL, this); - if (!trace_startsolid) UNSET_DUCKED(this); - } - } - - // set onground - vector origin1 = this.origin + '0 0 1'; - vector origin2 = this.origin - '0 0 1'; - - if (ground && autocvar_cl_movement == 3) - { - tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this); - if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7) - { - SET_ONGROUND(this); - - // this code actually "predicts" an impact; so let's clip velocity first - this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal; - } - else - UNSET_ONGROUND(this); - } - - if(autocvar_cl_movement == 3) - { - // set watertype/waterlevel - origin1 = this.origin; - origin1.z += this.mins_z + 1; - this.waterlevel = WATERLEVEL_NONE; - - int thepoint = pointcontents(origin1); - - this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME); - - if (this.watertype) - { - this.waterlevel = WATERLEVEL_WETFEET; - origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5; - thepoint = pointcontents(origin1); - if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) - { - this.waterlevel = WATERLEVEL_SWIMMING; - origin1.z = this.origin.z + 22; - thepoint = pointcontents(origin1); - if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) - this.waterlevel = WATERLEVEL_SUBMERGED; - } - } - } - - if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0) - pmove_waterjumptime = 0; -#endif -} - -void PM_ClientMovement_Move(entity this) -{ -#ifdef CSQC - - PM_ClientMovement_UpdateStatus(this, false); - if(autocvar_cl_movement == 1) - return; - - int bump; - float t; - float f; - vector neworigin; - vector currentorigin2; - vector neworigin2; - vector primalvelocity; - - vector trace1_endpos = '0 0 0'; - vector trace2_endpos = '0 0 0'; - vector trace3_endpos = '0 0 0'; - float trace1_fraction = 0; - float trace2_fraction = 0; - float trace3_fraction = 0; - vector trace1_plane_normal = '0 0 0'; - vector trace2_plane_normal = '0 0 0'; - vector trace3_plane_normal = '0 0 0'; - - primalvelocity = this.velocity; - for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++) - { - neworigin = this.origin + t * this.velocity; - tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this); - trace1_endpos = trace_endpos; - trace1_fraction = trace_fraction; - trace1_plane_normal = trace_plane_normal; - if(trace1_fraction < 1 && trace1_plane_normal_z == 0) - { - // may be a step or wall, try stepping up - // first move forward at a higher level - currentorigin2 = this.origin; - currentorigin2_z += PHYS_STEPHEIGHT(this); - neworigin2 = neworigin; - neworigin2_z += PHYS_STEPHEIGHT(this); - tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this); - trace2_endpos = trace_endpos; - trace2_fraction = trace_fraction; - trace2_plane_normal = trace_plane_normal; - if(!trace_startsolid) - { - // then move down from there - currentorigin2 = trace2_endpos; - neworigin2 = trace2_endpos; - neworigin2_z = this.origin_z; - tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this); - trace3_endpos = trace_endpos; - trace3_fraction = trace_fraction; - trace3_plane_normal = trace_plane_normal; - // accept the new trace if it made some progress - if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125) - { - trace1_endpos = trace2_endpos; - trace1_fraction = trace2_fraction; - trace1_plane_normal = trace2_plane_normal; - trace1_endpos = trace3_endpos; - } - } - } - - // check if it moved at all - if(trace1_fraction >= 0.001) - setorigin(this, trace1_endpos); - - // check if it moved all the way - if(trace1_fraction == 1) - break; - - // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate - // I'm pretty sure I commented it out solely because it seemed redundant - // this got commented out in a change that supposedly makes the code match QW better - // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block - if(trace1_plane_normal_z > 0.7) - SET_ONGROUND(this); - - t -= t * trace1_fraction; - - f = (this.velocity * trace1_plane_normal); - this.velocity = this.velocity + -f * trace1_plane_normal; - } - if(PHYS_TELEPORT_TIME(this) > 0) - this.velocity = primalvelocity; -#endif -} - -void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed) -{ - float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1); - if (k <= 0) - return; - - k *= bound(0, wishspeed / PHYS_MAXAIRSPEED(this), 1); - - float zspeed = this.velocity_z; - this.velocity_z = 0; - float xyspeed = vlen(this.velocity); - this.velocity = normalize(this.velocity); - - float dot = this.velocity * wishdir; - - if (dot > 0) // we can't change direction while slowing down - { - k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH; - xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32); - k *= PHYS_AIRCONTROL(this); - this.velocity = normalize(this.velocity * xyspeed + wishdir * k); - } - - this.velocity = this.velocity * xyspeed; - this.velocity_z = zspeed; -} - -float AdjustAirAccelQW(float accelqw, float factor) -{ - return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw); -} - -// example config for alternate speed clamping: -// sv_airaccel_qw 0.8 -// sv_airaccel_sideways_friction 0 -// prvm_globalset server speedclamp_mode 1 -// (or 2) -void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit) -{ - float speedclamp = stretchfactor > 0 ? stretchfactor - : accelqw < 0 ? 1 // full clamping, no stretch - : -1; // no clamping - - accelqw = fabs(accelqw); - - if (GAMEPLAYFIX_Q2AIRACCELERATE) - wishspeed0 = wishspeed; // don't need to emulate this Q1 bug - - float vel_straight = this.velocity * wishdir; - float vel_z = this.velocity_z; - vector vel_xy = vec2(this.velocity); - vector vel_perpend = vel_xy - vel_straight * wishdir; - - float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0; - - float vel_xy_current = vlen(vel_xy); - if (speedlimit) - accelqw = AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed)); - float vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); - float vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw); - vel_xy_backward = max(0, vel_xy_backward); // not that it REALLY occurs that this would cause wrong behaviour afterwards - vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw); - - if (sidefric < 0 && (vel_perpend*vel_perpend)) - // negative: only apply so much sideways friction to stay below the speed you could get by "braking" - { - float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); - float themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend); - // assume: themin > 1 - // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend - // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend - // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy - // obviously, this cannot be - if (themin <= 0) - vel_perpend *= f; - else - { - themin = sqrt(themin); - vel_perpend *= max(themin, f); - } - } - else - vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); - - vel_xy = vel_straight * wishdir + vel_perpend; - - if (speedclamp >= 0) - { - float vel_xy_preclamp; - vel_xy_preclamp = vlen(vel_xy); - if (vel_xy_preclamp > 0) // prevent division by zero - { - vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp; - if (vel_xy_current < vel_xy_preclamp) - vel_xy *= (vel_xy_current / vel_xy_preclamp); - } - } - - this.velocity = vel_xy + vel_z * '0 0 1'; -} - -void PM_AirAccelerate(entity this, vector wishdir, float wishspeed) -{ - if (wishspeed == 0) - return; - - vector curvel = this.velocity; - curvel_z = 0; - float curspeed = vlen(curvel); - - if (wishspeed > curspeed * 1.01) - wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH); - else - { - float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this))); - wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH; - } - vector wishvel = wishdir * wishspeed; - vector acceldir = wishvel - curvel; - float addspeed = vlen(acceldir); - acceldir = normalize(acceldir); - - float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH); - - if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1) - { - vector curdir = normalize(curvel); - float dot = acceldir * curdir; - if (dot < 0) - acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this)) * dot * curdir; - } - - this.velocity += accelspeed * acceldir; -} - - -/* -============= -PlayerJump - -When you press the jump key -returns true if handled -============= -*/ -bool PlayerJump(entity this) -{ - if (PHYS_FROZEN(this)) - return true; // no jumping in freezetag when frozen - -#ifdef SVQC - if (this.player_blocked) - return true; // no jumping while blocked -#endif - - bool doublejump = false; - float mjumpheight = PHYS_JUMPVELOCITY(this); - - if (MUTATOR_CALLHOOK(PlayerJump, this, doublejump, mjumpheight)) - return true; - - doublejump = player_multijump; - mjumpheight = player_jumpheight; - - if (this.waterlevel >= WATERLEVEL_SWIMMING) - { - if(this.viewloc) - { - doublejump = true; - mjumpheight *= 0.7; - } - else - { - this.velocity_z = PHYS_MAXSPEED(this) * 0.7; - return true; - } - } - - if (!doublejump) - if (!IS_ONGROUND(this)) - return IS_JUMP_HELD(this); - - bool track_jump = PHYS_CL_TRACK_CANJUMP(this); - if(PHYS_TRACK_CANJUMP(this)) - track_jump = true; - - if (track_jump) - if (IS_JUMP_HELD(this)) - return true; - - // sv_jumpspeedcap_min/sv_jumpspeedcap_max act as baseline - // velocity bounds. Final velocity is bound between (jumpheight * - // min + jumpheight) and (jumpheight * max + jumpheight); - - if(PHYS_JUMPSPEEDCAP_MIN != "") - { - float minjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MIN); - - if (this.velocity_z < minjumpspeed) - mjumpheight += minjumpspeed - this.velocity_z; - } - - if(PHYS_JUMPSPEEDCAP_MAX != "") - { - // don't do jump speedcaps on ramps to preserve old xonotic ramjump style - tracebox(this.origin + '0 0 0.01', this.mins, this.maxs, this.origin - '0 0 0.01', MOVE_NORMAL, this); - - if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(this))) - { - float maxjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MAX); - - if (this.velocity_z > maxjumpspeed) - mjumpheight -= this.velocity_z - maxjumpspeed; - } - } - - 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) - { - float f = (1 - PHYS_FRICTION_ONLAND(this)); - this.velocity_x *= f; - this.velocity_y *= f; - } -#ifdef SVQC - if(this.jumppadcount > 1) - LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n")); - this.jumppadcount = 0; -#endif - } - - this.velocity_z += mjumpheight; - - UNSET_ONGROUND(this); - SET_JUMP_HELD(this); - -#ifdef SVQC - - this.oldvelocity_z = this.velocity_z; - - animdecide_setaction(this, ANIMACTION_JUMP, true); - - if (autocvar_g_jump_grunt) - PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); -#endif - return true; -} - -void CheckWaterJump(entity this) -{ -// check for a jump-out-of-water - makevectors(this.v_angle); - vector start = this.origin; - start_z += 8; - v_forward_z = 0; - normalize(v_forward); - vector end = start + v_forward*24; - traceline (start, end, true, this); - if (trace_fraction < 1) - { // solid at waist - start_z = start_z + this.maxs_z - 8; - end = start + v_forward*24; - this.movedir = trace_plane_normal * -50; - traceline(start, end, true, this); - if (trace_fraction == 1) - { // open at eye level - this.velocity_z = 225; - this.flags |= FL_WATERJUMP; - SET_JUMP_HELD(this); - #ifdef SVQC - PHYS_TELEPORT_TIME(this) = time + 2; // safety net - #elif defined(CSQC) - pmove_waterjumptime = time + 2; - #endif - } - } -} - - -#ifdef SVQC - #define JETPACK_JUMP(s) s.cvar_cl_jetpack_jump -#elif defined(CSQC) - float autocvar_cl_jetpack_jump; - #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump -#endif -.float jetpack_stopped; -void CheckPlayerJump(entity this) -{ -#ifdef SVQC - float was_flying = ITEMS_STAT(this) & IT_USING_JETPACK; -#endif - if (JETPACK_JUMP(this) < 2) - ITEMS_STAT(this) &= ~IT_USING_JETPACK; - - if(PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this)) - { - float air_jump = !PlayerJump(this) || player_multijump; // PlayerJump() has important side effects - float activate = JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this); - float has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO; - - if (!(ITEMS_STAT(this) & ITEM_Jetpack.m_itemid)) { } - else if (this.jetpack_stopped) { } - else if (!has_fuel) - { -#ifdef SVQC - if (was_flying) // TODO: ran out of fuel message - Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL); - else if (activate) - Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL); -#endif - this.jetpack_stopped = true; - ITEMS_STAT(this) &= ~IT_USING_JETPACK; - } - else if (activate && !PHYS_FROZEN(this)) - ITEMS_STAT(this) |= IT_USING_JETPACK; - } - else - { - this.jetpack_stopped = false; - ITEMS_STAT(this) &= ~IT_USING_JETPACK; - } - if (!PHYS_INPUT_BUTTON_JUMP(this)) - UNSET_JUMP_HELD(this); - - if (this.waterlevel == WATERLEVEL_SWIMMING) - CheckWaterJump(this); -} - -float racecar_angle(float forward, float down) -{ - if (forward < 0) - { - forward = -forward; - down = -down; - } - - float ret = vectoyaw('0 1 0' * down + '1 0 0' * forward); - - float angle_mult = forward / (800 + forward); - - if (ret > 180) - return ret * angle_mult + 360 * (1 - angle_mult); - else - return ret * angle_mult; -} - -string specialcommand = "xwxwxsxsxaxdxaxdx1x "; -.float specialcommand_pos; -void SpecialCommand() -{ -#ifdef SVQC - if (!CheatImpulse(CHIMPULSE_GIVE_ALL.impulse)) - LOG_INFO("A hollow voice says \"Plugh\".\n"); -#endif -} - -float PM_check_specialcommand(entity this, float buttons) -{ -#ifdef SVQC - string c; - if (!buttons) - c = "x"; - else if (buttons == 1) - c = "1"; - else if (buttons == 2) - c = " "; - else if (buttons == 128) - c = "s"; - else if (buttons == 256) - c = "w"; - else if (buttons == 512) - c = "a"; - else if (buttons == 1024) - c = "d"; - else - c = "?"; - - if (c == substring(specialcommand, this.specialcommand_pos, 1)) - { - this.specialcommand_pos += 1; - if (this.specialcommand_pos >= strlen(specialcommand)) - { - this.specialcommand_pos = 0; - SpecialCommand(); - return true; - } - } - else if (this.specialcommand_pos && (c != substring(specialcommand, this.specialcommand_pos - 1, 1))) - this.specialcommand_pos = 0; -#endif - return false; -} - -void PM_check_nickspam(entity this) -{ -#ifdef SVQC - if (time >= this.nickspamtime) - return; - if (this.nickspamcount >= autocvar_g_nick_flood_penalty_yellow) - { - // slight annoyance for nick change scripts - this.movement = -1 * this.movement; - this.BUTTON_ATCK = this.BUTTON_JUMP = this.BUTTON_ATCK2 = this.BUTTON_ZOOM = this.BUTTON_CROUCH = this.BUTTON_HOOK = this.BUTTON_USE = 0; - - if (this.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you! - { - this.v_angle_x = random() * 360; - this.v_angle_y = random() * 360; - // at least I'm not forcing retardedview by also assigning to angles_z - this.fixangle = true; - } - } -#endif -} - -void PM_check_punch(entity this) -{ -#ifdef SVQC - if (this.punchangle != '0 0 0') - { - float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH; - if (f > 0) - this.punchangle = normalize(this.punchangle) * f; - else - this.punchangle = '0 0 0'; - } - - if (this.punchvector != '0 0 0') - { - float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH; - if (f > 0) - this.punchvector = normalize(this.punchvector) * f; - else - this.punchvector = '0 0 0'; - } -#endif -} - -// predict frozen movement, as frozen players CAN move in some cases -void PM_check_frozen(entity this) -{ - if (!PHYS_FROZEN(this)) - return; - if (PHYS_DODGING_FROZEN(this) -#ifdef SVQC - && IS_REAL_CLIENT(this) -#endif - ) - { - this.movement_x = bound(-5, this.movement.x, 5); - this.movement_y = bound(-5, this.movement.y, 5); - this.movement_z = bound(-5, this.movement.z, 5); - } - else - this.movement = '0 0 0'; - - vector midpoint = ((this.absmin + this.absmax) * 0.5); - if (pointcontents(midpoint) == CONTENT_WATER) - { - this.velocity = this.velocity * 0.5; - - if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER) - this.velocity_z = 200; - } -} - -void PM_check_hitground(entity this) -{ -#ifdef SVQC - if (!this.wasFlying) return; - this.wasFlying = false; - if (this.waterlevel >= WATERLEVEL_SWIMMING) return; - if (time < this.ladder_time) return; - if (this.hook) return; - this.nextstep = time + 0.3 + random() * 0.1; - trace_dphitq3surfaceflags = 0; - tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this); - if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return; - entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) - ? GS_FALL_METAL - : GS_FALL; - GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND); -#endif -} - -void PM_Footsteps(entity this) -{ -#ifdef SVQC - if (!g_footsteps) return; - if (IS_DUCKED(this)) return; - if (time >= this.lastground + 0.2) return; - if (vdist(this.velocity, <=, autocvar_sv_maxspeed * 0.6)) return; - if ((time > this.nextstep) || (time < (this.nextstep - 10.0))) - { - this.nextstep = time + 0.3 + random() * 0.1; - trace_dphitq3surfaceflags = 0; - tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this); - if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return; - entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) - ? GS_STEP_METAL - : GS_STEP; - GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND); - } -#endif -} - -void PM_check_blocked(entity this) -{ -#ifdef SVQC - if (!this.player_blocked) - return; - this.movement = '0 0 0'; - this.disableclientprediction = 1; -#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)) -#endif - PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0); - PM_ClientMovement_Move(this); -} - -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; - #ifdef CSQC - pmove_waterjumptime = 2; - #endif - UNSET_ONGROUND(this); - SET_JUMP_HELD(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 = -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 (IS_DUCKED(this)) - wishspeed *= 0.5; - -// 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 - 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); - PM_ClientMovement_Move(this); - } -} - -.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); - if(time >= PHYS_TELEPORT_TIME(this)) - // water acceleration - PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0); - PM_ClientMovement_Move(this); -} - -void PM_jetpack(entity this, float maxspd_mod) -{ - //makevectors(this.v_angle.y * '0 1 0'); - makevectors(this.v_angle); - vector wishvel = v_forward * this.movement_x - + v_right * this.movement_y; - // add remaining speed as Z component - float maxairspd = PHYS_MAXAIRSPEED(this) * max(1, maxspd_mod); - // fix speedhacks :P - wishvel = normalize(wishvel) * min(1, vlen(wishvel) / maxairspd); - // add the unused velocity as up component - wishvel_z = 0; - - // if (this.BUTTON_JUMP) - wishvel_z = sqrt(max(0, 1 - wishvel * wishvel)); - - // it is now normalized, so... - float a_side = PHYS_JETPACK_ACCEL_SIDE(this); - float a_up = PHYS_JETPACK_ACCEL_UP(this); - float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this); - - wishvel_x *= a_side; - wishvel_y *= a_side; - wishvel_z *= a_up; - wishvel_z += a_add; - - float best = 0; - ////////////////////////////////////////////////////////////////////////////////////// - // finding the maximum over all vectors of above form - // with wishvel having an absolute value of 1 - ////////////////////////////////////////////////////////////////////////////////////// - // we're finding the maximum over - // f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2; - // for z in the range from -1 to 1 - ////////////////////////////////////////////////////////////////////////////////////// - // maximum is EITHER attained at the single extreme point: - float a_diff = a_side * a_side - a_up * a_up; - float f; - if (a_diff != 0) - { - f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z) - if (f > -1 && f < 1) // can it be attained? - { - best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff; - //print("middle\n"); - } - } - // OR attained at z = 1: - f = (a_up + a_add) * (a_up + a_add); - if (f > best) - { - best = f; - //print("top\n"); - } - // OR attained at z = -1: - f = (a_up - a_add) * (a_up - a_add); - if (f > best) - { - best = f; - //print("bottom\n"); - } - best = sqrt(best); - ////////////////////////////////////////////////////////////////////////////////////// - - //print("best possible acceleration: ", ftos(best), "\n"); - - float fxy, fz; - fxy = bound(0, 1 - (this.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE(this), 1); - if (wishvel_z - PHYS_GRAVITY(this) > 0) - fz = bound(0, 1 - this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1); - else - fz = bound(0, 1 + this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1); - - float fvel; - fvel = vlen(wishvel); - wishvel_x *= fxy; - wishvel_y *= fxy; - wishvel_z = (wishvel_z - PHYS_GRAVITY(this)) * fz + PHYS_GRAVITY(this); - - fvel = min(1, vlen(wishvel) / best); - if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) - f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel)); - else - f = 1; - - //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n"); - - if (f > 0 && wishvel != '0 0 0') - { - this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH; - UNSET_ONGROUND(this); - -#ifdef SVQC - if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) - this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f; - - ITEMS_STAT(this) |= IT_USING_JETPACK; - - // jetpack also inhibits health regeneration, but only for 1 second - this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen); -#endif - } - -#ifdef CSQC - float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; - if(autocvar_cl_movement == 3) - { - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; - else - this.velocity_z -= g; - } - PM_ClientMovement_Move(this); - if(autocvar_cl_movement == 3) - { - if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; - } -#endif -} - -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; - } -#ifdef CSQC - float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; - if(autocvar_cl_movement == 3) - { - if (!(GAMEPLAYFIX_NOGRAVITYONGROUND)) - this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1); - } - if (vdist(this.velocity, >, 0)) - PM_ClientMovement_Move(this); - if(autocvar_cl_movement == 3) - { - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND) - this.velocity_z -= g * 0.5; - } -#endif -} - -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 - PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(this)); - - if (PHYS_AIRCONTROL(this)) - CPM_PM_Aircontrol(this, wishdir, wishspeed2); - } -#ifdef CSQC - float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; - if(autocvar_cl_movement == 3) - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; - else - this.velocity_z -= g; -#endif - PM_ClientMovement_Move(this); -#ifdef CSQC - if(autocvar_cl_movement == 3) - if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; -#endif -} - -// used for calculating airshots -bool IsFlying(entity this) -{ - if(IS_ONGROUND(this)) - return false; - if(this.waterlevel >= WATERLEVEL_SWIMMING) - return false; - traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this); - if(trace_fraction < 1) - return false; - 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); - - vector oldv_angle = this.v_angle; - vector oldangles = this.angles; // we need to save these, as they're abused by other code - this.v_angle = PHYS_INPUT_ANGLES(this); - this.angles = PHYS_WORLD_ANGLES(this); - - 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, true); -#endif - - this.oldmovement = this.movement; - - -#ifdef SVQC - WarpZone_PlayerPhysics_FixVAngle(); -#endif - float maxspeed_mod = 1; - maxspeed_mod *= PHYS_HIGHSPEED(this); - -#ifdef SVQC - Physics_UpdateStats(this, maxspeed_mod); - - if (this.PlayerPhysplug) - if (this.PlayerPhysplug()) - return; -#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; - WITH(entity, self, this, bot_think()); - } -#endif - -#ifdef SVQC - if (IS_PLAYER(this)) - { - 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; - } - } -#endif - -#ifdef SVQC - if (this.movetype == MOVETYPE_NONE) - return; - - // when we get here, disableclientprediction cannot be 2 - this.disableclientprediction = 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 != "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(PHYS_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); - } - - if(IsFlying(this)) - this.wasFlying = 1; - - if (IS_PLAYER(this)) - CheckPlayerJump(this); - - if (this.flags & FL_WATERJUMP) - { - this.velocity_x = this.movedir.x; - this.velocity_y = this.movedir.y; - if (time > PHYS_TELEPORT_TIME(this) || this.waterlevel == WATERLEVEL_NONE) - { - this.flags &= ~FL_WATERJUMP; - PHYS_TELEPORT_TIME(this) = 0; - } - } - - else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod)) - { } - -#ifdef SVQC - else if (this.movetype == MOVETYPE_NOCLIP || this.movetype == MOVETYPE_FLY || this.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); - -: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; - -#ifdef CSQC - this.v_angle = oldv_angle; - this.angles = oldangles; -#endif -} - -#if defined(SVQC) -void SV_PlayerPhysics() -#elif defined(CSQC) -void CSQC_ClientMovement_PlayerMove_Frame(entity this) -#endif -{ -#ifdef SVQC - SELFPARAM(); -#endif - PM_Main(this); -} diff --git a/qcsrc/common/physics.qh b/qcsrc/common/physics.qh deleted file mode 100644 index 66824bbcf..000000000 --- a/qcsrc/common/physics.qh +++ /dev/null @@ -1,310 +0,0 @@ -#ifndef COMMON_PHYSICS_H -#define COMMON_PHYSICS_H - -// Client/server mappings - -.entity conveyor; - -.float race_penalty; - -.float gravity; -.float swamp_slowdown; -.float lastflags; -.float lastground; -.float wasFlying; -#ifdef SVQC -.float spectatorspeed = _STAT(SPECTATORSPEED); -#elif defined(CSQC) -.float spectatorspeed; -#endif - -.vector movement_old; -.float buttons_old; -.vector v_angle_old; -.string lastclassname; - -.float() PlayerPhysplug; -float AdjustAirAccelQW(float accelqw, float factor); - -bool IsFlying(entity a); - -#define BUFFS_STAT(s) STAT(BUFFS, s) - -#define GAMEPLAYFIX_DOWNTRACEONGROUND(s) STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, s) -#define GAMEPLAYFIX_EASIERWATERJUMP(s) STAT(GAMEPLAYFIX_EASIERWATERJUMP, s) -#define GAMEPLAYFIX_STEPDOWN(s) STAT(GAMEPLAYFIX_STEPDOWN, s) -#define GAMEPLAYFIX_STEPMULTIPLETIMES(s) STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, s) -#define GAMEPLAYFIX_UNSTICKPLAYERS(s) STAT(GAMEPLAYFIX_UNSTICKPLAYERS, s) - -#define PHYS_ACCELERATE(s) STAT(MOVEVARS_ACCELERATE, s) -#define PHYS_AIRACCELERATE(s) STAT(MOVEVARS_AIRACCELERATE, s) -#define PHYS_AIRACCEL_QW(s) STAT(MOVEVARS_AIRACCEL_QW, s) -#define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, s) -#define PHYS_AIRACCEL_SIDEWAYS_FRICTION(s) STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, s) -#define PHYS_AIRCONTROL(s) STAT(MOVEVARS_AIRCONTROL, s) -#define PHYS_AIRCONTROL_PENALTY(s) STAT(MOVEVARS_AIRCONTROL_PENALTY, s) -#define PHYS_AIRCONTROL_POWER(s) STAT(MOVEVARS_AIRCONTROL_POWER, s) -#define PHYS_AIRSPEEDLIMIT_NONQW(s) STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, s) -#define PHYS_AIRSTOPACCELERATE(s) STAT(MOVEVARS_AIRSTOPACCELERATE, s) -#define PHYS_AIRSTRAFEACCELERATE(s) STAT(MOVEVARS_AIRSTRAFEACCELERATE, s) -#define PHYS_AIRSTRAFEACCEL_QW(s) STAT(MOVEVARS_AIRSTRAFEACCEL_QW, s) - -#define PHYS_AMMO_FUEL(s) STAT(FUEL, s) - -#define PHYS_DODGING_FROZEN(s) STAT(DODGING_FROZEN, s) - -#define PHYS_FRICTION(s) STAT(MOVEVARS_FRICTION, s) -#define PHYS_FRICTION_ONLAND(s) STAT(MOVEVARS_FRICTION_ONLAND, s) -#define PHYS_FRICTION_SLICK(s) STAT(MOVEVARS_FRICTION_SLICK, s) - -#define PHYS_FROZEN(s) STAT(FROZEN, s) - -#define PHYS_HIGHSPEED(s) STAT(MOVEVARS_HIGHSPEED, s) - -#define PHYS_JETPACK_ACCEL_SIDE(s) STAT(JETPACK_ACCEL_SIDE, s) -#define PHYS_JETPACK_ACCEL_UP(s) STAT(JETPACK_ACCEL_UP, s) -#define PHYS_JETPACK_ANTIGRAVITY(s) STAT(JETPACK_ANTIGRAVITY, s) -#define PHYS_JETPACK_FUEL(s) STAT(JETPACK_FUEL, s) -#define PHYS_JETPACK_MAXSPEED_SIDE(s) STAT(JETPACK_MAXSPEED_SIDE, s) -#define PHYS_JETPACK_MAXSPEED_UP(s) STAT(JETPACK_MAXSPEED_UP, s) - -#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, s) -#define PHYS_JUMPSTEP(s) STAT(MOVEVARS_JUMPSTEP, s) -#define PHYS_JUMPVELOCITY(s) STAT(MOVEVARS_JUMPVELOCITY, s) - -#define PHYS_MAXAIRSPEED(s) STAT(MOVEVARS_MAXAIRSPEED, s) -#define PHYS_MAXAIRSTRAFESPEED(s) STAT(MOVEVARS_MAXAIRSTRAFESPEED, s) -#define PHYS_MAXSPEED(s) STAT(MOVEVARS_MAXSPEED, s) - -#define PHYS_NOSTEP(s) STAT(NOSTEP, s) -#define PHYS_STEPHEIGHT(s) STAT(MOVEVARS_STEPHEIGHT, s) - -#define PHYS_STOPSPEED(s) STAT(MOVEVARS_STOPSPEED, s) - -#define PHYS_TRACK_CANJUMP(s) STAT(MOVEVARS_TRACK_CANJUMP, s) - -#define PHYS_WALLFRICTION(s) STAT(MOVEVARS_WALLFRICTION, s) - -#define PHYS_WARSOWBUNNY_ACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_ACCEL, s) -#define PHYS_WARSOWBUNNY_AIRFORWARDACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, s) -#define PHYS_WARSOWBUNNY_BACKTOSIDERATIO(s) STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, s) -#define PHYS_WARSOWBUNNY_TOPSPEED(s) STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, s) -#define PHYS_WARSOWBUNNY_TURNACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, s) - -#define UPWARD_VELOCITY_CLEARS_ONGROUND(s) STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, s) - -#define PHYS_INPUT_BUTTON_ATCK(s) PHYS_INPUT_BUTTON_BUTTON1(s) -#define PHYS_INPUT_BUTTON_JUMP(s) PHYS_INPUT_BUTTON_BUTTON2(s) -#define PHYS_INPUT_BUTTON_ATCK2(s) PHYS_INPUT_BUTTON_BUTTON3(s) -#define PHYS_INPUT_BUTTON_ZOOM(s) PHYS_INPUT_BUTTON_BUTTON4(s) -#define PHYS_INPUT_BUTTON_CROUCH(s) PHYS_INPUT_BUTTON_BUTTON5(s) -#define PHYS_INPUT_BUTTON_HOOK(s) PHYS_INPUT_BUTTON_BUTTON6(s) -#define PHYS_INPUT_BUTTON_INFO(s) PHYS_INPUT_BUTTON_BUTTON7(s) -#define PHYS_INPUT_BUTTON_DRAG(s) PHYS_INPUT_BUTTON_BUTTON8(s) -#define PHYS_INPUT_BUTTON_USE(s) PHYS_INPUT_BUTTON_BUTTON_USE(s) -#define PHYS_INPUT_BUTTON_CHAT(s) PHYS_INPUT_BUTTON_BUTTON_CHAT(s) -#define PHYS_INPUT_BUTTON_PRYDON(s) PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) -#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s) PHYS_INPUT_BUTTON_BUTTON9(s) -#define PHYS_INPUT_BUTTON_JETPACK(s) PHYS_INPUT_BUTTON_BUTTON10(s) - -// if more buttons are needed, start using impulse bits as buttons - -#define PHYS_INPUT_BUTTON_BACKWARD(s) (PHYS_INPUT_MOVEVALUES(s).x < 0) -#define PHYS_INPUT_BUTTON_FORWARD(s) (PHYS_INPUT_MOVEVALUES(s).x > 0) -#define PHYS_INPUT_BUTTON_LEFT(s) (PHYS_INPUT_MOVEVALUES(s).y < 0) -#define PHYS_INPUT_BUTTON_RIGHT(s) (PHYS_INPUT_MOVEVALUES(s).y > 0) - -// used for special commands and idle checking, not from the engine -// TODO: cache -#define PHYS_INPUT_BUTTON_MASK(s) ( \ - (1 << 0) * PHYS_INPUT_BUTTON_ATCK(s) \ - | (1 << 1) * PHYS_INPUT_BUTTON_JUMP(s) \ - | (1 << 2) * PHYS_INPUT_BUTTON_ATCK2(s) \ - | (1 << 3) * PHYS_INPUT_BUTTON_ZOOM(s) \ - | (1 << 4) * PHYS_INPUT_BUTTON_CROUCH(s) \ - | (1 << 5) * PHYS_INPUT_BUTTON_HOOK(s) \ - | (1 << 6) * PHYS_INPUT_BUTTON_USE(s) \ - | (1 << 7) * PHYS_INPUT_BUTTON_BACKWARD(s) \ - | (1 << 8) * PHYS_INPUT_BUTTON_FORWARD(s) \ - | (1 << 9) * PHYS_INPUT_BUTTON_LEFT(s) \ - | (1 << 10) * PHYS_INPUT_BUTTON_RIGHT(s) \ - ) - -#define IS_JUMP_HELD(s) (!((s).flags & FL_JUMPRELEASED)) -#define SET_JUMP_HELD(s) ((s).flags &= ~FL_JUMPRELEASED) -#define UNSET_JUMP_HELD(s) ((s).flags |= FL_JUMPRELEASED) - -#define IS_ONGROUND(s) boolean((s).flags & FL_ONGROUND) -#define SET_ONGROUND(s) ((s).flags |= FL_ONGROUND) -#define UNSET_ONGROUND(s) ((s).flags &= ~FL_ONGROUND) - -#define WAS_ONGROUND(s) boolean((s).lastflags & FL_ONGROUND) - -#define ITEMS_STAT(s) ((s).items) - -#ifdef CSQC - - 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 - - .float watertype; - .float waterlevel; - .int items; - - .vector movement; - .vector v_angle; - -// TODO - #define IS_CLIENT(s) ((s).isplayermodel) - #define IS_PLAYER(s) ((s).isplayermodel) - #define IS_NOT_A_CLIENT(s) (!(s).isplayermodel) - #define isPushable(s) ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE)) - - //float player_multijump; - //float player_jumpheight; - - #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 -// TODO - #define PHYS_WORLD_ANGLES(s) input_angles - - #define PHYS_INPUT_TIMELENGTH input_timelength - #define PHYS_INPUT_FRAMETIME serverdeltatime - - #define PHYS_INPUT_MOVEVALUES(s) input_movevalues - - #define PHYS_INPUT_BUTTON_BUTTON1(s) boolean(input_buttons & BIT(0)) - #define PHYS_INPUT_BUTTON_BUTTON2(s) boolean(input_buttons & BIT(1)) - #define PHYS_INPUT_BUTTON_BUTTON3(s) boolean(input_buttons & BIT(2)) - #define PHYS_INPUT_BUTTON_BUTTON4(s) boolean(input_buttons & BIT(3)) - #define PHYS_INPUT_BUTTON_BUTTON5(s) boolean(input_buttons & BIT(4)) - #define PHYS_INPUT_BUTTON_BUTTON6(s) boolean(input_buttons & BIT(5)) - #define PHYS_INPUT_BUTTON_BUTTON7(s) boolean(input_buttons & BIT(6)) - #define PHYS_INPUT_BUTTON_BUTTON8(s) boolean(input_buttons & BIT(7)) - #define PHYS_INPUT_BUTTON_BUTTON_USE(s) boolean(input_buttons & BIT(8)) - #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) boolean(input_buttons & BIT(9)) - #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) boolean(input_buttons & BIT(10)) - #define PHYS_INPUT_BUTTON_BUTTON9(s) boolean(input_buttons & BIT(11)) - #define PHYS_INPUT_BUTTON_BUTTON10(s) boolean(input_buttons & BIT(12)) - #define PHYS_INPUT_BUTTON_BUTTON11(s) boolean(input_buttons & BIT(13)) - #define PHYS_INPUT_BUTTON_BUTTON12(s) boolean(input_buttons & BIT(14)) - #define PHYS_INPUT_BUTTON_BUTTON13(s) boolean(input_buttons & BIT(15)) - #define PHYS_INPUT_BUTTON_BUTTON14(s) boolean(input_buttons & BIT(16)) - #define PHYS_INPUT_BUTTON_BUTTON15(s) boolean(input_buttons & BIT(17)) - #define PHYS_INPUT_BUTTON_BUTTON16(s) boolean(input_buttons & BIT(18)) - - #define PHYS_DEAD(s) ((s).csqcmodel_isdead) - - #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE (boolean(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)) - #define GAMEPLAYFIX_NOGRAVITYONGROUND (boolean(moveflags & MOVEFLAG_NOGRAVITYONGROUND)) - #define GAMEPLAYFIX_Q2AIRACCELERATE (boolean(moveflags & MOVEFLAG_Q2AIRACCELERATE)) - - #define IS_DUCKED(s) (boolean((s).flags & FL_DUCKED)) - #define SET_DUCKED(s) ((s).flags |= FL_DUCKED) - #define UNSET_DUCKED(s) ((s).flags &= ~FL_DUCKED) - - #define PHYS_JUMPSPEEDCAP_MIN autocvar_cl_jumpspeedcap_min - #define PHYS_JUMPSPEEDCAP_MAX autocvar_cl_jumpspeedcap_max - - #define PHYS_CL_TRACK_CANJUMP(s) STAT(MOVEVARS_CL_TRACK_CANJUMP, s) - // FIXME: 0 doesn't mean zero gravity - #define PHYS_ENTGRAVITY(s) STAT(MOVEVARS_ENTGRAVITY, s) - -#elif defined(SVQC) - - bool Physics_Valid(string thecvar); - - .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW); - .float stat_sv_maxspeed = _STAT(MOVEVARS_MAXSPEED); - - /** Not real stats */ - .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 - - #define PHYS_INPUT_ANGLES(s) ((s).v_angle) - #define PHYS_WORLD_ANGLES(s) ((s).angles) - - #define PHYS_INPUT_TIMELENGTH frametime - #define PHYS_INPUT_FRAMETIME sys_frametime - - #define PHYS_INPUT_MOVEVALUES(s) ((s).movement) - - #define PHYS_INPUT_BUTTON_BUTTON1(s) ((s).button0) - #define PHYS_INPUT_BUTTON_BUTTON2(s) ((s).button2) - #define PHYS_INPUT_BUTTON_BUTTON3(s) ((s).button3) - #define PHYS_INPUT_BUTTON_BUTTON4(s) ((s).button4) - #define PHYS_INPUT_BUTTON_BUTTON5(s) ((s).button5) - #define PHYS_INPUT_BUTTON_BUTTON6(s) ((s).button6) - #define PHYS_INPUT_BUTTON_BUTTON7(s) ((s).button7) - #define PHYS_INPUT_BUTTON_BUTTON8(s) ((s).button8) - #define PHYS_INPUT_BUTTON_BUTTON_USE(s) ((s).buttonuse) - #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) ((s).buttonchat) - #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) ((s).cursor_active) - #define PHYS_INPUT_BUTTON_BUTTON9(s) ((s).button9) - #define PHYS_INPUT_BUTTON_BUTTON10(s) ((s).button10) - #define PHYS_INPUT_BUTTON_BUTTON11(s) ((s).button11) - #define PHYS_INPUT_BUTTON_BUTTON12(s) ((s).button12) - #define PHYS_INPUT_BUTTON_BUTTON13(s) ((s).button13) - #define PHYS_INPUT_BUTTON_BUTTON14(s) ((s).button14) - #define PHYS_INPUT_BUTTON_BUTTON15(s) ((s).button15) - #define PHYS_INPUT_BUTTON_BUTTON16(s) ((s).button16) - - #define PHYS_DEAD(s) ((s).deadflag != DEAD_NO) - - #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate - #define GAMEPLAYFIX_NOGRAVITYONGROUND autocvar_sv_gameplayfix_nogravityonground - #define GAMEPLAYFIX_Q2AIRACCELERATE autocvar_sv_gameplayfix_q2airaccelerate - - #define IS_DUCKED(s) ((s).crouch) - #define SET_DUCKED(s) ((s).crouch = true) - #define UNSET_DUCKED(s) ((s).crouch = false) - - #define PHYS_JUMPSPEEDCAP_MIN autocvar_sv_jumpspeedcap_min - #define PHYS_JUMPSPEEDCAP_MAX autocvar_sv_jumpspeedcap_max - - #define PHYS_CL_TRACK_CANJUMP(s) ((s).cvar_cl_movement_track_canjump) - #define PHYS_ENTGRAVITY(s) ((s).gravity) - -#endif - -REGISTER_NET_C2S(setpause) -#ifdef CSQC -void unpause_update() -{ - static bool waspaused; - bool ispaused = PHYS_INPUT_BUTTON_CHAT(this); - if (ispaused == waspaused) return; - waspaused = ispaused; - // if (!serverispaused) return; // TODO: find out somehow - if (ispaused) return; // ignore setting pause, server will get those presses anyway, but it won't get releases - int channel = MSG_C2S; - WriteHeader(channel, setpause); - WriteByte(channel, ispaused); -} -#endif -#ifdef SVQC -NET_HANDLE(setpause, bool) -{ - bool ispaused = boolean(ReadByte()); - PHYS_INPUT_BUTTON_CHAT(sender) = ispaused; - return true; -} -#endif - -#endif diff --git a/qcsrc/common/physics/all.inc b/qcsrc/common/physics/all.inc new file mode 100644 index 000000000..cea8df71e --- /dev/null +++ b/qcsrc/common/physics/all.inc @@ -0,0 +1,3 @@ +#include "player.qc" +#include "movelib.qc" +#include "movetypes/include.qc" diff --git a/qcsrc/common/physics/movelib.qc b/qcsrc/common/physics/movelib.qc new file mode 100644 index 000000000..acacba093 --- /dev/null +++ b/qcsrc/common/physics/movelib.qc @@ -0,0 +1,237 @@ +#include "movelib.qh" + +#ifdef SVQC +.vector moveto; + +/** + Simulate drag + self.velocity = movelib_dragvec(self.velocity,0.02,0.5); +**/ +vector movelib_dragvec(float drag, float exp_) +{SELFPARAM(); + float lspeed,ldrag; + + lspeed = vlen(self.velocity); + ldrag = lspeed * drag; + ldrag = ldrag * (drag * exp_); + ldrag = 1 - (ldrag / lspeed); + + return self.velocity * ldrag; +} + +/** + Simulate drag + self.velocity *= movelib_dragflt(somespeed,0.01,0.7); +**/ +float movelib_dragflt(float fspeed,float drag,float exp_) +{ + float ldrag; + + ldrag = fspeed * drag; + ldrag = ldrag * ldrag * exp_; + ldrag = 1 - (ldrag / fspeed); + + return ldrag; +} + +/** + Do a inertia simulation based on velocity. + Basicaly, this allows you to simulate loss of steering with higher speed. + self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9); +**/ +vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax) +{SELFPARAM(); + float influense; + + influense = vlen(self.velocity) * (1 / vel_max); + + influense = bound(newmin,influense,oldmax); + + return (vel_new * (1 - influense)) + (self.velocity * influense); +} + +vector movelib_inertmove(vector new_vel,float new_bias) +{SELFPARAM(); + return new_vel * new_bias + self.velocity * (1-new_bias); +} + +void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce) +{SELFPARAM(); + float deltatime; + float acceleration; + float mspeed; + vector breakvec; + + deltatime = time - self.movelib_lastupdate; + if (deltatime > 0.15) deltatime = 0; + self.movelib_lastupdate = time; + if (!deltatime) return; + + mspeed = vlen(self.velocity); + + if (theMass) + acceleration = vlen(force) / theMass; + else + acceleration = vlen(force); + + if (self.flags & FL_ONGROUND) + { + if (breakforce) + { + breakvec = (normalize(self.velocity) * (breakforce / theMass) * deltatime); + self.velocity = self.velocity - breakvec; + } + + self.velocity = self.velocity + force * (acceleration * deltatime); + } + + if (drag) + self.velocity = movelib_dragvec(drag, 1); + + if (self.waterlevel > 1) + { + self.velocity = self.velocity + force * (acceleration * deltatime); + self.velocity = self.velocity + '0 0 0.05' * autocvar_sv_gravity * deltatime; + } + else + self.velocity = self.velocity + '0 0 -1' * autocvar_sv_gravity * deltatime; + + mspeed = vlen(self.velocity); + + if (max_velocity) + if (mspeed > max_velocity) + self.velocity = normalize(self.velocity) * (mspeed - 50);//* max_velocity; +} + +/* +.float mass; +.float side_friction; +.float ground_friction; +.float air_friction; +.float water_friction; +.float buoyancy; +float movelib_deltatime; + +void movelib_startupdate() +{ + movelib_deltatime = time - self.movelib_lastupdate; + + if (movelib_deltatime > 0.5) + movelib_deltatime = 0; + + self.movelib_lastupdate = time; +} + +void movelib_update(vector dir,float force) +{ + vector acceleration; + float old_speed; + float ffriction,v_z; + + vector breakvec; + vector old_dir; + vector ggravity; + vector old; + + if(!movelib_deltatime) + return; + v_z = self.velocity_z; + old_speed = vlen(self.velocity); + old_dir = normalize(self.velocity); + + //ggravity = (autocvar_sv_gravity / self.mass) * '0 0 100'; + acceleration = (force / self.mass) * dir; + //acceleration -= old_dir * (old_speed / self.mass); + acceleration -= ggravity; + + if(self.waterlevel > 1) + { + ffriction = self.water_friction; + acceleration += self.buoyancy * '0 0 1'; + } + else + if(self.flags & FL_ONGROUND) + ffriction = self.ground_friction; + else + ffriction = self.air_friction; + + acceleration *= ffriction; + //self.velocity = self.velocity * (ffriction * movelib_deltatime); + self.velocity += acceleration * movelib_deltatime; + self.velocity_z = v_z; + +} +*/ + +void movelib_beak_simple(float force) +{SELFPARAM(); + float mspeed; + vector mdir; + float vz; + + mspeed = max(0,vlen(self.velocity) - force); + mdir = normalize(self.velocity); + vz = self.velocity.z; + self.velocity = mdir * mspeed; + self.velocity_z = vz; +} + +/** +Pitches and rolls the entity to match the gound. +Yed need to set v_up and v_forward (generally by calling makevectors) before calling this. +**/ +#endif + +void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max) +{SELFPARAM(); + vector a, b, c, d, e, r, push_angle, ahead, side; + + push_angle.y = 0; + r = (self.absmax + self.absmin) * 0.5 + (v_up * spring_up); + e = v_up * spring_length; + + // Put springs slightly inside bbox + ahead = v_forward * (self.maxs.x * 0.8); + side = v_right * (self.maxs.y * 0.8); + + a = r + ahead + side; + b = r + ahead - side; + c = r - ahead + side; + d = r - ahead - side; + + traceline(a, a - e,MOVE_NORMAL,self); + a.z = (1 - trace_fraction); + r = trace_endpos; + + traceline(b, b - e,MOVE_NORMAL,self); + b.z = (1 - trace_fraction); + r += trace_endpos; + + traceline(c, c - e,MOVE_NORMAL,self); + c.z = (1 - trace_fraction); + r += trace_endpos; + + traceline(d, d - e,MOVE_NORMAL,self); + d.z = (1 - trace_fraction); + r += trace_endpos; + + a.x = r.z; + r = self.origin; + r.z = r.z; + + push_angle.x = (a.z - c.z) * _max; + push_angle.x += (b.z - d.z) * _max; + + push_angle.z = (b.z - a.z) * _max; + push_angle.z += (d.z - c.z) * _max; + + //self.angles_x += push_angle_x * 0.95; + //self.angles_z += push_angle_z * 0.95; + + self.angles_x = ((1-blendrate) * self.angles.x) + (push_angle.x * blendrate); + self.angles_z = ((1-blendrate) * self.angles.z) + (push_angle.z * blendrate); + + //a = self.origin; + setorigin(self,r); +} + diff --git a/qcsrc/common/physics/movelib.qh b/qcsrc/common/physics/movelib.qh new file mode 100644 index 000000000..8a4bfd488 --- /dev/null +++ b/qcsrc/common/physics/movelib.qh @@ -0,0 +1,53 @@ +#ifndef MOVELIB_H +#define MOVELIB_H + +#ifdef SVQC +.vector moveto; + +/** + Simulate drag + self.velocity = movelib_dragvec(self.velocity,0.02,0.5); +**/ +vector movelib_dragvec(float drag, float exp_); + +/** + Simulate drag + self.velocity *= movelib_dragflt(somespeed,0.01,0.7); +**/ +float movelib_dragflt(float fspeed,float drag,float exp_); + +/** + Do a inertia simulation based on velocity. + Basicaly, this allows you to simulate loss of steering with higher speed. + self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9); +**/ +vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax); + +vector movelib_inertmove(vector new_vel,float new_bias); + +.float movelib_lastupdate; +void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce); + +/* +void movelib_move_simple(vector newdir,float velo,float blendrate) +{ + self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo; +} +*/ +#define movelib_move_simple(newdir,velo,blendrate) \ + self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo + +#define movelib_move_simple_gravity(newdir,velo,blendrate) \ + if(self.flags & FL_ONGROUND) movelib_move_simple(newdir,velo,blendrate) + +void movelib_beak_simple(float force); + +/** +Pitches and rolls the entity to match the gound. +Yed need to set v_up and v_forward (generally by calling makevectors) before calling this. +**/ +#endif + +void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max); + +#endif diff --git a/qcsrc/common/physics/movetypes/follow.qc b/qcsrc/common/physics/movetypes/follow.qc new file mode 100644 index 000000000..2d3e24f44 --- /dev/null +++ b/qcsrc/common/physics/movetypes/follow.qc @@ -0,0 +1,31 @@ +void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow +{ + entity e = this.move_aiment; // TODO: networking? + + // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects + if(this.move_angles == this.move_punchangle) + { + this.move_origin = e.move_origin + this.view_ofs; + } + else + { + vector ang, v; + ang_x = -this.move_punchangle_x; + ang_y = this.move_punchangle_y; + ang_z = this.move_punchangle_z; + makevectors(ang); + v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x; + v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y; + v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.view_ofs_z * v_up_z; + ang_x = -e.move_angles_x; + ang_y = e.move_angles_y; + ang_z = e.move_angles_z; + makevectors(ang); + this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x; + this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y; + this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z; + } + + this.move_angles = e.move_angles + this.v_angle; + _Movetype_LinkEdict(this, false); +} diff --git a/qcsrc/common/physics/movetypes/include.qc b/qcsrc/common/physics/movetypes/include.qc new file mode 100644 index 000000000..322b3c4de --- /dev/null +++ b/qcsrc/common/physics/movetypes/include.qc @@ -0,0 +1,7 @@ +#include "push.qc" +#include "toss.qc" +#include "walk.qc" +#include "step.qc" +#include "follow.qc" + +#include "movetypes.qc" diff --git a/qcsrc/common/physics/movetypes/include.qh b/qcsrc/common/physics/movetypes/include.qh new file mode 100644 index 000000000..a96e5957a --- /dev/null +++ b/qcsrc/common/physics/movetypes/include.qh @@ -0,0 +1,8 @@ +#ifndef MOVETYPES_INCLUDE_H +#define MOVETYPES_INCLUDE_H + +#include "push.qh" +#include "toss.qh" +#include "walk.qh" + +#endif diff --git a/qcsrc/common/physics/movetypes/movetypes.qc b/qcsrc/common/physics/movetypes/movetypes.qc new file mode 100644 index 000000000..fc9396b30 --- /dev/null +++ b/qcsrc/common/physics/movetypes/movetypes.qc @@ -0,0 +1,717 @@ +#include "include.qh" +#include "../physics.qh" + +#if defined(CSQC) + #include "../../client/defs.qh" + #include "../stats.qh" + #include "../util.qh" + #include "movetypes.qh" + #include "../../lib/csqcmodel/common.qh" + #include "../../server/t_items.qh" +#elif defined(MENUQC) +#elif defined(SVQC) + #include "../../server/autocvars.qh" +#endif + +void _Movetype_WallFriction(entity this, vector stepnormal) // SV_WallFriction +{ + /*float d, i; + vector into, side; + makevectors(this.v_angle); + d = (stepnormal * v_forward) + 0.5; + + if(d < 0) + { + i = (stepnormal * this.move_velocity); + into = i * stepnormal; + side = this.move_velocity - into; + this.move_velocity_x = side.x * (1 * d); + this.move_velocity_y = side.y * (1 * d); + }*/ +} + +vector planes[MAX_CLIP_PLANES]; +int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight) // SV_FlyMove +{ + int blocked = 0, bumpcount; + int i, j, numplanes = 0; + float time_left = dt, grav = 0; + vector push; + vector primal_velocity, original_velocity, restore_velocity; + + for(i = 0; i < MAX_CLIP_PLANES; ++i) + planes[i] = '0 0 0'; + + if(applygravity) + { + this.move_didgravity = 1; + grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this); + + if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND)) + { + if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + this.move_velocity_z -= grav * 0.5; + else + this.move_velocity_z -= grav; + } + } + + original_velocity = primal_velocity = restore_velocity = this.move_velocity; + + for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++) + { + if(this.move_velocity == '0 0 0') + break; + + push = this.move_velocity * time_left; + _Movetype_PushEntity(this, push, true); + if(trace_startsolid) + { + // we got teleported by a touch function + // let's abort the move + blocked |= 8; + break; + } + + // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity + // abort move if we're stuck in the world (and didn't make it out) + if(trace_startsolid && trace_allsolid) + { + this.move_velocity = restore_velocity; + return 3; + } + + if(trace_fraction == 1) + break; + + float my_trace_fraction = trace_fraction; + vector my_trace_plane_normal = trace_plane_normal; + + if(trace_plane_normal.z) + { + if(trace_plane_normal.z > 0.7) + { + // floor + blocked |= 1; + + if(!trace_ent) + { + //dprint("_Movetype_FlyMove: !trace_ent\n"); + trace_ent = world; + } + + this.move_flags |= FL_ONGROUND; + this.move_groundentity = trace_ent; + } + } + else if(stepheight) + { + // step - handle it immediately + vector org = this.move_origin; + vector steppush = '0 0 1' * stepheight; + + _Movetype_PushEntity(this, steppush, true); + if(trace_startsolid) + { + blocked |= 8; + break; + } + _Movetype_PushEntity(this, push, true); + if(trace_startsolid) + { + blocked |= 8; + break; + } + float trace2_fraction = trace_fraction; + steppush = '0 0 1' * (org_z - this.move_origin_z); + _Movetype_PushEntity(this, steppush, true); + if(trace_startsolid) + { + blocked |= 8; + break; + } + + // accept the new position if it made some progress... + if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125) + { + trace_endpos = this.move_origin; + time_left *= 1 - trace2_fraction; + numplanes = 0; + continue; + } + else + this.move_origin = org; + } + else + { + // step - return it to caller + blocked |= 2; + // save the trace for player extrafriction + if(stepnormal) + stepnormal = trace_plane_normal; + } + + if(my_trace_fraction >= 0.001) + { + // actually covered some distance + original_velocity = this.move_velocity; + numplanes = 0; + } + + time_left *= 1 - my_trace_fraction; + + // clipped to another plane + if(numplanes >= MAX_CLIP_PLANES) + { + // this shouldn't really happen + this.move_velocity = '0 0 0'; + blocked = 3; + break; + } + + planes[numplanes] = my_trace_plane_normal; + numplanes++; + + // modify original_velocity so it parallels all of the clip planes + vector new_velocity = '0 0 0'; + for (i = 0;i < numplanes;i++) + { + new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1); + for (j = 0;j < numplanes;j++) + { + if(j != i) + { + // not ok + if((new_velocity * planes[j]) < 0) + break; + } + } + if(j == numplanes) + break; + } + + if(i != numplanes) + { + // go along this plane + this.move_velocity = new_velocity; + } + else + { + // go along the crease + if(numplanes != 2) + { + this.move_velocity = '0 0 0'; + blocked = 7; + break; + } + vector dir = cross(planes[0], planes[1]); + // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners + float ilength = sqrt((dir * dir)); + if(ilength) + ilength = 1.0 / ilength; + dir.x *= ilength; + dir.y *= ilength; + dir.z *= ilength; + float d = (dir * this.move_velocity); + this.move_velocity = dir * d; + } + + // if current velocity is against the original velocity, + // stop dead to avoid tiny occilations in sloping corners + if((this.move_velocity * primal_velocity) <= 0) + { + this.move_velocity = '0 0 0'; + break; + } + } + + // LordHavoc: this came from QW and allows you to get out of water more easily + if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8)) + this.move_velocity = primal_velocity; + + if(applygravity) + { + if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND)) + { + if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + this.move_velocity_z -= grav * 0.5f; + } + } + + return blocked; +} + +void _Movetype_CheckVelocity(entity this) // SV_CheckVelocity +{ + // if(vlen(this.move_velocity) < 0.0001) + // this.move_velocity = '0 0 0'; +} + +bool _Movetype_CheckWater(entity this) // SV_CheckWater +{ + vector point = this.move_origin; + point.z += this.mins.z + 1; + + int nativecontents = pointcontents(point); + if(this.move_watertype && this.move_watertype != nativecontents) + { + // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents); + if(this.contentstransition) + this.contentstransition(this.move_watertype, nativecontents); + } + + this.move_waterlevel = WATERLEVEL_NONE; + this.move_watertype = CONTENT_EMPTY; + + int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents); + if(supercontents & DPCONTENTS_LIQUIDSMASK) + { + this.move_watertype = nativecontents; + this.move_waterlevel = WATERLEVEL_WETFEET; + point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5; + if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK) + { + this.move_waterlevel = WATERLEVEL_SWIMMING; + point.z = this.move_origin.z + this.view_ofs.z; + if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK) + this.move_waterlevel = WATERLEVEL_SUBMERGED; + } + } + + return this.move_waterlevel > 1; +} + +void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition +{ + int contents = pointcontents(ent.move_origin); + + if(!ent.move_watertype) + { + // just spawned here + if(!autocvar_cl_gameplayfix_fixedcheckwatertransition) + { + ent.move_watertype = contents; + ent.move_waterlevel = 1; + return; + } + } + else if(ent.move_watertype != contents) + { + // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents); + if(ent.contentstransition) + ent.contentstransition(ent.move_watertype, contents); + } + + if(contents <= CONTENT_WATER) + { + ent.move_watertype = contents; + ent.move_waterlevel = 1; + } + else + { + ent.move_watertype = CONTENT_EMPTY; + ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents); + } +} + +void _Movetype_Impact(entity this, entity oth) // SV_Impact +{ + entity oldother = other; + + if(this.move_touch) + { + other = oth; + + WITH(entity, self, this, this.move_touch()); + + other = oldother; + } + + if(oth.move_touch) + { + other = this; + + WITH(entity, self, oth, oth.move_touch()); + + other = oldother; + } +} + +void _Movetype_LinkEdict_TouchAreaGrid(entity this) // SV_LinkEdict_TouchAreaGrid +{ + entity oldother = other; + + for (entity e = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); e; e = e.chain) + { + if(e.move_nomonsters != MOVE_NOMONSTERS && e.move_nomonsters != MOVE_WORLDONLY) + if(e.move_touch && boxesoverlap(e.absmin, e.absmax, this.absmin, this.absmax)) + { + other = this; + + trace_allsolid = false; + trace_startsolid = false; + trace_fraction = 1; + trace_inwater = false; + trace_inopen = true; + trace_endpos = e.move_origin; + trace_plane_normal = '0 0 1'; + trace_plane_dist = 0; + trace_ent = this; + + WITH(entity, self, e, e.move_touch()); + } + } + + other = oldother; +} + +void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict +{ + vector mi, ma; + if(this.solid == SOLID_BSP) + { + // TODO set the absolute bbox + mi = this.mins; + ma = this.maxs; + } + else + { + mi = this.mins; + ma = this.maxs; + } + mi += this.move_origin; + ma += this.move_origin; + + if(this.move_flags & FL_ITEM) + { + mi.x -= 15; + mi.y -= 15; + mi.z -= 1; + ma.x += 15; + ma.y += 15; + ma.z += 1; + } + else + { + mi.x -= 1; + mi.y -= 1; + mi.z -= 1; + ma.x += 1; + ma.y += 1; + ma.z += 1; + } + + this.absmin = mi; + this.absmax = ma; + + if(touch_triggers) + _Movetype_LinkEdict_TouchAreaGrid(this); +} + +bool _Movetype_TestEntityPosition(entity this, vector ofs) // SV_TestEntityPosition +{ +// vector org = this.move_origin + ofs; + + int cont = this.dphitcontentsmask; + this.dphitcontentsmask = DPCONTENTS_SOLID; + tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this); + this.dphitcontentsmask = cont; + + if(trace_startsolid) + return true; + + if(vlen(trace_endpos - this.move_origin) > 0.0001) + this.move_origin = trace_endpos; + return false; +} + +bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity +{ + if(!_Movetype_TestEntityPosition(this, '0 0 0')) return true; + if(!_Movetype_TestEntityPosition(this, '-1 0 0')) goto success; + if(!_Movetype_TestEntityPosition(this, '1 0 0')) goto success; + if(!_Movetype_TestEntityPosition(this, '0 -1 0')) goto success; + if(!_Movetype_TestEntityPosition(this, '0 1 0')) goto success; + if(!_Movetype_TestEntityPosition(this, '-1 -1 0')) goto success; + if(!_Movetype_TestEntityPosition(this, '1 -1 0')) goto success; + if(!_Movetype_TestEntityPosition(this, '-1 1 0')) goto success; + if(!_Movetype_TestEntityPosition(this, '1 1 0')) goto success; + for (int i = 1; i <= 17; ++i) + { + if(!_Movetype_TestEntityPosition(this, '0 0 -1' * i)) goto success; + if(!_Movetype_TestEntityPosition(this, '0 0 1' * i)) goto success; + } + LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", + etof(this), this.classname, vtos(this.move_origin)); + return false; + : success; + LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", + etof(this), this.classname, vtos(this.move_origin)); + _Movetype_LinkEdict(this, true); + return true; +} + +vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity +{ + vel -= ((vel * norm) * norm) * f; + + if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0; + if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0; + if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0; + + return vel; +} + +void _Movetype_PushEntityTrace(entity this, vector push) +{ + vector end = this.move_origin + push; + int type; + if(this.move_nomonsters) + type = max(0, this.move_nomonsters); + else if(this.move_movetype == MOVETYPE_FLYMISSILE) + type = MOVE_MISSILE; + else if(this.move_movetype == MOVETYPE_FLY_WORLDONLY) + type = MOVE_WORLDONLY; + else if(this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT) + type = MOVE_NOMONSTERS; + else + type = MOVE_NORMAL; + + tracebox(this.move_origin, this.mins, this.maxs, end, type, this); +} + +float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid) // SV_PushEntity +{ + _Movetype_PushEntityTrace(this, push); + + if(trace_startsolid && failonstartsolid) + return trace_fraction; + + this.move_origin = trace_endpos; + + if(trace_fraction < 1) + if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent))) + _Movetype_Impact(this, trace_ent); + + return trace_fraction; +} + + +.float ltime; +.void() blocked; +// matrix version of makevectors, sets v_forward, v_right and v_up +void makevectors_matrix(vector myangles) // AngleVectorsFLU +{ + v_forward = v_right = v_up = '0 0 0'; + + float y = myangles.y * (M_PI * 2 / 360); + float sy = sin(y); + float cy = cos(y); + float p = myangles.x * (M_PI * 2 / 360); + float sp = sin(p); + float cp = cos(p); + if(v_forward) + { + v_forward.x = cp * cy; + v_forward.y = cp * sy; + v_forward.z = -sp; + } + if(v_right || v_up) + { + if(myangles.z) + { + float r = myangles.z * (M_PI * 2 / 360); + float sr = sin(r); + float cr = cos(r); + if(v_right) + { + v_right.x = sr * sp * cy + cr * -sy; + v_right.y = sr * sp * sy + cr * cy; + v_right.z = sr * cp; + } + if(v_up) + { + v_up.x = cr * sp * cy + -sr * -sy; + v_up.y = cr * sp * sy + -sr * cy; + v_up.z = cr * cp; + } + } + else + { + if(v_right) + { + v_right.x = -sy; + v_right.y = cy; + v_right.z = 0; + } + if(v_up) + { + v_up.x = sp * cy; + v_up.y = sp * sy; + v_up.z = cp; + } + } + } +} + +void _Movetype_Physics_Frame(entity this, float movedt) +{ + this.move_didgravity = -1; + switch (this.move_movetype) + { + case MOVETYPE_PUSH: + case MOVETYPE_FAKEPUSH: + _Movetype_Physics_Pusher(this, movedt); + break; + case MOVETYPE_NONE: + break; + case MOVETYPE_FOLLOW: + _Movetype_Physics_Follow(this); + break; + case MOVETYPE_NOCLIP: + _Movetype_CheckWater(this); + this.move_origin = this.move_origin + TICRATE * this.move_velocity; + this.move_angles = this.move_angles + TICRATE * this.move_avelocity; + _Movetype_LinkEdict(this, false); + break; + case MOVETYPE_STEP: + _Movetype_Physics_Step(this, movedt); + break; + case MOVETYPE_WALK: + _Movetype_Physics_Walk(this, movedt); + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_BOUNCEMISSILE: + case MOVETYPE_FLYMISSILE: + case MOVETYPE_FLY: + case MOVETYPE_FLY_WORLDONLY: + _Movetype_Physics_Toss(this, movedt); + _Movetype_LinkEdict(this, true); + break; + case MOVETYPE_PHYSICS: + break; + } +} + +void _Movetype_Physics_ClientFrame(entity this, float movedt) +{ + this.move_didgravity = -1; + switch (this.move_movetype) + { + case MOVETYPE_PUSH: + case MOVETYPE_FAKEPUSH: + _Movetype_Physics_Pusher(this, movedt); + break; + case MOVETYPE_NONE: + break; + case MOVETYPE_FOLLOW: + _Movetype_Physics_Follow(this); + break; + case MOVETYPE_NOCLIP: + _Movetype_CheckWater(this); + this.move_origin = this.move_origin + TICRATE * this.move_velocity; + this.move_angles = this.move_angles + TICRATE * this.move_avelocity; + _Movetype_LinkEdict(this, false); + break; + case MOVETYPE_STEP: + _Movetype_Physics_Step(this, movedt); + break; + case MOVETYPE_WALK: + case MOVETYPE_FLY: + case MOVETYPE_FLY_WORLDONLY: + _Movetype_Physics_Walk(this, movedt); + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_BOUNCEMISSILE: + case MOVETYPE_FLYMISSILE: + _Movetype_Physics_Toss(this, movedt); + break; + case MOVETYPE_PHYSICS: + break; + } +} + +void Movetype_Physics_NoMatchServer(entity this) // optimized +{ + float movedt = time - this.move_time; + this.move_time = time; + + _Movetype_Physics_Frame(this, movedt); + if(wasfreed(this)) + return; + + this.avelocity = this.move_avelocity; + this.velocity = this.move_velocity; + this.angles = this.move_angles; + setorigin(this, this.move_origin); +} + +void Movetype_Physics_MatchServer(entity this, bool sloppy) +{ + Movetype_Physics_MatchTicrate(this, TICRATE, sloppy); +} + +void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Physics_Entity +{ + if(tr <= 0) + { + Movetype_Physics_NoMatchServer(this); + return; + } + + float dt = time - this.move_time; + + int n = max(0, floor(dt / tr)); + dt -= n * tr; + this.move_time += n * tr; + + if(!this.move_didgravity) + this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND)); + + for (int i = 0; i < n; ++i) + { + _Movetype_Physics_Frame(this, tr); + if(wasfreed(this)) + return; + } + + this.avelocity = this.move_avelocity; + + if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND)) + { + // now continue the move from move_time to time + this.velocity = this.move_velocity; + + if(this.move_didgravity > 0) + { + this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1) + * dt + * (this.gravity ? this.gravity : 1) + * PHYS_GRAVITY(this); + } + + this.angles = this.move_angles + dt * this.avelocity; + + if(sloppy || this.move_movetype == MOVETYPE_NOCLIP) + { + setorigin(this, this.move_origin + dt * this.velocity); + } + else + { + _Movetype_PushEntityTrace(this, dt * this.velocity); + if(!trace_startsolid) + setorigin(this, trace_endpos); + } + + if(this.move_didgravity > 0 && GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); + } + else + { + this.velocity = this.move_velocity; + this.angles = this.move_angles; + setorigin(this, this.move_origin); + } +} diff --git a/qcsrc/common/physics/movetypes/movetypes.qh b/qcsrc/common/physics/movetypes/movetypes.qh new file mode 100644 index 000000000..7578ae2fa --- /dev/null +++ b/qcsrc/common/physics/movetypes/movetypes.qh @@ -0,0 +1,90 @@ +#ifndef MOVETYPES_H +#define MOVETYPES_H + +.float move_ltime; +.void()move_think; +.float move_nextthink; +.void()move_blocked; + +.float move_movetype; +.float move_time; +.vector move_origin; +.vector move_angles; +.vector move_velocity; +.vector move_avelocity; +.int move_flags; +.int move_watertype; +.int move_waterlevel; +.void()move_touch; +.void(float, float)contentstransition; +.float move_bounce_factor; +.float move_bounce_stopspeed; +.float move_nomonsters; // -1 for MOVE_NORMAL, otherwise a MOVE_ constant + +.entity move_aiment; +.vector move_punchangle; + +// should match sv_gameplayfix_fixedcheckwatertransition +float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1; + +.entity move_groundentity; // FIXME add move_groundnetworkentity? +.float move_suspendedinair; +.float move_didgravity; + +void _Movetype_WallFriction(entity this, vector stepnormal); +int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight); +void _Movetype_CheckVelocity(entity this); +void _Movetype_CheckWaterTransition(entity ent); +float _Movetype_CheckWater(entity ent); +void _Movetype_LinkEdict_TouchAreaGrid(entity this); +void _Movetype_LinkEdict(entity this, float touch_triggers); +float _Movetype_TestEntityPosition(entity this, vector ofs); +float _Movetype_UnstickEntity(entity this); +vector _Movetype_ClipVelocity(vector vel, vector norm, float f); +void _Movetype_PushEntityTrace(entity this, vector push); +float _Movetype_PushEntity(entity this, vector push, float failonstartsolid); +void makevectors_matrix(vector myangles); + +void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy); +void Movetype_Physics_MatchServer(entity this, bool sloppy); +void Movetype_Physics_NoMatchServer(entity this); +void _Movetype_LinkEdict(entity this, float touch_triggers); +void _Movetype_LinkEdict_TouchAreaGrid(entity this); + +float _Movetype_UnstickEntity(entity this); + +const int MAX_CLIP_PLANES = 5; + +#ifdef CSQC +const int MOVETYPE_NONE = 0; +const int MOVETYPE_ANGLENOCLIP = 1; +const int MOVETYPE_ANGLECLIP = 2; +const int MOVETYPE_WALK = 3; +const int MOVETYPE_STEP = 4; +const int MOVETYPE_FLY = 5; +const int MOVETYPE_TOSS = 6; +const int MOVETYPE_PUSH = 7; +const int MOVETYPE_NOCLIP = 8; +const int MOVETYPE_FLYMISSILE = 9; +const int MOVETYPE_BOUNCE = 10; +const int MOVETYPE_BOUNCEMISSILE = 11; // Like bounce but doesn't lose speed on bouncing +const int MOVETYPE_FOLLOW = 12; +const int MOVETYPE_PHYSICS = 32; +const int MOVETYPE_FLY_WORLDONLY = 33; + +const int FL_ITEM = 256; +const int FL_ONGROUND = 512; +#endif + +const int MOVETYPE_FAKEPUSH = 13; + +const int MOVEFLAG_VALID = BIT(23); +const int MOVEFLAG_Q2AIRACCELERATE = BIT(0); +const int MOVEFLAG_NOGRAVITYONGROUND = BIT(1); +const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2); + +#ifdef CSQC +#define moveflags STAT(MOVEFLAGS) +#endif + +#endif diff --git a/qcsrc/common/physics/movetypes/push.qc b/qcsrc/common/physics/movetypes/push.qc new file mode 100644 index 000000000..b83246546 --- /dev/null +++ b/qcsrc/common/physics/movetypes/push.qc @@ -0,0 +1,157 @@ +void _Movetype_PushMove(entity this, float dt) // SV_PushMove +{ + if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0') + { + this.move_ltime += dt; + return; + } + + switch (this.solid) + { + // LordHavoc: valid pusher types + case SOLID_BSP: + case SOLID_BBOX: + case SOLID_SLIDEBOX: + case SOLID_CORPSE: // LordHavoc: this would be weird... + break; + // LordHavoc: no collisions + case SOLID_NOT: + case SOLID_TRIGGER: + this.move_origin = this.move_origin + dt * this.move_velocity; + this.move_angles = this.move_angles + dt * this.move_avelocity; + this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0)); + this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0)); + this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0)); + this.move_ltime += dt; + _Movetype_LinkEdict(this, true); + return; + default: + LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid); + return; + } + + bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0; + + vector move1 = this.move_velocity * dt; + vector moveangle = this.move_avelocity * dt; + + makevectors_matrix(-moveangle); + +// vector pushorig = this.move_origin; +// vector pushang = this.move_angles; +// float pushltime = this.move_ltime; + +// move the pusher to its final position + + this.move_origin = this.move_origin + dt * this.move_velocity; + this.move_angles = this.move_angles + dt * this.move_avelocity; + + this.move_ltime += dt; + _Movetype_LinkEdict(this, true); + + int savesolid = this.solid; + + if (this.move_movetype != MOVETYPE_FAKEPUSH) + { + for (entity check = findradius(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin)); check; check = check.chain) + { + switch (check.move_movetype) + { + case MOVETYPE_NONE: + case MOVETYPE_PUSH: + case MOVETYPE_FOLLOW: + case MOVETYPE_NOCLIP: + case MOVETYPE_FLY_WORLDONLY: + continue; + default: + break; + } + + if (check.owner == this) + continue; + + if (this.owner == check) + continue; + + vector pivot = check.mins + 0.5 * (check.maxs - check.mins); + vector move; + if (rotated) + { + vector org = (check.move_origin - this.move_origin) + pivot; + vector org2; + org2.x = org * v_forward; + org2.y = org * v_right; + org2.z = org * v_up; + move = (org2 - org) + move1; + } + else + { + move = move1; + } + + // physics objects need better collisions than this code can do + if (check.move_movetype == 32) // MOVETYPE_PHYSICS + { + check.move_origin = check.move_origin + move; + WITH(entity, this, check, _Movetype_LinkEdict(this, true)); + continue; + } + + // try moving the contacted entity + this.solid = SOLID_NOT; + bool flag = false; + WITH(entity, this, check, { + flag = _Movetype_PushEntity(this, move, true); + }); + if (!flag) + { + // entity "check" got teleported + check.move_angles_y += trace_fraction * moveangle.y; + this.solid = savesolid; + continue; // pushed enough + } + // FIXME: turn players specially + check.move_angles_y += trace_fraction * moveangle.y; + this.solid = savesolid; + + // this trace.fraction < 1 check causes items to fall off of pushers + // if they pass under or through a wall + // the groundentity check causes items to fall off of ledges + if (check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != this)) + check.move_flags &= ~FL_ONGROUND; + } + } + + this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0)); + this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0)); + this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0)); +} + +void _Movetype_Physics_Pusher(entity this, float dt) // SV_Physics_Pusher +{ + float oldltime = this.move_ltime; + float thinktime = this.move_nextthink; + float movetime; + if (thinktime < this.move_ltime + dt) + { + movetime = thinktime - this.move_ltime; + if (movetime < 0) + movetime = 0; + } + else + { + movetime = dt; + } + + if (movetime) + // advances this.move_ltime if not blocked + _Movetype_PushMove(this, movetime); + + if (thinktime > oldltime && thinktime <= this.move_ltime) + { + this.move_nextthink = 0; + this.move_time = time; + other = world; + WITH(entity, self, this, this.move_think()); + } +} diff --git a/qcsrc/common/physics/movetypes/push.qh b/qcsrc/common/physics/movetypes/push.qh new file mode 100644 index 000000000..d0c8493b7 --- /dev/null +++ b/qcsrc/common/physics/movetypes/push.qh @@ -0,0 +1,6 @@ +#ifndef MOVETYPE_PUSH_H +#define MOVETYPE_PUSH_H + +void _Movetype_Physics_Pusher(entity this, float dt); + +#endif diff --git a/qcsrc/common/physics/movetypes/step.qc b/qcsrc/common/physics/movetypes/step.qc new file mode 100644 index 000000000..d7a2d5627 --- /dev/null +++ b/qcsrc/common/physics/movetypes/step.qc @@ -0,0 +1,23 @@ +void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step +{ + if(this.move_flags & FL_ONGROUND) + { + if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this)) + { + this.move_flags &= ~FL_ONGROUND; + _Movetype_CheckVelocity(this); + _Movetype_FlyMove(this, dt, true, '0 0 0', 0); + _Movetype_LinkEdict(this, true); + } + } + else + { + _Movetype_CheckVelocity(this); + _Movetype_FlyMove(this, dt, true, '0 0 0', 0); + _Movetype_LinkEdict(this, true); + + // TODO? movetypesteplandevent + } + + _Movetype_CheckWaterTransition(this); +} diff --git a/qcsrc/common/physics/movetypes/toss.qc b/qcsrc/common/physics/movetypes/toss.qc new file mode 100644 index 000000000..43b5a8a1a --- /dev/null +++ b/qcsrc/common/physics/movetypes/toss.qc @@ -0,0 +1,115 @@ +#include "../physics.qh" + +void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss +{ + if (this.move_flags & FL_ONGROUND) + { + if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this)) + { + this.move_flags &= ~FL_ONGROUND; + } + else if (!this.move_groundentity) + { + return; + } + else if (this.move_suspendedinair && wasfreed(this.move_groundentity)) + { + this.move_groundentity = world; + return; + } + } + + this.move_suspendedinair = false; + + _Movetype_CheckVelocity(this); + + if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) + { + this.move_didgravity = 1; + this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1) + * dt + * (this.gravity ? this.gravity : 1) + * PHYS_GRAVITY(this); + } + + this.move_angles = this.move_angles + this.move_avelocity * dt; + + float movetime = dt; + for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump) + { + vector move = this.move_velocity * movetime; + _Movetype_PushEntity(this, move, true); + if (wasfreed(this)) + return; + + if (trace_startsolid) + { + _Movetype_UnstickEntity(this); + _Movetype_PushEntity(this, move, false); + if (wasfreed(this)) + return; + } + + if (trace_fraction == 1) + break; + + movetime *= 1 - min(1, trace_fraction); + + if (this.move_movetype == MOVETYPE_BOUNCEMISSILE) + { + this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0); + this.move_flags &= ~FL_ONGROUND; + } + else if (this.move_movetype == MOVETYPE_BOUNCE) + { + float bouncefac = this.move_bounce_factor; if (!bouncefac) bouncefac = 0.5; + float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800; + bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); + + this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac); + + float d = trace_plane_normal * this.move_velocity; + if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop) + { + this.move_flags |= FL_ONGROUND; + this.move_groundentity = trace_ent; + this.move_velocity = '0 0 0'; + this.move_avelocity = '0 0 0'; + } + else + { + this.move_flags &= ~FL_ONGROUND; + } + } + else + { + this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0); + if (trace_plane_normal.z > 0.7) + { + this.move_flags |= FL_ONGROUND; + this.move_groundentity = trace_ent; + if (trace_ent.solid == SOLID_BSP) + this.move_suspendedinair = true; + this.move_velocity = '0 0 0'; + this.move_avelocity = '0 0 0'; + } + else + { + this.move_flags &= ~FL_ONGROUND; + } + } + + // DP revision 8905 (just, WHY...) + if (this.move_movetype == MOVETYPE_BOUNCEMISSILE) + break; + + // DP revision 8918 (WHY...) + if (this.move_flags & FL_ONGROUND) + break; + } + + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND)) + this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); + + _Movetype_CheckWaterTransition(this); +} diff --git a/qcsrc/common/physics/movetypes/toss.qh b/qcsrc/common/physics/movetypes/toss.qh new file mode 100644 index 000000000..cf5cf49bb --- /dev/null +++ b/qcsrc/common/physics/movetypes/toss.qh @@ -0,0 +1,6 @@ +#ifndef MOVETYPE_TOSS_H +#define MOVETYPE_TOSS_H + +void _Movetype_Physics_Toss(entity this, float dt); + +#endif diff --git a/qcsrc/common/physics/movetypes/walk.qc b/qcsrc/common/physics/movetypes/walk.qc new file mode 100644 index 000000000..e92624626 --- /dev/null +++ b/qcsrc/common/physics/movetypes/walk.qc @@ -0,0 +1,176 @@ +void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove +{ + vector stepnormal = '0 0 0'; + + // if frametime is 0 (due to client sending the same timestamp twice), don't move + if (dt <= 0) + return; + + if (GAMEPLAYFIX_UNSTICKPLAYERS(this)) + _Movetype_UnstickEntity(this); + + bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP)); + + _Movetype_CheckVelocity(this); + + // do a regular slide move unless it looks like you ran into a step + bool oldonground = (this.move_flags & FL_ONGROUND); + + vector start_origin = this.move_origin; + vector start_velocity = this.move_velocity; + + int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0); + + if (GAMEPLAYFIX_DOWNTRACEONGROUND(this) && !(clip & 1)) + { + // only try this if there was no floor in the way in the trace (no, + // this check seems to be not REALLY necessary, because if clip & 1, + // our trace will hit that thing too) + vector upmove = this.move_origin + '0 0 1'; + vector downmove = this.move_origin - '0 0 1'; + int type; + if (this.move_movetype == MOVETYPE_FLYMISSILE) + type = MOVE_MISSILE; + else if (this.move_movetype == MOVETYPE_FLY_WORLDONLY) + type = MOVE_WORLDONLY; + else if (this.solid == SOLID_TRIGGER || this.solid == SOLID_NOT) + type = MOVE_NOMONSTERS; + else type = MOVE_NORMAL; + tracebox(upmove, this.mins, this.maxs, downmove, type, this); + if (trace_fraction < 1 && trace_plane_normal.z > 0.7) + clip |= 1; // but we HAVE found a floor + } + + // if the move did not hit the ground at any point, we're not on ground + if (!(clip & 1)) + this.move_flags &= ~FL_ONGROUND; + + _Movetype_CheckVelocity(this); + _Movetype_LinkEdict(this, true); + + if (clip & 8) // teleport + return; + + if (this.move_flags & FL_WATERJUMP) + return; + + if (PHYS_NOSTEP(this)) + return; + + vector originalmove_origin = this.move_origin; + vector originalmove_velocity = this.move_velocity; + // originalmove_clip = clip; + int originalmove_flags = this.move_flags; + entity originalmove_groundentity = this.move_groundentity; + + // if move didn't block on a step, return + if (clip & 2) + { + // if move was not trying to move into the step, return + if (fabs(start_velocity.x) < 0.03125 && fabs(start_velocity.y) < 0.03125) + return; + + if (this.move_movetype != MOVETYPE_FLY) + { + // return if gibbed by a trigger + if (this.move_movetype != MOVETYPE_WALK) + return; + + // return if attempting to jump while airborn (unless sv_jumpstep) + if (!PHYS_JUMPSTEP(this)) + if (!oldonground && this.move_waterlevel == 0) + return; + } + + // try moving up and forward to go up a step + // back to start pos + this.move_origin = start_origin; + this.move_velocity = start_velocity; + + // move up + vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this); + _Movetype_PushEntity(this, upmove, true); + if(wasfreed(this)) + return; + if(trace_startsolid) + { + // we got teleported when upstepping... must abort the move + return; + } + + // move forward + this.move_velocity_z = 0; + clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0); + this.move_velocity_z += start_velocity.z; + if (clip & 8) + { + // we got teleported when upstepping... must abort the move + // note that z velocity handling may not be what QC expects here, but we cannot help it + return; + } + + _Movetype_CheckVelocity(this); + _Movetype_LinkEdict(this, true); + + // check for stuckness, possibly due to the limited precision of floats + // in the clipping hulls + if (clip + && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125 + && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125) + { + // Con_Printf("wall\n"); + // stepping up didn't make any progress, revert to original move + this.move_origin = originalmove_origin; + this.move_velocity = originalmove_velocity; + // clip = originalmove_clip; + this.move_flags = originalmove_flags; + this.move_groundentity = originalmove_groundentity; + // now try to unstick if needed + // clip = SV_TryUnstick (ent, oldvel); + return; + } + + // Con_Printf("step - "); + + // extra friction based on view angle + if ((clip & 2) && PHYS_WALLFRICTION(this)) + _Movetype_WallFriction(this, stepnormal); + } + // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground + else if (!GAMEPLAYFIX_STEPDOWN(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND)) + { + return; + } + + // move down + vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT(this) + start_velocity.z * dt); + _Movetype_PushEntity(this, downmove, true); + if(wasfreed(this)) + return; + + if(trace_startsolid) + { + // we got teleported when downstepping... must abort the move + return; + } + + if (trace_fraction < 1 && trace_plane_normal.z > 0.7) + { + // this has been disabled so that you can't jump when you are stepping + // up while already jumping (also known as the Quake2 double jump bug) + } + else + { + // Con_Printf("slope\n"); + // if the push down didn't end up on good ground, use the move without + // the step up. This happens near wall / slope combinations, and can + // cause the player to hop up higher on a slope too steep to climb + this.move_origin = originalmove_origin; + this.move_velocity = originalmove_velocity; + this.move_flags = originalmove_flags; + this.move_groundentity = originalmove_groundentity; + } + + _Movetype_CheckVelocity(this); + _Movetype_LinkEdict(this, true); +} diff --git a/qcsrc/common/physics/movetypes/walk.qh b/qcsrc/common/physics/movetypes/walk.qh new file mode 100644 index 000000000..a920c7a4f --- /dev/null +++ b/qcsrc/common/physics/movetypes/walk.qh @@ -0,0 +1,6 @@ +#ifndef MOVETYPE_WALK_H +#define MOVETYPE_WALK_H + +void _Movetype_Physics_Walk(entity this, float dt); + +#endif diff --git a/qcsrc/common/physics/player.qc b/qcsrc/common/physics/player.qc new file mode 100644 index 000000000..32c2fbfa8 --- /dev/null +++ b/qcsrc/common/physics/player.qc @@ -0,0 +1,1545 @@ +#include "physics.qh" +#include "triggers/include.qh" +#include "viewloc.qh" + +#ifdef SVQC + +#include "../server/miscfunctions.qh" +#include "triggers/trigger/viewloc.qh" + +// client side physics +bool Physics_Valid(string thecvar) +{ + return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar); +} + +float Physics_ClientOption(entity this, string option) +{ + if(Physics_Valid(this.cvar_cl_physics)) + { + string s = sprintf("g_physics_%s_%s", this.cvar_cl_physics, option); + if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS) + return cvar(s); + } + if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default) + { + string s = sprintf("g_physics_%s_%s", autocvar_g_physics_clientselect_default, option); + if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS) + return cvar(s); + } + return cvar(strcat("sv_", option)); +} + +void Physics_UpdateStats(entity this, float maxspd_mod) +{ + STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod); + STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw")) + ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod) + : 0; + STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod; + STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking + + // old stats + // fix some new settings + STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor"); + STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed"); + STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed"); + STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate"); + STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel"); + STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction"); + STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol"); + STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power"); + STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty"); + STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel"); + STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed"); + STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel"); + STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio"); + STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction"); + STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate"); + STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed"); + STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate"); + STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate"); + STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity"); + STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump"); +} +#endif + +float IsMoveInDirection(vector mv, float ang) // key mix factor +{ + if (mv_x == 0 && mv_y == 0) + return 0; // avoid division by zero + ang -= RAD2DEG * atan2(mv_y, mv_x); + ang = remainder(ang, 360) / 45; + return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang); +} + +float GeomLerp(float a, float lerp, float b) +{ + return a == 0 ? (lerp < 1 ? 0 : b) + : b == 0 ? (lerp > 0 ? 0 : a) + : a * pow(fabs(b / a), lerp); +} + +#define unstick_offsets(X) \ +/* 1 no nudge (just return the original if this test passes) */ \ + X(' 0.000 0.000 0.000') \ +/* 6 simple nudges */ \ + X(' 0.000 0.000 0.125') X('0.000 0.000 -0.125') \ + X('-0.125 0.000 0.000') X('0.125 0.000 0.000') \ + X(' 0.000 -0.125 0.000') X('0.000 0.125 0.000') \ +/* 4 diagonal flat nudges */ \ + X('-0.125 -0.125 0.000') X('0.125 -0.125 0.000') \ + X('-0.125 0.125 0.000') X('0.125 0.125 0.000') \ +/* 8 diagonal upward nudges */ \ + X('-0.125 0.000 0.125') X('0.125 0.000 0.125') \ + X(' 0.000 -0.125 0.125') X('0.000 0.125 0.125') \ + X('-0.125 -0.125 0.125') X('0.125 -0.125 0.125') \ + X('-0.125 0.125 0.125') X('0.125 0.125 0.125') \ +/* 8 diagonal downward nudges */ \ + X('-0.125 0.000 -0.125') X('0.125 0.000 -0.125') \ + X(' 0.000 -0.125 -0.125') X('0.000 0.125 -0.125') \ + X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \ + X('-0.125 0.125 -0.125') X('0.125 0.125 -0.125') \ +/**/ + +void PM_ClientMovement_Unstick(entity this) +{ + #define X(unstick_offset) \ + { \ + vector neworigin = unstick_offset + this.origin; \ + tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, this); \ + if (!trace_startsolid) \ + { \ + setorigin(this, neworigin); \ + return; \ + } \ + } + unstick_offsets(X); + #undef X +} + +void PM_ClientMovement_UpdateStatus(entity this, bool ground) +{ +#ifdef CSQC + if(!IS_PLAYER(this)) + return; + // make sure player is not stuck + if(autocvar_cl_movement == 3) + PM_ClientMovement_Unstick(this); + + // set crouched + if (PHYS_INPUT_BUTTON_CROUCH(this)) + { + // wants to crouch, this always works + if (!IS_DUCKED(this)) SET_DUCKED(this); + } + else + { + // wants to stand, if currently crouching we need to check for a low ceiling first + if (IS_DUCKED(this)) + { + tracebox(this.origin, PL_MIN, PL_MAX, this.origin, MOVE_NORMAL, this); + if (!trace_startsolid) UNSET_DUCKED(this); + } + } + + // set onground + vector origin1 = this.origin + '0 0 1'; + vector origin2 = this.origin - '0 0 1'; + + if (ground && autocvar_cl_movement == 3) + { + tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this); + if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7) + { + SET_ONGROUND(this); + + // this code actually "predicts" an impact; so let's clip velocity first + this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal; + } + else + UNSET_ONGROUND(this); + } + + if(autocvar_cl_movement == 3) + { + // set watertype/waterlevel + origin1 = this.origin; + origin1.z += this.mins_z + 1; + this.waterlevel = WATERLEVEL_NONE; + + int thepoint = pointcontents(origin1); + + this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME); + + if (this.watertype) + { + this.waterlevel = WATERLEVEL_WETFEET; + origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5; + thepoint = pointcontents(origin1); + if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) + { + this.waterlevel = WATERLEVEL_SWIMMING; + origin1.z = this.origin.z + 22; + thepoint = pointcontents(origin1); + if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) + this.waterlevel = WATERLEVEL_SUBMERGED; + } + } + } + + if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0) + pmove_waterjumptime = 0; +#endif +} + +void PM_ClientMovement_Move(entity this) +{ +#ifdef CSQC + + PM_ClientMovement_UpdateStatus(this, false); + if(autocvar_cl_movement == 1) + return; + + int bump; + float t; + float f; + vector neworigin; + vector currentorigin2; + vector neworigin2; + vector primalvelocity; + + vector trace1_endpos = '0 0 0'; + vector trace2_endpos = '0 0 0'; + vector trace3_endpos = '0 0 0'; + float trace1_fraction = 0; + float trace2_fraction = 0; + float trace3_fraction = 0; + vector trace1_plane_normal = '0 0 0'; + vector trace2_plane_normal = '0 0 0'; + vector trace3_plane_normal = '0 0 0'; + + primalvelocity = this.velocity; + for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++) + { + neworigin = this.origin + t * this.velocity; + tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this); + trace1_endpos = trace_endpos; + trace1_fraction = trace_fraction; + trace1_plane_normal = trace_plane_normal; + if(trace1_fraction < 1 && trace1_plane_normal_z == 0) + { + // may be a step or wall, try stepping up + // first move forward at a higher level + currentorigin2 = this.origin; + currentorigin2_z += PHYS_STEPHEIGHT(this); + neworigin2 = neworigin; + neworigin2_z += PHYS_STEPHEIGHT(this); + tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this); + trace2_endpos = trace_endpos; + trace2_fraction = trace_fraction; + trace2_plane_normal = trace_plane_normal; + if(!trace_startsolid) + { + // then move down from there + currentorigin2 = trace2_endpos; + neworigin2 = trace2_endpos; + neworigin2_z = this.origin_z; + tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this); + trace3_endpos = trace_endpos; + trace3_fraction = trace_fraction; + trace3_plane_normal = trace_plane_normal; + // accept the new trace if it made some progress + if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125) + { + trace1_endpos = trace2_endpos; + trace1_fraction = trace2_fraction; + trace1_plane_normal = trace2_plane_normal; + trace1_endpos = trace3_endpos; + } + } + } + + // check if it moved at all + if(trace1_fraction >= 0.001) + setorigin(this, trace1_endpos); + + // check if it moved all the way + if(trace1_fraction == 1) + break; + + // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate + // I'm pretty sure I commented it out solely because it seemed redundant + // this got commented out in a change that supposedly makes the code match QW better + // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block + if(trace1_plane_normal_z > 0.7) + SET_ONGROUND(this); + + t -= t * trace1_fraction; + + f = (this.velocity * trace1_plane_normal); + this.velocity = this.velocity + -f * trace1_plane_normal; + } + if(PHYS_TELEPORT_TIME(this) > 0) + this.velocity = primalvelocity; +#endif +} + +void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed) +{ + float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1); + if (k <= 0) + return; + + k *= bound(0, wishspeed / PHYS_MAXAIRSPEED(this), 1); + + float zspeed = this.velocity_z; + this.velocity_z = 0; + float xyspeed = vlen(this.velocity); + this.velocity = normalize(this.velocity); + + float dot = this.velocity * wishdir; + + if (dot > 0) // we can't change direction while slowing down + { + k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH; + xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32); + k *= PHYS_AIRCONTROL(this); + this.velocity = normalize(this.velocity * xyspeed + wishdir * k); + } + + this.velocity = this.velocity * xyspeed; + this.velocity_z = zspeed; +} + +float AdjustAirAccelQW(float accelqw, float factor) +{ + return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw); +} + +// example config for alternate speed clamping: +// sv_airaccel_qw 0.8 +// sv_airaccel_sideways_friction 0 +// prvm_globalset server speedclamp_mode 1 +// (or 2) +void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit) +{ + float speedclamp = stretchfactor > 0 ? stretchfactor + : accelqw < 0 ? 1 // full clamping, no stretch + : -1; // no clamping + + accelqw = fabs(accelqw); + + if (GAMEPLAYFIX_Q2AIRACCELERATE) + wishspeed0 = wishspeed; // don't need to emulate this Q1 bug + + float vel_straight = this.velocity * wishdir; + float vel_z = this.velocity_z; + vector vel_xy = vec2(this.velocity); + vector vel_perpend = vel_xy - vel_straight * wishdir; + + float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0; + + float vel_xy_current = vlen(vel_xy); + if (speedlimit) + accelqw = AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed)); + float vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); + float vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw); + vel_xy_backward = max(0, vel_xy_backward); // not that it REALLY occurs that this would cause wrong behaviour afterwards + vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw); + + if (sidefric < 0 && (vel_perpend*vel_perpend)) + // negative: only apply so much sideways friction to stay below the speed you could get by "braking" + { + float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); + float themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend); + // assume: themin > 1 + // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend + // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend + // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy + // obviously, this cannot be + if (themin <= 0) + vel_perpend *= f; + else + { + themin = sqrt(themin); + vel_perpend *= max(themin, f); + } + } + else + vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); + + vel_xy = vel_straight * wishdir + vel_perpend; + + if (speedclamp >= 0) + { + float vel_xy_preclamp; + vel_xy_preclamp = vlen(vel_xy); + if (vel_xy_preclamp > 0) // prevent division by zero + { + vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp; + if (vel_xy_current < vel_xy_preclamp) + vel_xy *= (vel_xy_current / vel_xy_preclamp); + } + } + + this.velocity = vel_xy + vel_z * '0 0 1'; +} + +void PM_AirAccelerate(entity this, vector wishdir, float wishspeed) +{ + if (wishspeed == 0) + return; + + vector curvel = this.velocity; + curvel_z = 0; + float curspeed = vlen(curvel); + + if (wishspeed > curspeed * 1.01) + wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH); + else + { + float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this))); + wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH; + } + vector wishvel = wishdir * wishspeed; + vector acceldir = wishvel - curvel; + float addspeed = vlen(acceldir); + acceldir = normalize(acceldir); + + float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH); + + if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1) + { + vector curdir = normalize(curvel); + float dot = acceldir * curdir; + if (dot < 0) + acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this)) * dot * curdir; + } + + this.velocity += accelspeed * acceldir; +} + + +/* +============= +PlayerJump + +When you press the jump key +returns true if handled +============= +*/ +bool PlayerJump(entity this) +{ + if (PHYS_FROZEN(this)) + return true; // no jumping in freezetag when frozen + +#ifdef SVQC + if (this.player_blocked) + return true; // no jumping while blocked +#endif + + bool doublejump = false; + float mjumpheight = PHYS_JUMPVELOCITY(this); + + if (MUTATOR_CALLHOOK(PlayerJump, this, doublejump, mjumpheight)) + return true; + + doublejump = player_multijump; + mjumpheight = player_jumpheight; + + if (this.waterlevel >= WATERLEVEL_SWIMMING) + { + if(this.viewloc) + { + doublejump = true; + mjumpheight *= 0.7; + } + else + { + this.velocity_z = PHYS_MAXSPEED(this) * 0.7; + return true; + } + } + + if (!doublejump) + if (!IS_ONGROUND(this)) + return IS_JUMP_HELD(this); + + bool track_jump = PHYS_CL_TRACK_CANJUMP(this); + if(PHYS_TRACK_CANJUMP(this)) + track_jump = true; + + if (track_jump) + if (IS_JUMP_HELD(this)) + return true; + + // sv_jumpspeedcap_min/sv_jumpspeedcap_max act as baseline + // velocity bounds. Final velocity is bound between (jumpheight * + // min + jumpheight) and (jumpheight * max + jumpheight); + + if(PHYS_JUMPSPEEDCAP_MIN != "") + { + float minjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MIN); + + if (this.velocity_z < minjumpspeed) + mjumpheight += minjumpspeed - this.velocity_z; + } + + if(PHYS_JUMPSPEEDCAP_MAX != "") + { + // don't do jump speedcaps on ramps to preserve old xonotic ramjump style + tracebox(this.origin + '0 0 0.01', this.mins, this.maxs, this.origin - '0 0 0.01', MOVE_NORMAL, this); + + if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(this))) + { + float maxjumpspeed = mjumpheight * stof(PHYS_JUMPSPEEDCAP_MAX); + + if (this.velocity_z > maxjumpspeed) + mjumpheight -= this.velocity_z - maxjumpspeed; + } + } + + 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) + { + float f = (1 - PHYS_FRICTION_ONLAND(this)); + this.velocity_x *= f; + this.velocity_y *= f; + } +#ifdef SVQC + if(this.jumppadcount > 1) + LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n")); + this.jumppadcount = 0; +#endif + } + + this.velocity_z += mjumpheight; + + UNSET_ONGROUND(this); + SET_JUMP_HELD(this); + +#ifdef SVQC + + this.oldvelocity_z = this.velocity_z; + + animdecide_setaction(this, ANIMACTION_JUMP, true); + + if (autocvar_g_jump_grunt) + PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); +#endif + return true; +} + +void CheckWaterJump(entity this) +{ +// check for a jump-out-of-water + makevectors(this.v_angle); + vector start = this.origin; + start_z += 8; + v_forward_z = 0; + normalize(v_forward); + vector end = start + v_forward*24; + traceline (start, end, true, this); + if (trace_fraction < 1) + { // solid at waist + start_z = start_z + this.maxs_z - 8; + end = start + v_forward*24; + this.movedir = trace_plane_normal * -50; + traceline(start, end, true, this); + if (trace_fraction == 1) + { // open at eye level + this.velocity_z = 225; + this.flags |= FL_WATERJUMP; + SET_JUMP_HELD(this); + #ifdef SVQC + PHYS_TELEPORT_TIME(this) = time + 2; // safety net + #elif defined(CSQC) + pmove_waterjumptime = time + 2; + #endif + } + } +} + + +#ifdef SVQC + #define JETPACK_JUMP(s) s.cvar_cl_jetpack_jump +#elif defined(CSQC) + float autocvar_cl_jetpack_jump; + #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump +#endif +.float jetpack_stopped; +void CheckPlayerJump(entity this) +{ +#ifdef SVQC + float was_flying = ITEMS_STAT(this) & IT_USING_JETPACK; +#endif + if (JETPACK_JUMP(this) < 2) + ITEMS_STAT(this) &= ~IT_USING_JETPACK; + + if(PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this)) + { + float air_jump = !PlayerJump(this) || player_multijump; // PlayerJump() has important side effects + float activate = JETPACK_JUMP(this) && air_jump && PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_JETPACK(this); + float has_fuel = !PHYS_JETPACK_FUEL(this) || PHYS_AMMO_FUEL(this) || ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO; + + if (!(ITEMS_STAT(this) & ITEM_Jetpack.m_itemid)) { } + else if (this.jetpack_stopped) { } + else if (!has_fuel) + { +#ifdef SVQC + if (was_flying) // TODO: ran out of fuel message + Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL); + else if (activate) + Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_JETPACK_NOFUEL); +#endif + this.jetpack_stopped = true; + ITEMS_STAT(this) &= ~IT_USING_JETPACK; + } + else if (activate && !PHYS_FROZEN(this)) + ITEMS_STAT(this) |= IT_USING_JETPACK; + } + else + { + this.jetpack_stopped = false; + ITEMS_STAT(this) &= ~IT_USING_JETPACK; + } + if (!PHYS_INPUT_BUTTON_JUMP(this)) + UNSET_JUMP_HELD(this); + + if (this.waterlevel == WATERLEVEL_SWIMMING) + CheckWaterJump(this); +} + +float racecar_angle(float forward, float down) +{ + if (forward < 0) + { + forward = -forward; + down = -down; + } + + float ret = vectoyaw('0 1 0' * down + '1 0 0' * forward); + + float angle_mult = forward / (800 + forward); + + if (ret > 180) + return ret * angle_mult + 360 * (1 - angle_mult); + else + return ret * angle_mult; +} + +string specialcommand = "xwxwxsxsxaxdxaxdx1x "; +.float specialcommand_pos; +void SpecialCommand() +{ +#ifdef SVQC + if (!CheatImpulse(CHIMPULSE_GIVE_ALL.impulse)) + LOG_INFO("A hollow voice says \"Plugh\".\n"); +#endif +} + +float PM_check_specialcommand(entity this, float buttons) +{ +#ifdef SVQC + string c; + if (!buttons) + c = "x"; + else if (buttons == 1) + c = "1"; + else if (buttons == 2) + c = " "; + else if (buttons == 128) + c = "s"; + else if (buttons == 256) + c = "w"; + else if (buttons == 512) + c = "a"; + else if (buttons == 1024) + c = "d"; + else + c = "?"; + + if (c == substring(specialcommand, this.specialcommand_pos, 1)) + { + this.specialcommand_pos += 1; + if (this.specialcommand_pos >= strlen(specialcommand)) + { + this.specialcommand_pos = 0; + SpecialCommand(); + return true; + } + } + else if (this.specialcommand_pos && (c != substring(specialcommand, this.specialcommand_pos - 1, 1))) + this.specialcommand_pos = 0; +#endif + return false; +} + +void PM_check_nickspam(entity this) +{ +#ifdef SVQC + if (time >= this.nickspamtime) + return; + if (this.nickspamcount >= autocvar_g_nick_flood_penalty_yellow) + { + // slight annoyance for nick change scripts + this.movement = -1 * this.movement; + this.BUTTON_ATCK = this.BUTTON_JUMP = this.BUTTON_ATCK2 = this.BUTTON_ZOOM = this.BUTTON_CROUCH = this.BUTTON_HOOK = this.BUTTON_USE = 0; + + if (this.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you! + { + this.v_angle_x = random() * 360; + this.v_angle_y = random() * 360; + // at least I'm not forcing retardedview by also assigning to angles_z + this.fixangle = true; + } + } +#endif +} + +void PM_check_punch(entity this) +{ +#ifdef SVQC + if (this.punchangle != '0 0 0') + { + float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH; + if (f > 0) + this.punchangle = normalize(this.punchangle) * f; + else + this.punchangle = '0 0 0'; + } + + if (this.punchvector != '0 0 0') + { + float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH; + if (f > 0) + this.punchvector = normalize(this.punchvector) * f; + else + this.punchvector = '0 0 0'; + } +#endif +} + +// predict frozen movement, as frozen players CAN move in some cases +void PM_check_frozen(entity this) +{ + if (!PHYS_FROZEN(this)) + return; + if (PHYS_DODGING_FROZEN(this) +#ifdef SVQC + && IS_REAL_CLIENT(this) +#endif + ) + { + this.movement_x = bound(-5, this.movement.x, 5); + this.movement_y = bound(-5, this.movement.y, 5); + this.movement_z = bound(-5, this.movement.z, 5); + } + else + this.movement = '0 0 0'; + + vector midpoint = ((this.absmin + this.absmax) * 0.5); + if (pointcontents(midpoint) == CONTENT_WATER) + { + this.velocity = this.velocity * 0.5; + + if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER) + this.velocity_z = 200; + } +} + +void PM_check_hitground(entity this) +{ +#ifdef SVQC + if (!this.wasFlying) return; + this.wasFlying = false; + if (this.waterlevel >= WATERLEVEL_SWIMMING) return; + if (time < this.ladder_time) return; + if (this.hook) return; + this.nextstep = time + 0.3 + random() * 0.1; + trace_dphitq3surfaceflags = 0; + tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this); + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return; + entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) + ? GS_FALL_METAL + : GS_FALL; + GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND); +#endif +} + +void PM_Footsteps(entity this) +{ +#ifdef SVQC + if (!g_footsteps) return; + if (IS_DUCKED(this)) return; + if (time >= this.lastground + 0.2) return; + if (vdist(this.velocity, <=, autocvar_sv_maxspeed * 0.6)) return; + if ((time > this.nextstep) || (time < (this.nextstep - 10.0))) + { + this.nextstep = time + 0.3 + random() * 0.1; + trace_dphitq3surfaceflags = 0; + tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this); + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) return; + entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) + ? GS_STEP_METAL + : GS_STEP; + GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND); + } +#endif +} + +void PM_check_blocked(entity this) +{ +#ifdef SVQC + if (!this.player_blocked) + return; + this.movement = '0 0 0'; + this.disableclientprediction = 1; +#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)) +#endif + PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0); + PM_ClientMovement_Move(this); +} + +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; + #ifdef CSQC + pmove_waterjumptime = 2; + #endif + UNSET_ONGROUND(this); + SET_JUMP_HELD(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 = -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 (IS_DUCKED(this)) + wishspeed *= 0.5; + +// 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 + 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); + PM_ClientMovement_Move(this); + } +} + +.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); + if(time >= PHYS_TELEPORT_TIME(this)) + // water acceleration + PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0); + PM_ClientMovement_Move(this); +} + +void PM_jetpack(entity this, float maxspd_mod) +{ + //makevectors(this.v_angle.y * '0 1 0'); + makevectors(this.v_angle); + vector wishvel = v_forward * this.movement_x + + v_right * this.movement_y; + // add remaining speed as Z component + float maxairspd = PHYS_MAXAIRSPEED(this) * max(1, maxspd_mod); + // fix speedhacks :P + wishvel = normalize(wishvel) * min(1, vlen(wishvel) / maxairspd); + // add the unused velocity as up component + wishvel_z = 0; + + // if (this.BUTTON_JUMP) + wishvel_z = sqrt(max(0, 1 - wishvel * wishvel)); + + // it is now normalized, so... + float a_side = PHYS_JETPACK_ACCEL_SIDE(this); + float a_up = PHYS_JETPACK_ACCEL_UP(this); + float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this); + + wishvel_x *= a_side; + wishvel_y *= a_side; + wishvel_z *= a_up; + wishvel_z += a_add; + + float best = 0; + ////////////////////////////////////////////////////////////////////////////////////// + // finding the maximum over all vectors of above form + // with wishvel having an absolute value of 1 + ////////////////////////////////////////////////////////////////////////////////////// + // we're finding the maximum over + // f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2; + // for z in the range from -1 to 1 + ////////////////////////////////////////////////////////////////////////////////////// + // maximum is EITHER attained at the single extreme point: + float a_diff = a_side * a_side - a_up * a_up; + float f; + if (a_diff != 0) + { + f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z) + if (f > -1 && f < 1) // can it be attained? + { + best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff; + //print("middle\n"); + } + } + // OR attained at z = 1: + f = (a_up + a_add) * (a_up + a_add); + if (f > best) + { + best = f; + //print("top\n"); + } + // OR attained at z = -1: + f = (a_up - a_add) * (a_up - a_add); + if (f > best) + { + best = f; + //print("bottom\n"); + } + best = sqrt(best); + ////////////////////////////////////////////////////////////////////////////////////// + + //print("best possible acceleration: ", ftos(best), "\n"); + + float fxy, fz; + fxy = bound(0, 1 - (this.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE(this), 1); + if (wishvel_z - PHYS_GRAVITY(this) > 0) + fz = bound(0, 1 - this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1); + else + fz = bound(0, 1 + this.velocity_z / PHYS_JETPACK_MAXSPEED_UP(this), 1); + + float fvel; + fvel = vlen(wishvel); + wishvel_x *= fxy; + wishvel_y *= fxy; + wishvel_z = (wishvel_z - PHYS_GRAVITY(this)) * fz + PHYS_GRAVITY(this); + + fvel = min(1, vlen(wishvel) / best); + if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) + f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel)); + else + f = 1; + + //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n"); + + if (f > 0 && wishvel != '0 0 0') + { + this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH; + UNSET_ONGROUND(this); + +#ifdef SVQC + if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) + this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f; + + ITEMS_STAT(this) |= IT_USING_JETPACK; + + // jetpack also inhibits health regeneration, but only for 1 second + this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen); +#endif + } + +#ifdef CSQC + float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; + if(autocvar_cl_movement == 3) + { + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + this.velocity_z -= g * 0.5; + else + this.velocity_z -= g; + } + PM_ClientMovement_Move(this); + if(autocvar_cl_movement == 3) + { + if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + this.velocity_z -= g * 0.5; + } +#endif +} + +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; + } +#ifdef CSQC + float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; + if(autocvar_cl_movement == 3) + { + if (!(GAMEPLAYFIX_NOGRAVITYONGROUND)) + this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1); + } + if (vdist(this.velocity, >, 0)) + PM_ClientMovement_Move(this); + if(autocvar_cl_movement == 3) + { + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND) + this.velocity_z -= g * 0.5; + } +#endif +} + +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 + PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(this)); + + if (PHYS_AIRCONTROL(this)) + CPM_PM_Aircontrol(this, wishdir, wishspeed2); + } +#ifdef CSQC + float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; + if(autocvar_cl_movement == 3) + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + this.velocity_z -= g * 0.5; + else + this.velocity_z -= g; +#endif + PM_ClientMovement_Move(this); +#ifdef CSQC + if(autocvar_cl_movement == 3) + if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + this.velocity_z -= g * 0.5; +#endif +} + +// used for calculating airshots +bool IsFlying(entity this) +{ + if(IS_ONGROUND(this)) + return false; + if(this.waterlevel >= WATERLEVEL_SWIMMING) + return false; + traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this); + if(trace_fraction < 1) + return false; + 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); + + vector oldv_angle = this.v_angle; + vector oldangles = this.angles; // we need to save these, as they're abused by other code + this.v_angle = PHYS_INPUT_ANGLES(this); + this.angles = PHYS_WORLD_ANGLES(this); + + 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, true); +#endif + + this.oldmovement = this.movement; + + +#ifdef SVQC + WarpZone_PlayerPhysics_FixVAngle(); +#endif + float maxspeed_mod = 1; + maxspeed_mod *= PHYS_HIGHSPEED(this); + +#ifdef SVQC + Physics_UpdateStats(this, maxspeed_mod); + + if (this.PlayerPhysplug) + if (this.PlayerPhysplug()) + return; +#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; + WITH(entity, self, this, bot_think()); + } +#endif + +#ifdef SVQC + if (IS_PLAYER(this)) + { + 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; + } + } +#endif + +#ifdef SVQC + if (this.movetype == MOVETYPE_NONE) + return; + + // when we get here, disableclientprediction cannot be 2 + this.disableclientprediction = 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 != "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(PHYS_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); + } + + if(IsFlying(this)) + this.wasFlying = 1; + + if (IS_PLAYER(this)) + CheckPlayerJump(this); + + if (this.flags & FL_WATERJUMP) + { + this.velocity_x = this.movedir.x; + this.velocity_y = this.movedir.y; + if (time > PHYS_TELEPORT_TIME(this) || this.waterlevel == WATERLEVEL_NONE) + { + this.flags &= ~FL_WATERJUMP; + PHYS_TELEPORT_TIME(this) = 0; + } + } + + else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod)) + { } + +#ifdef SVQC + else if (this.movetype == MOVETYPE_NOCLIP || this.movetype == MOVETYPE_FLY || this.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); + +: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; + +#ifdef CSQC + this.v_angle = oldv_angle; + this.angles = oldangles; +#endif +} + +#if defined(SVQC) +void SV_PlayerPhysics() +#elif defined(CSQC) +void CSQC_ClientMovement_PlayerMove_Frame(entity this) +#endif +{ +#ifdef SVQC + SELFPARAM(); +#endif + PM_Main(this); +} diff --git a/qcsrc/common/physics/player.qh b/qcsrc/common/physics/player.qh new file mode 100644 index 000000000..66824bbcf --- /dev/null +++ b/qcsrc/common/physics/player.qh @@ -0,0 +1,310 @@ +#ifndef COMMON_PHYSICS_H +#define COMMON_PHYSICS_H + +// Client/server mappings + +.entity conveyor; + +.float race_penalty; + +.float gravity; +.float swamp_slowdown; +.float lastflags; +.float lastground; +.float wasFlying; +#ifdef SVQC +.float spectatorspeed = _STAT(SPECTATORSPEED); +#elif defined(CSQC) +.float spectatorspeed; +#endif + +.vector movement_old; +.float buttons_old; +.vector v_angle_old; +.string lastclassname; + +.float() PlayerPhysplug; +float AdjustAirAccelQW(float accelqw, float factor); + +bool IsFlying(entity a); + +#define BUFFS_STAT(s) STAT(BUFFS, s) + +#define GAMEPLAYFIX_DOWNTRACEONGROUND(s) STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, s) +#define GAMEPLAYFIX_EASIERWATERJUMP(s) STAT(GAMEPLAYFIX_EASIERWATERJUMP, s) +#define GAMEPLAYFIX_STEPDOWN(s) STAT(GAMEPLAYFIX_STEPDOWN, s) +#define GAMEPLAYFIX_STEPMULTIPLETIMES(s) STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, s) +#define GAMEPLAYFIX_UNSTICKPLAYERS(s) STAT(GAMEPLAYFIX_UNSTICKPLAYERS, s) + +#define PHYS_ACCELERATE(s) STAT(MOVEVARS_ACCELERATE, s) +#define PHYS_AIRACCELERATE(s) STAT(MOVEVARS_AIRACCELERATE, s) +#define PHYS_AIRACCEL_QW(s) STAT(MOVEVARS_AIRACCEL_QW, s) +#define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, s) +#define PHYS_AIRACCEL_SIDEWAYS_FRICTION(s) STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, s) +#define PHYS_AIRCONTROL(s) STAT(MOVEVARS_AIRCONTROL, s) +#define PHYS_AIRCONTROL_PENALTY(s) STAT(MOVEVARS_AIRCONTROL_PENALTY, s) +#define PHYS_AIRCONTROL_POWER(s) STAT(MOVEVARS_AIRCONTROL_POWER, s) +#define PHYS_AIRSPEEDLIMIT_NONQW(s) STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, s) +#define PHYS_AIRSTOPACCELERATE(s) STAT(MOVEVARS_AIRSTOPACCELERATE, s) +#define PHYS_AIRSTRAFEACCELERATE(s) STAT(MOVEVARS_AIRSTRAFEACCELERATE, s) +#define PHYS_AIRSTRAFEACCEL_QW(s) STAT(MOVEVARS_AIRSTRAFEACCEL_QW, s) + +#define PHYS_AMMO_FUEL(s) STAT(FUEL, s) + +#define PHYS_DODGING_FROZEN(s) STAT(DODGING_FROZEN, s) + +#define PHYS_FRICTION(s) STAT(MOVEVARS_FRICTION, s) +#define PHYS_FRICTION_ONLAND(s) STAT(MOVEVARS_FRICTION_ONLAND, s) +#define PHYS_FRICTION_SLICK(s) STAT(MOVEVARS_FRICTION_SLICK, s) + +#define PHYS_FROZEN(s) STAT(FROZEN, s) + +#define PHYS_HIGHSPEED(s) STAT(MOVEVARS_HIGHSPEED, s) + +#define PHYS_JETPACK_ACCEL_SIDE(s) STAT(JETPACK_ACCEL_SIDE, s) +#define PHYS_JETPACK_ACCEL_UP(s) STAT(JETPACK_ACCEL_UP, s) +#define PHYS_JETPACK_ANTIGRAVITY(s) STAT(JETPACK_ANTIGRAVITY, s) +#define PHYS_JETPACK_FUEL(s) STAT(JETPACK_FUEL, s) +#define PHYS_JETPACK_MAXSPEED_SIDE(s) STAT(JETPACK_MAXSPEED_SIDE, s) +#define PHYS_JETPACK_MAXSPEED_UP(s) STAT(JETPACK_MAXSPEED_UP, s) + +#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, s) +#define PHYS_JUMPSTEP(s) STAT(MOVEVARS_JUMPSTEP, s) +#define PHYS_JUMPVELOCITY(s) STAT(MOVEVARS_JUMPVELOCITY, s) + +#define PHYS_MAXAIRSPEED(s) STAT(MOVEVARS_MAXAIRSPEED, s) +#define PHYS_MAXAIRSTRAFESPEED(s) STAT(MOVEVARS_MAXAIRSTRAFESPEED, s) +#define PHYS_MAXSPEED(s) STAT(MOVEVARS_MAXSPEED, s) + +#define PHYS_NOSTEP(s) STAT(NOSTEP, s) +#define PHYS_STEPHEIGHT(s) STAT(MOVEVARS_STEPHEIGHT, s) + +#define PHYS_STOPSPEED(s) STAT(MOVEVARS_STOPSPEED, s) + +#define PHYS_TRACK_CANJUMP(s) STAT(MOVEVARS_TRACK_CANJUMP, s) + +#define PHYS_WALLFRICTION(s) STAT(MOVEVARS_WALLFRICTION, s) + +#define PHYS_WARSOWBUNNY_ACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_ACCEL, s) +#define PHYS_WARSOWBUNNY_AIRFORWARDACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, s) +#define PHYS_WARSOWBUNNY_BACKTOSIDERATIO(s) STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, s) +#define PHYS_WARSOWBUNNY_TOPSPEED(s) STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, s) +#define PHYS_WARSOWBUNNY_TURNACCEL(s) STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, s) + +#define UPWARD_VELOCITY_CLEARS_ONGROUND(s) STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, s) + +#define PHYS_INPUT_BUTTON_ATCK(s) PHYS_INPUT_BUTTON_BUTTON1(s) +#define PHYS_INPUT_BUTTON_JUMP(s) PHYS_INPUT_BUTTON_BUTTON2(s) +#define PHYS_INPUT_BUTTON_ATCK2(s) PHYS_INPUT_BUTTON_BUTTON3(s) +#define PHYS_INPUT_BUTTON_ZOOM(s) PHYS_INPUT_BUTTON_BUTTON4(s) +#define PHYS_INPUT_BUTTON_CROUCH(s) PHYS_INPUT_BUTTON_BUTTON5(s) +#define PHYS_INPUT_BUTTON_HOOK(s) PHYS_INPUT_BUTTON_BUTTON6(s) +#define PHYS_INPUT_BUTTON_INFO(s) PHYS_INPUT_BUTTON_BUTTON7(s) +#define PHYS_INPUT_BUTTON_DRAG(s) PHYS_INPUT_BUTTON_BUTTON8(s) +#define PHYS_INPUT_BUTTON_USE(s) PHYS_INPUT_BUTTON_BUTTON_USE(s) +#define PHYS_INPUT_BUTTON_CHAT(s) PHYS_INPUT_BUTTON_BUTTON_CHAT(s) +#define PHYS_INPUT_BUTTON_PRYDON(s) PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) +#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s) PHYS_INPUT_BUTTON_BUTTON9(s) +#define PHYS_INPUT_BUTTON_JETPACK(s) PHYS_INPUT_BUTTON_BUTTON10(s) + +// if more buttons are needed, start using impulse bits as buttons + +#define PHYS_INPUT_BUTTON_BACKWARD(s) (PHYS_INPUT_MOVEVALUES(s).x < 0) +#define PHYS_INPUT_BUTTON_FORWARD(s) (PHYS_INPUT_MOVEVALUES(s).x > 0) +#define PHYS_INPUT_BUTTON_LEFT(s) (PHYS_INPUT_MOVEVALUES(s).y < 0) +#define PHYS_INPUT_BUTTON_RIGHT(s) (PHYS_INPUT_MOVEVALUES(s).y > 0) + +// used for special commands and idle checking, not from the engine +// TODO: cache +#define PHYS_INPUT_BUTTON_MASK(s) ( \ + (1 << 0) * PHYS_INPUT_BUTTON_ATCK(s) \ + | (1 << 1) * PHYS_INPUT_BUTTON_JUMP(s) \ + | (1 << 2) * PHYS_INPUT_BUTTON_ATCK2(s) \ + | (1 << 3) * PHYS_INPUT_BUTTON_ZOOM(s) \ + | (1 << 4) * PHYS_INPUT_BUTTON_CROUCH(s) \ + | (1 << 5) * PHYS_INPUT_BUTTON_HOOK(s) \ + | (1 << 6) * PHYS_INPUT_BUTTON_USE(s) \ + | (1 << 7) * PHYS_INPUT_BUTTON_BACKWARD(s) \ + | (1 << 8) * PHYS_INPUT_BUTTON_FORWARD(s) \ + | (1 << 9) * PHYS_INPUT_BUTTON_LEFT(s) \ + | (1 << 10) * PHYS_INPUT_BUTTON_RIGHT(s) \ + ) + +#define IS_JUMP_HELD(s) (!((s).flags & FL_JUMPRELEASED)) +#define SET_JUMP_HELD(s) ((s).flags &= ~FL_JUMPRELEASED) +#define UNSET_JUMP_HELD(s) ((s).flags |= FL_JUMPRELEASED) + +#define IS_ONGROUND(s) boolean((s).flags & FL_ONGROUND) +#define SET_ONGROUND(s) ((s).flags |= FL_ONGROUND) +#define UNSET_ONGROUND(s) ((s).flags &= ~FL_ONGROUND) + +#define WAS_ONGROUND(s) boolean((s).lastflags & FL_ONGROUND) + +#define ITEMS_STAT(s) ((s).items) + +#ifdef CSQC + + 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 + + .float watertype; + .float waterlevel; + .int items; + + .vector movement; + .vector v_angle; + +// TODO + #define IS_CLIENT(s) ((s).isplayermodel) + #define IS_PLAYER(s) ((s).isplayermodel) + #define IS_NOT_A_CLIENT(s) (!(s).isplayermodel) + #define isPushable(s) ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE)) + + //float player_multijump; + //float player_jumpheight; + + #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 +// TODO + #define PHYS_WORLD_ANGLES(s) input_angles + + #define PHYS_INPUT_TIMELENGTH input_timelength + #define PHYS_INPUT_FRAMETIME serverdeltatime + + #define PHYS_INPUT_MOVEVALUES(s) input_movevalues + + #define PHYS_INPUT_BUTTON_BUTTON1(s) boolean(input_buttons & BIT(0)) + #define PHYS_INPUT_BUTTON_BUTTON2(s) boolean(input_buttons & BIT(1)) + #define PHYS_INPUT_BUTTON_BUTTON3(s) boolean(input_buttons & BIT(2)) + #define PHYS_INPUT_BUTTON_BUTTON4(s) boolean(input_buttons & BIT(3)) + #define PHYS_INPUT_BUTTON_BUTTON5(s) boolean(input_buttons & BIT(4)) + #define PHYS_INPUT_BUTTON_BUTTON6(s) boolean(input_buttons & BIT(5)) + #define PHYS_INPUT_BUTTON_BUTTON7(s) boolean(input_buttons & BIT(6)) + #define PHYS_INPUT_BUTTON_BUTTON8(s) boolean(input_buttons & BIT(7)) + #define PHYS_INPUT_BUTTON_BUTTON_USE(s) boolean(input_buttons & BIT(8)) + #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) boolean(input_buttons & BIT(9)) + #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) boolean(input_buttons & BIT(10)) + #define PHYS_INPUT_BUTTON_BUTTON9(s) boolean(input_buttons & BIT(11)) + #define PHYS_INPUT_BUTTON_BUTTON10(s) boolean(input_buttons & BIT(12)) + #define PHYS_INPUT_BUTTON_BUTTON11(s) boolean(input_buttons & BIT(13)) + #define PHYS_INPUT_BUTTON_BUTTON12(s) boolean(input_buttons & BIT(14)) + #define PHYS_INPUT_BUTTON_BUTTON13(s) boolean(input_buttons & BIT(15)) + #define PHYS_INPUT_BUTTON_BUTTON14(s) boolean(input_buttons & BIT(16)) + #define PHYS_INPUT_BUTTON_BUTTON15(s) boolean(input_buttons & BIT(17)) + #define PHYS_INPUT_BUTTON_BUTTON16(s) boolean(input_buttons & BIT(18)) + + #define PHYS_DEAD(s) ((s).csqcmodel_isdead) + + #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE (boolean(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)) + #define GAMEPLAYFIX_NOGRAVITYONGROUND (boolean(moveflags & MOVEFLAG_NOGRAVITYONGROUND)) + #define GAMEPLAYFIX_Q2AIRACCELERATE (boolean(moveflags & MOVEFLAG_Q2AIRACCELERATE)) + + #define IS_DUCKED(s) (boolean((s).flags & FL_DUCKED)) + #define SET_DUCKED(s) ((s).flags |= FL_DUCKED) + #define UNSET_DUCKED(s) ((s).flags &= ~FL_DUCKED) + + #define PHYS_JUMPSPEEDCAP_MIN autocvar_cl_jumpspeedcap_min + #define PHYS_JUMPSPEEDCAP_MAX autocvar_cl_jumpspeedcap_max + + #define PHYS_CL_TRACK_CANJUMP(s) STAT(MOVEVARS_CL_TRACK_CANJUMP, s) + // FIXME: 0 doesn't mean zero gravity + #define PHYS_ENTGRAVITY(s) STAT(MOVEVARS_ENTGRAVITY, s) + +#elif defined(SVQC) + + bool Physics_Valid(string thecvar); + + .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW); + .float stat_sv_maxspeed = _STAT(MOVEVARS_MAXSPEED); + + /** Not real stats */ + .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 + + #define PHYS_INPUT_ANGLES(s) ((s).v_angle) + #define PHYS_WORLD_ANGLES(s) ((s).angles) + + #define PHYS_INPUT_TIMELENGTH frametime + #define PHYS_INPUT_FRAMETIME sys_frametime + + #define PHYS_INPUT_MOVEVALUES(s) ((s).movement) + + #define PHYS_INPUT_BUTTON_BUTTON1(s) ((s).button0) + #define PHYS_INPUT_BUTTON_BUTTON2(s) ((s).button2) + #define PHYS_INPUT_BUTTON_BUTTON3(s) ((s).button3) + #define PHYS_INPUT_BUTTON_BUTTON4(s) ((s).button4) + #define PHYS_INPUT_BUTTON_BUTTON5(s) ((s).button5) + #define PHYS_INPUT_BUTTON_BUTTON6(s) ((s).button6) + #define PHYS_INPUT_BUTTON_BUTTON7(s) ((s).button7) + #define PHYS_INPUT_BUTTON_BUTTON8(s) ((s).button8) + #define PHYS_INPUT_BUTTON_BUTTON_USE(s) ((s).buttonuse) + #define PHYS_INPUT_BUTTON_BUTTON_CHAT(s) ((s).buttonchat) + #define PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) ((s).cursor_active) + #define PHYS_INPUT_BUTTON_BUTTON9(s) ((s).button9) + #define PHYS_INPUT_BUTTON_BUTTON10(s) ((s).button10) + #define PHYS_INPUT_BUTTON_BUTTON11(s) ((s).button11) + #define PHYS_INPUT_BUTTON_BUTTON12(s) ((s).button12) + #define PHYS_INPUT_BUTTON_BUTTON13(s) ((s).button13) + #define PHYS_INPUT_BUTTON_BUTTON14(s) ((s).button14) + #define PHYS_INPUT_BUTTON_BUTTON15(s) ((s).button15) + #define PHYS_INPUT_BUTTON_BUTTON16(s) ((s).button16) + + #define PHYS_DEAD(s) ((s).deadflag != DEAD_NO) + + #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate + #define GAMEPLAYFIX_NOGRAVITYONGROUND autocvar_sv_gameplayfix_nogravityonground + #define GAMEPLAYFIX_Q2AIRACCELERATE autocvar_sv_gameplayfix_q2airaccelerate + + #define IS_DUCKED(s) ((s).crouch) + #define SET_DUCKED(s) ((s).crouch = true) + #define UNSET_DUCKED(s) ((s).crouch = false) + + #define PHYS_JUMPSPEEDCAP_MIN autocvar_sv_jumpspeedcap_min + #define PHYS_JUMPSPEEDCAP_MAX autocvar_sv_jumpspeedcap_max + + #define PHYS_CL_TRACK_CANJUMP(s) ((s).cvar_cl_movement_track_canjump) + #define PHYS_ENTGRAVITY(s) ((s).gravity) + +#endif + +REGISTER_NET_C2S(setpause) +#ifdef CSQC +void unpause_update() +{ + static bool waspaused; + bool ispaused = PHYS_INPUT_BUTTON_CHAT(this); + if (ispaused == waspaused) return; + waspaused = ispaused; + // if (!serverispaused) return; // TODO: find out somehow + if (ispaused) return; // ignore setting pause, server will get those presses anyway, but it won't get releases + int channel = MSG_C2S; + WriteHeader(channel, setpause); + WriteByte(channel, ispaused); +} +#endif +#ifdef SVQC +NET_HANDLE(setpause, bool) +{ + bool ispaused = boolean(ReadByte()); + PHYS_INPUT_BUTTON_CHAT(sender) = ispaused; + return true; +} +#endif + +#endif diff --git a/qcsrc/server/movelib.qc b/qcsrc/server/movelib.qc deleted file mode 100644 index acacba093..000000000 --- a/qcsrc/server/movelib.qc +++ /dev/null @@ -1,237 +0,0 @@ -#include "movelib.qh" - -#ifdef SVQC -.vector moveto; - -/** - Simulate drag - self.velocity = movelib_dragvec(self.velocity,0.02,0.5); -**/ -vector movelib_dragvec(float drag, float exp_) -{SELFPARAM(); - float lspeed,ldrag; - - lspeed = vlen(self.velocity); - ldrag = lspeed * drag; - ldrag = ldrag * (drag * exp_); - ldrag = 1 - (ldrag / lspeed); - - return self.velocity * ldrag; -} - -/** - Simulate drag - self.velocity *= movelib_dragflt(somespeed,0.01,0.7); -**/ -float movelib_dragflt(float fspeed,float drag,float exp_) -{ - float ldrag; - - ldrag = fspeed * drag; - ldrag = ldrag * ldrag * exp_; - ldrag = 1 - (ldrag / fspeed); - - return ldrag; -} - -/** - Do a inertia simulation based on velocity. - Basicaly, this allows you to simulate loss of steering with higher speed. - self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9); -**/ -vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax) -{SELFPARAM(); - float influense; - - influense = vlen(self.velocity) * (1 / vel_max); - - influense = bound(newmin,influense,oldmax); - - return (vel_new * (1 - influense)) + (self.velocity * influense); -} - -vector movelib_inertmove(vector new_vel,float new_bias) -{SELFPARAM(); - return new_vel * new_bias + self.velocity * (1-new_bias); -} - -void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce) -{SELFPARAM(); - float deltatime; - float acceleration; - float mspeed; - vector breakvec; - - deltatime = time - self.movelib_lastupdate; - if (deltatime > 0.15) deltatime = 0; - self.movelib_lastupdate = time; - if (!deltatime) return; - - mspeed = vlen(self.velocity); - - if (theMass) - acceleration = vlen(force) / theMass; - else - acceleration = vlen(force); - - if (self.flags & FL_ONGROUND) - { - if (breakforce) - { - breakvec = (normalize(self.velocity) * (breakforce / theMass) * deltatime); - self.velocity = self.velocity - breakvec; - } - - self.velocity = self.velocity + force * (acceleration * deltatime); - } - - if (drag) - self.velocity = movelib_dragvec(drag, 1); - - if (self.waterlevel > 1) - { - self.velocity = self.velocity + force * (acceleration * deltatime); - self.velocity = self.velocity + '0 0 0.05' * autocvar_sv_gravity * deltatime; - } - else - self.velocity = self.velocity + '0 0 -1' * autocvar_sv_gravity * deltatime; - - mspeed = vlen(self.velocity); - - if (max_velocity) - if (mspeed > max_velocity) - self.velocity = normalize(self.velocity) * (mspeed - 50);//* max_velocity; -} - -/* -.float mass; -.float side_friction; -.float ground_friction; -.float air_friction; -.float water_friction; -.float buoyancy; -float movelib_deltatime; - -void movelib_startupdate() -{ - movelib_deltatime = time - self.movelib_lastupdate; - - if (movelib_deltatime > 0.5) - movelib_deltatime = 0; - - self.movelib_lastupdate = time; -} - -void movelib_update(vector dir,float force) -{ - vector acceleration; - float old_speed; - float ffriction,v_z; - - vector breakvec; - vector old_dir; - vector ggravity; - vector old; - - if(!movelib_deltatime) - return; - v_z = self.velocity_z; - old_speed = vlen(self.velocity); - old_dir = normalize(self.velocity); - - //ggravity = (autocvar_sv_gravity / self.mass) * '0 0 100'; - acceleration = (force / self.mass) * dir; - //acceleration -= old_dir * (old_speed / self.mass); - acceleration -= ggravity; - - if(self.waterlevel > 1) - { - ffriction = self.water_friction; - acceleration += self.buoyancy * '0 0 1'; - } - else - if(self.flags & FL_ONGROUND) - ffriction = self.ground_friction; - else - ffriction = self.air_friction; - - acceleration *= ffriction; - //self.velocity = self.velocity * (ffriction * movelib_deltatime); - self.velocity += acceleration * movelib_deltatime; - self.velocity_z = v_z; - -} -*/ - -void movelib_beak_simple(float force) -{SELFPARAM(); - float mspeed; - vector mdir; - float vz; - - mspeed = max(0,vlen(self.velocity) - force); - mdir = normalize(self.velocity); - vz = self.velocity.z; - self.velocity = mdir * mspeed; - self.velocity_z = vz; -} - -/** -Pitches and rolls the entity to match the gound. -Yed need to set v_up and v_forward (generally by calling makevectors) before calling this. -**/ -#endif - -void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max) -{SELFPARAM(); - vector a, b, c, d, e, r, push_angle, ahead, side; - - push_angle.y = 0; - r = (self.absmax + self.absmin) * 0.5 + (v_up * spring_up); - e = v_up * spring_length; - - // Put springs slightly inside bbox - ahead = v_forward * (self.maxs.x * 0.8); - side = v_right * (self.maxs.y * 0.8); - - a = r + ahead + side; - b = r + ahead - side; - c = r - ahead + side; - d = r - ahead - side; - - traceline(a, a - e,MOVE_NORMAL,self); - a.z = (1 - trace_fraction); - r = trace_endpos; - - traceline(b, b - e,MOVE_NORMAL,self); - b.z = (1 - trace_fraction); - r += trace_endpos; - - traceline(c, c - e,MOVE_NORMAL,self); - c.z = (1 - trace_fraction); - r += trace_endpos; - - traceline(d, d - e,MOVE_NORMAL,self); - d.z = (1 - trace_fraction); - r += trace_endpos; - - a.x = r.z; - r = self.origin; - r.z = r.z; - - push_angle.x = (a.z - c.z) * _max; - push_angle.x += (b.z - d.z) * _max; - - push_angle.z = (b.z - a.z) * _max; - push_angle.z += (d.z - c.z) * _max; - - //self.angles_x += push_angle_x * 0.95; - //self.angles_z += push_angle_z * 0.95; - - self.angles_x = ((1-blendrate) * self.angles.x) + (push_angle.x * blendrate); - self.angles_z = ((1-blendrate) * self.angles.z) + (push_angle.z * blendrate); - - //a = self.origin; - setorigin(self,r); -} - diff --git a/qcsrc/server/movelib.qh b/qcsrc/server/movelib.qh deleted file mode 100644 index 8a4bfd488..000000000 --- a/qcsrc/server/movelib.qh +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef MOVELIB_H -#define MOVELIB_H - -#ifdef SVQC -.vector moveto; - -/** - Simulate drag - self.velocity = movelib_dragvec(self.velocity,0.02,0.5); -**/ -vector movelib_dragvec(float drag, float exp_); - -/** - Simulate drag - self.velocity *= movelib_dragflt(somespeed,0.01,0.7); -**/ -float movelib_dragflt(float fspeed,float drag,float exp_); - -/** - Do a inertia simulation based on velocity. - Basicaly, this allows you to simulate loss of steering with higher speed. - self.velocity = movelib_inertmove_byspeed(self.velocity,newvel,1000,0.1,0.9); -**/ -vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax); - -vector movelib_inertmove(vector new_vel,float new_bias); - -.float movelib_lastupdate; -void movelib_move(vector force,float max_velocity,float drag,float theMass,float breakforce); - -/* -void movelib_move_simple(vector newdir,float velo,float blendrate) -{ - self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo; -} -*/ -#define movelib_move_simple(newdir,velo,blendrate) \ - self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo - -#define movelib_move_simple_gravity(newdir,velo,blendrate) \ - if(self.flags & FL_ONGROUND) movelib_move_simple(newdir,velo,blendrate) - -void movelib_beak_simple(float force); - -/** -Pitches and rolls the entity to match the gound. -Yed need to set v_up and v_forward (generally by calling makevectors) before calling this. -**/ -#endif - -void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max); - -#endif diff --git a/qcsrc/server/progs.inc b/qcsrc/server/progs.inc index cde4b68f9..f0c13be64 100644 --- a/qcsrc/server/progs.inc +++ b/qcsrc/server/progs.inc @@ -20,7 +20,6 @@ #include "item_key.qc" #include "mapvoting.qc" #include "miscfunctions.qc" -#include "movelib.qc" #include "playerdemo.qc" #include "portals.qc" #include "race.qc"