]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/physics/movetypes/movetypes.qc
Some minor tweaks to the QC physics logic
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / physics / movetypes / movetypes.qc
index 7894d14fd856bb1a8e09a4401fd88259ee940320..2634b8246bedc6e0eb165b772a11f341d6364924 100644 (file)
@@ -4,12 +4,14 @@
 void set_movetype(entity this, int mt)
 {
        this.move_movetype = mt;
-       if (mt == MOVETYPE_PHYSICS || mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH) {
+       if (mt == MOVETYPE_PHYSICS) {
                this.move_qcphysics = false;
+       } else if (autocvar_sv_qcphysics == 2) {
+               this.move_qcphysics = true;
        }
        if(!IL_CONTAINS(g_moveables, this))
                IL_PUSH(g_moveables, this); // add it to the moveable entities list (even if it doesn't move!) logic: if an object never sets its movetype, we assume it never does anything notable
-       this.movetype = (this.move_qcphysics) ? MOVETYPE_NONE : mt;
+       this.movetype = (this.move_qcphysics) ? MOVETYPE_QCENTITY : mt;
 }
 #elif defined(CSQC)
 void set_movetype(entity this, int mt)
@@ -18,6 +20,89 @@ void set_movetype(entity this, int mt)
 }
 #endif
 
+bool _Movetype_NudgeOutOfSolid_PivotIsKnownGood(entity this, vector pivot) // SV_NudgeOutOfSolid_PivotIsKnownGood
+{
+       vector stuckorigin = this.origin;
+       vector goodmins = pivot, goodmaxs = pivot;
+       for(int bump = 0; bump < 6; bump++)
+       {
+               int coord = 2 - (bump >> 1);
+               int dir = (bump & 1);
+
+               for(int subbump = 0; ; ++subbump)
+               {
+                       vector testorigin = stuckorigin;
+                       if(dir)
+                       {
+                               // pushing maxs
+                               switch(coord)
+                               {
+                                       case 0: testorigin.x += this.maxs_x - goodmaxs.x; break;
+                                       case 1: testorigin.y += this.maxs_y - goodmaxs.y; break;
+                                       case 2: testorigin.z += this.maxs_z - goodmaxs.z; break;
+                               }
+                       }
+                       else
+                       {
+                               // pushing mins
+                               switch(coord)
+                               {
+                                       case 0: testorigin.x += this.mins_x - goodmins.x; break;
+                                       case 1: testorigin.y += this.mins_y - goodmins.y; break;
+                                       case 2: testorigin.z += this.mins_z - goodmins.z; break;
+                               }
+                       }
+
+                       tracebox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, this);
+                       if(trace_startsolid && trace_ent.solid == SOLID_BSP) // NOTE: this checks for bmodelstartsolid in the engine
+                       {
+                               // BAD BAD, can't fix that
+                               return false;
+                       }
+
+                       if(trace_fraction >= 1)
+                               break; // it WORKS!
+
+                       if(subbump >= 10)
+                       {
+                               // BAD BAD, can't fix that
+                               return false;
+                       }
+
+                       // we hit something... let's move out of it
+                       vector move = trace_endpos - testorigin;
+                       float nudge = (trace_plane_normal * move) + 0.03125; // FIXME cvar this constant
+                       stuckorigin = stuckorigin + nudge * trace_plane_normal;
+               }
+
+               if(dir)
+               {
+                       // pushing maxs
+                       switch(coord)
+                       {
+                               case 0: goodmaxs.x = this.maxs_x; break;
+                               case 1: goodmaxs.y = this.maxs_y; break;
+                               case 2: goodmaxs.z = this.maxs_z; break;
+                       }
+               }
+               else
+               {
+                       // pushing mins
+                       switch(coord)
+                       {
+                               case 0: goodmins.x = this.mins_x; break;
+                               case 1: goodmins.y = this.mins_y; break;
+                               case 2: goodmins.z = this.mins_z; break;
+                       }
+               }
+       }
+
+       // WE WIN
+       this.origin = stuckorigin;
+
+       return true;
+}
+
 void _Movetype_WallFriction(entity this, vector stepnormal)  // SV_WallFriction
 {
        /*float d, i;
@@ -43,15 +128,15 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
        if(dt <= 0)
                return 0;
 
-       int blocked = 0;
-       int i, j, numplanes = 0;
+       int blockedflag = 0;
+       int numplanes = 0;
        float time_left = dt, grav = 0;
        vector push;
        vector primal_velocity, original_velocity;
        vector restore_velocity = this.velocity;
 
-       for(i = 0; i < MAX_CLIP_PLANES; ++i)
-               planes[i] = '0 0 0';
+       for(int j = 0; j < MAX_CLIP_PLANES; ++j)
+               planes[j] = '0 0 0';
 
        if(applygravity)
        {
@@ -75,11 +160,11 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                        break;
 
                push = this.velocity * time_left;
-               if(!_Movetype_PushEntity(this, push, true, false))
+               if(!_Movetype_PushEntity(this, push, false))
                {
                        // we got teleported by a touch function
                        // let's abort the move
-                       blocked |= 8;
+                       blockedflag |= 8;
                        break;
                }
 
@@ -94,6 +179,8 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                if(trace_fraction == 1)
                        break;
 
+               time_left *= 1 - trace_fraction;
+
                float my_trace_fraction = trace_fraction;
                vector my_trace_plane_normal = trace_plane_normal;
 
@@ -102,7 +189,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                        if(trace_plane_normal.z > 0.7)
                        {
                                // floor
-                               blocked |= 1;
+                               blockedflag |= 1;
 
                                if(!trace_ent)
                                {
@@ -118,28 +205,30 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                {
                        // step - handle it immediately
                        vector org = this.origin;
-                       vector steppush = '0 0 1' * stepheight;
+                       vector steppush = vec3(0, 0, stepheight);
+                       push = this.velocity * time_left;
 
-                       if(!_Movetype_PushEntity(this, steppush, true, false))
+                       if(!_Movetype_PushEntity(this, steppush, false))
                        {
-                               blocked |= 8;
+                               blockedflag |= 8;
                                break;
                        }
-                       if(!_Movetype_PushEntity(this, push, true, false))
+                       if(!_Movetype_PushEntity(this, push, false))
                        {
-                               blocked |= 8;
+                               blockedflag |= 8;
                                break;
                        }
                        float trace2_fraction = trace_fraction;
                        steppush = vec3(0, 0, org.z - this.origin_z);
-                       if(!_Movetype_PushEntity(this, steppush, true, false))
+                       if(!_Movetype_PushEntity(this, steppush, false))
                        {
-                               blocked |= 8;
+                               blockedflag |= 8;
                                break;
                        }
 
                        // accept the new position if it made some progress...
-                       if(fabs(this.origin_x - org.x) >= 0.03125 || fabs(this.origin_y - org.y) >= 0.03125)
+                       // previously this checked if absolute distance >= 0.03125 which made stepping up unreliable
+                       if(this.origin_x - org.x || this.origin_y - org.y)
                        {
                                trace_endpos = this.origin;
                                time_left *= 1 - trace2_fraction;
@@ -152,7 +241,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                else
                {
                        // step - return it to caller
-                       blocked |= 2;
+                       blockedflag |= 2;
                        // save the trace for player extrafriction
                        if(applystepnormal)
                                move_stepnormal = trace_plane_normal;
@@ -165,14 +254,12 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                        numplanes = 0;
                }
 
-               time_left *= 1 - my_trace_fraction;
-
                // clipped to another plane
                if(numplanes >= MAX_CLIP_PLANES)
                {
                        // this shouldn't really happen
                        this.velocity = '0 0 0';
-                       blocked = 3;
+                       blockedflag = 3;
                        break;
                }
 
@@ -181,23 +268,25 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
 
                // modify original_velocity so it parallels all of the clip planes
                vector new_velocity = '0 0 0';
-               for (i = 0;i < numplanes;i++)
+               int plane;
+               for (plane = 0;plane < numplanes;plane++)
                {
-                       new_velocity = _Movetype_ClipVelocity(original_velocity, planes[i], 1);
-                       for (j = 0;j < numplanes;j++)
+                       int newplane;
+                       new_velocity = _Movetype_ClipVelocity(original_velocity, planes[plane], 1);
+                       for (newplane = 0;newplane < numplanes;newplane++)
                        {
-                               if(j != i)
+                               if(newplane != plane)
                                {
                                        // not ok
-                                       if((new_velocity * planes[j]) < 0)
+                                       if((new_velocity * planes[newplane]) < 0)
                                                break;
                                }
                        }
-                       if(j == numplanes)
+                       if(newplane == numplanes)
                                break;
                }
 
-               if(i != numplanes)
+               if(plane != numplanes)
                {
                        // go along this plane
                        this.velocity = new_velocity;
@@ -208,17 +297,12 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                        if(numplanes != 2)
                        {
                                this.velocity = '0 0 0';
-                               blocked = 7;
+                               blockedflag = 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;
+                       dir = normalize(dir);
                        float d = (dir * this.velocity);
                        this.velocity = dir * d;
                }
@@ -233,10 +317,10 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
        }
 
        // LordHavoc: this came from QW and allows you to get out of water more easily
-       if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.flags & FL_WATERJUMP) && !(blocked & 8))
+       if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.flags & FL_WATERJUMP) && !(blockedflag & 8))
                this.velocity = primal_velocity;
 
-       if(PHYS_WALLCLIP(this) && this.pm_time && !(this.flags & FL_WATERJUMP) && !(blocked & 8))
+       if(PHYS_WALLCLIP(this) && this.pm_time && !(this.flags & FL_WATERJUMP) && !(blockedflag & 8))
                this.velocity = primal_velocity;
 
        if(applygravity)
@@ -248,7 +332,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepno
                }
        }
 
-       return blocked;
+       return blockedflag;
 }
 
 void _Movetype_CheckVelocity(entity this)  // SV_CheckVelocity
@@ -329,11 +413,40 @@ void _Movetype_Impact(entity this, entity oth)  // SV_Impact
        if(!this && !oth)
                return;
 
+       // due to a lack of pointers in QC, we must save the trace values and restore them for other functions
+       bool save_trace_allsolid = trace_allsolid;
+       bool save_trace_startsolid = trace_startsolid;
+       float save_trace_fraction = trace_fraction;
+       bool save_trace_inwater = trace_inwater;
+       bool save_trace_inopen = trace_inopen;
+       vector save_trace_endpos = trace_endpos;
+       vector save_trace_plane_normal = trace_plane_normal;
+       float save_trace_plane_dist = trace_plane_dist;
+       entity save_trace_ent = trace_ent;
+       int save_trace_dpstartcontents = trace_dpstartcontents;
+       int save_trace_dphitcontents = trace_dphitcontents;
+       int save_trace_dphitq3surfaceflags = trace_dphitq3surfaceflags;
+       string save_trace_dphittexturename = trace_dphittexturename;
+
        if(this.solid != SOLID_NOT && gettouch(this))
                gettouch(this)(this, oth);
 
        if(oth.solid != SOLID_NOT && gettouch(oth))
                gettouch(oth)(oth, this);
+
+       trace_allsolid = save_trace_allsolid;
+       trace_startsolid = save_trace_startsolid;
+       trace_fraction = save_trace_fraction;
+       trace_inwater = save_trace_inwater;
+       trace_inopen = save_trace_inopen;
+       trace_endpos = save_trace_endpos;
+       trace_plane_normal = save_trace_plane_normal;
+       trace_plane_dist = save_trace_plane_dist;
+       trace_ent = save_trace_ent;
+       trace_dpstartcontents = save_trace_dpstartcontents;
+       trace_dphitcontents = save_trace_dphitcontents;
+       trace_dphitq3surfaceflags = save_trace_dphitq3surfaceflags;
+       trace_dphittexturename = save_trace_dphittexturename;
 }
 
 void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGrid
@@ -341,7 +454,22 @@ void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGr
        if(this.solid == SOLID_NOT)
                return;
 
-    FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
+       // due to a lack of pointers in QC, we must save the trace values and restore them for other functions
+       bool save_trace_allsolid = trace_allsolid;
+       bool save_trace_startsolid = trace_startsolid;
+       float save_trace_fraction = trace_fraction;
+       bool save_trace_inwater = trace_inwater;
+       bool save_trace_inopen = trace_inopen;
+       vector save_trace_endpos = trace_endpos;
+       vector save_trace_plane_normal = trace_plane_normal;
+       float save_trace_plane_dist = trace_plane_dist;
+       entity save_trace_ent = trace_ent;
+       int save_trace_dpstartcontents = trace_dpstartcontents;
+       int save_trace_dphitcontents = trace_dphitcontents;
+       int save_trace_dphitq3surfaceflags = trace_dphitq3surfaceflags;
+       string save_trace_dphittexturename = trace_dphittexturename;
+
+    FOREACH_ENTITY_RADIUS_ORDERED(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, {
                if (it.solid == SOLID_TRIGGER && it != this)
                if (it.move_nomonsters != MOVE_NOMONSTERS && it.move_nomonsters != MOVE_WORLDONLY)
                if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax))
@@ -363,6 +491,20 @@ void _Movetype_LinkEdict_TouchAreaGrid(entity this)  // SV_LinkEdict_TouchAreaGr
                        gettouch(it)(it, this);
                }
     });
+
+       trace_allsolid = save_trace_allsolid;
+       trace_startsolid = save_trace_startsolid;
+       trace_fraction = save_trace_fraction;
+       trace_inwater = save_trace_inwater;
+       trace_inopen = save_trace_inopen;
+       trace_endpos = save_trace_endpos;
+       trace_plane_normal = save_trace_plane_normal;
+       trace_plane_dist = save_trace_plane_dist;
+       trace_ent = save_trace_ent;
+       trace_dpstartcontents = save_trace_dpstartcontents;
+       trace_dphitcontents = save_trace_dphitcontents;
+       trace_dphitq3surfaceflags = save_trace_dphitq3surfaceflags;
+       trace_dphittexturename = save_trace_dphittexturename;
 }
 
 bool autocvar__movetype_debug = false;
@@ -421,7 +563,7 @@ int _Movetype_ContentsMask(entity this)  // SV_GenericHitSuperContentsMask
                        return this.dphitcontentsmask;
                else if(this.solid == SOLID_SLIDEBOX)
                {
-                       if(this.flags & 32) // TODO: FL_MONSTER
+                       if(this.flags & FL_MONSTER)
                                return DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_MONSTERCLIP;
                        else
                                return DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
@@ -522,7 +664,7 @@ void _Movetype_CheckStuck(entity this)  // SV_CheckStuck
        }
 }
 
-vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // SV_ClipVelocity
+vector _Movetype_ClipVelocity(vector vel, vector norm, float f)  // ClipVelocity
 {
        vel -= ((vel * norm) * norm) * f;
 
@@ -551,12 +693,12 @@ void _Movetype_PushEntityTrace(entity this, vector push)
        tracebox(this.origin, this.mins, this.maxs, end, type, this);
 }
 
-bool _Movetype_PushEntity(entity this, vector push, bool failonstartsolid, bool dolink)  // SV_PushEntity
+bool _Movetype_PushEntity(entity this, vector push, bool dolink)  // SV_PushEntity
 {
        _Movetype_PushEntityTrace(this, push);
 
        // NOTE: this is a workaround for the QC's lack of a worldstartsolid trace parameter
-       if(trace_startsolid && failonstartsolid)
+       if(trace_startsolid)
        {
                int oldtype = this.move_nomonsters;
                this.move_nomonsters = MOVE_WORLDONLY;
@@ -578,10 +720,6 @@ bool _Movetype_PushEntity(entity this, vector push, bool failonstartsolid, bool
        return (this.origin == last_origin); // false if teleported by touch
 }
 
-
-.float ltime;
-.void() blocked;
-
 void _Movetype_Physics_Frame(entity this, float movedt)
 {
        this.move_didgravity = -1;
@@ -589,7 +727,7 @@ void _Movetype_Physics_Frame(entity this, float movedt)
        {
                case MOVETYPE_PUSH:
                case MOVETYPE_FAKEPUSH:
-                       LOG_DEBUG("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!");
+                       _Movetype_Physics_Push(this, movedt);
                        break;
                case MOVETYPE_NONE:
                        break;
@@ -615,9 +753,6 @@ void _Movetype_Physics_Frame(entity this, float movedt)
                case MOVETYPE_FLY:
                case MOVETYPE_FLY_WORLDONLY:
                        _Movetype_Physics_Toss(this, movedt);
-                       if(wasfreed(this))
-                               return;
-                       _Movetype_LinkEdict(this, true);
                        break;
                case MOVETYPE_PHYSICS:
                        break;
@@ -655,6 +790,8 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
                case MOVETYPE_BOUNCE:
                case MOVETYPE_BOUNCEMISSILE:
                case MOVETYPE_FLYMISSILE:
+                       if (GAMEPLAYFIX_UNSTICKPLAYERS(this) == 2)
+                               _Movetype_CheckStuck(this);
                        _Movetype_Physics_Toss(this, movedt);
                        break;
                case MOVETYPE_PHYSICS:
@@ -670,12 +807,24 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
 
 void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)  // to be run every move frame
 {
+       bool didmove = (this.move_time != 0);
        this.move_time = time;
 
        if(isclient)
                _Movetype_Physics_ClientFrame(this, movedt);
        else
+       {
+               // this doesn't apply to clients, and only applies to unmatched entities
+               // don't run think/move on newly spawned projectiles as it messes up
+               // movement interpolation and rocket trails, and is inconsistent with
+               // respect to entities spawned in the same frame
+               // (if an ent spawns a higher numbered ent, it moves in the same frame,
+               //  but if it spawns a lower numbered ent, it doesn't - this never moves
+               //  ents in the first frame regardless)
+               if(!didmove && GAMEPLAYFIX_DELAYPROJECTILES(this) > 0)
+                       return;
                _Movetype_Physics_Frame(this, movedt);
+       }
        if(wasfreed(this))
                return;