]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Rough implementation of MOVETYPE_PUSH in QuakeC
authorMario <mario.mario@y7mail.com>
Mon, 1 Jun 2020 13:34:29 +0000 (23:34 +1000)
committerMario <mario.mario@y7mail.com>
Mon, 1 Jun 2020 13:34:29 +0000 (23:34 +1000)
qcsrc/common/physics/movetypes/_mod.inc
qcsrc/common/physics/movetypes/_mod.qh
qcsrc/common/physics/movetypes/all.inc
qcsrc/common/physics/movetypes/movetypes.qc
qcsrc/common/physics/movetypes/movetypes.qh
qcsrc/common/physics/movetypes/push.qc [new file with mode: 0644]
qcsrc/common/physics/movetypes/push.qh [new file with mode: 0644]
qcsrc/lib/self.qh
qcsrc/server/g_world.qc

index 5cb1d0bc4e99ac3840142341505923e501a3d5dc..4effcbd04a0a983e4d4bac87e760ada7f99d4928 100644 (file)
@@ -1,6 +1,7 @@
 // generated file; do not modify
 #include <common/physics/movetypes/follow.qc>
 #include <common/physics/movetypes/movetypes.qc>
+#include <common/physics/movetypes/push.qc>
 #include <common/physics/movetypes/step.qc>
 #include <common/physics/movetypes/toss.qc>
 #include <common/physics/movetypes/walk.qc>
index 1b1241a0aa8e2451dc8db4f243179ff5e67e0f4f..32ae3813c8ea37976b4f1bb2abb3befd08c135d3 100644 (file)
@@ -1,6 +1,7 @@
 // generated file; do not modify
 #include <common/physics/movetypes/follow.qh>
 #include <common/physics/movetypes/movetypes.qh>
+#include <common/physics/movetypes/push.qh>
 #include <common/physics/movetypes/step.qh>
 #include <common/physics/movetypes/toss.qh>
 #include <common/physics/movetypes/walk.qh>
index 70157d1862746141158ee5c31ee903ded84e7edb..b34bd3f883ba14421bdcac9e4e38e04132e4bd03 100644 (file)
@@ -2,5 +2,6 @@
 #include "walk.qc"
 #include "step.qc"
 #include "follow.qc"
+#include "push.qc"
 
 #include "movetypes.qc"
index 7894d14fd856bb1a8e09a4401fd88259ee940320..d8320cc27bad0d3ac55dc569aa2c2fac7fd96ddb 100644 (file)
@@ -4,12 +4,12 @@
 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 /*|| mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH*/) {
                this.move_qcphysics = false;
        }
        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 +18,90 @@ 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) // 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.03125f; // 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;
@@ -589,7 +673,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;
index 52610c58b27fe31604e3e4d1ca699cb4c0671e1a..13867d1fbad9b62ae5eabb19e7d91a43dbd19e76 100644 (file)
@@ -94,6 +94,7 @@ const int UNSTICK_STUCK = 2;
 // set by _Movetype_FlyMove
 vector move_stepnormal;
 
+bool _Movetype_NudgeOutOfSolid_PivotIsKnownGood(entity this, vector pivot);
 void _Movetype_WallFriction(entity this, vector stepnormal);
 int _Movetype_FlyMove(entity this, float dt, bool applygravity, bool applystepnormal, float stepheight);
 void _Movetype_CheckVelocity(entity this);
@@ -142,6 +143,7 @@ const int MOVETYPE_ANGLECLIP        = 2;
 #endif
 
 const int MOVETYPE_QCPLAYER = 150; // QC-driven player physics, no think functions!
+const int MOVETYPE_QCENTITY = 151; // QC-driven entity physics, some think functions!
 
 const int FL_ONSLICK = BIT(20);
 
diff --git a/qcsrc/common/physics/movetypes/push.qc b/qcsrc/common/physics/movetypes/push.qc
new file mode 100644 (file)
index 0000000..2885248
--- /dev/null
@@ -0,0 +1,227 @@
+#include "push.qh"
+void _Movetype_PushMove(entity this, float dt) // SV_PushMove
+{
+       if(this.velocity == '0 0 0' && this.avelocity == '0 0 0')
+       {
+               this.ltime += dt;
+               return;
+       }
+
+       switch(this.solid)
+       {
+               // LadyHavoc: valid pusher types
+               case SOLID_BSP:
+               case SOLID_BBOX:
+               case SOLID_SLIDEBOX:
+               case SOLID_CORPSE: // LadyHavoc: this would be weird...
+                       break;
+               // LadyHavoc: no collisions
+               case SOLID_NOT:
+               case SOLID_TRIGGER:
+               {
+                       this.origin = this.origin + dt * this.velocity;
+                       this.angles = this.angles + dt * this.avelocity;
+                       this.angles_x -= 360.0 * floor(this.angles_x) * (1.0 / 360.0);
+                       this.angles_y -= 360.0 * floor(this.angles_y) * (1.0 / 360.0);
+                       this.angles_z -= 360.0 * floor(this.angles_z) * (1.0 / 360.0);
+                       this.ltime += dt;
+                       _Movetype_LinkEdict(this, false);
+                       return;
+               }
+               default:
+               {
+                       LOG_INFOF("_Movetype_Physics_Push: entity #%d, unrecognized solid type %d", etof(this), this.solid);
+                       return;
+               }
+       }
+       if(!this.modelindex)
+       {
+               LOG_INFOF("_Movetype_Physics_Push: entity #%d has an invalid modelindex %d", etof(this), this.modelindex);
+               return;
+       }
+
+       bool rotated = ((vlen2(this.angles) + vlen2(this.avelocity)) > 0);
+
+       vector move1 = this.velocity * dt;
+       vector moveangle = this.avelocity * dt;
+
+       vector a = -moveangle;
+       vector forward, left, up;
+       MAKE_VECTORS(a, forward, left, up);
+       left *= -1; // actually make it left!
+
+       vector pushorig = this.origin;
+       vector pushang = this.angles;
+       float pushltime = this.ltime;
+
+       // move the pusher to its final position
+
+       this.origin = this.origin + dt * this.velocity;
+       this.angles = this.angles + dt * this.avelocity;
+       this.ltime += dt;
+       _Movetype_LinkEdict(this, false); // pulls absmin/absmax from the engine
+
+       if(this.move_movetype == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
+       {
+               this.angles_x -= 360.0 * floor(this.angles_x) * (1.0 / 360.0);
+               this.angles_y -= 360.0 * floor(this.angles_y) * (1.0 / 360.0);
+               this.angles_z -= 360.0 * floor(this.angles_z) * (1.0 / 360.0);
+               return;
+       }
+
+       IL_CLEAR(g_pushmove_moved); // make sure it's not somehow uncleared
+
+       FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true,
+       {
+               switch(it.move_movetype)
+               {
+                       case MOVETYPE_NONE:
+                       case MOVETYPE_PUSH:
+                       case MOVETYPE_FOLLOW:
+                       case MOVETYPE_NOCLIP:
+                       case MOVETYPE_FLY_WORLDONLY:
+                               continue;
+                       default:
+                               break;
+               }
+
+               if(it.owner == this || this.owner == it)
+                       continue;
+
+               // if the entity is standing on the pusher, it will definitely be moved
+               // if the entity is not standing on the pusher, but is in the pusher's
+               // final position, move it
+               if (!IS_ONGROUND(it) || it.groundentity != this)
+               {
+                       tracebox(it.origin, it.mins, it.maxs, it.origin, MOVE_NORMAL, it);
+                       if(!trace_startsolid)
+                               continue;
+               }
+               vector pivot = it.mins + 0.5 * it.maxs;
+               vector move;
+
+               if(rotated)
+               {
+                       vector org = it.origin - this.origin;
+                       org = org + pivot;
+
+                       vector org2;
+                       org2.x = (org * forward);
+                       org2.y = (org * left);
+                       org2.z = (org * up);
+                       move = org2 - org;
+                       move = move + move1;
+               }
+               else
+                       move = move1;
+
+               it.moved_from = it.origin;
+               it.moved_fromangles = it.angles;
+               IL_PUSH(g_pushmove_moved, it);
+
+               // physics objects need better collisions than this code can do
+               if(it.move_movetype == MOVETYPE_PHYSICS)
+               {
+                       it.origin = it.origin + move;
+                       _Movetype_LinkEdict(it, true);
+                       continue;
+               }
+
+               // try moving the contacted entity
+               int savesolid = it.solid;
+               it.solid = SOLID_NOT;
+               if(!_Movetype_PushEntity(it, move, true, false))
+               {
+                       // entity "check" got teleported
+                       it.angles_y += trace_fraction * moveangle.y;
+                       it.solid = savesolid;
+                       continue; // pushed enough
+               }
+               // FIXME: turn players specially
+               it.angles_y += trace_fraction * moveangle.y;
+               it.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(it.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || it.groundentity != this))
+                       UNSET_ONGROUND(it);
+
+               // if it is still inside the pusher, block
+               tracebox(it.origin, it.mins, it.maxs, it.origin, MOVE_NORMAL, it);
+               if(trace_startsolid)
+               {
+                       if(_Movetype_NudgeOutOfSolid_PivotIsKnownGood(it, pivot))
+                       {
+                               // hack to invoke all necessary movement triggers
+                               vector move2 = '0 0 0';
+                               if(!_Movetype_PushEntity(it, move2, true, false))
+                               {
+                                       // entity "check" got teleported
+                                       continue;
+                               }
+                               // we could fix it
+                               continue;
+                       }
+
+                       // still inside pusher, so it's really blocked
+
+                       // fail the move
+                       if(it.mins_x == it.maxs_x)
+                               continue;
+                       if(it.solid == SOLID_NOT || it.solid == SOLID_TRIGGER)
+                       {
+                               // corpse
+                               it.mins_x = it.mins_y = 0;
+                               it.maxs = it.mins;
+                               continue;
+                       }
+
+                       this.origin = pushorig;
+                       this.angles = pushang;
+                       this.ltime = pushltime;
+                       _Movetype_LinkEdict(this, false);
+
+                       // move back any entities we already moved
+                       IL_EACH(g_pushmove_moved, true,
+                       {
+                               it.origin = it.moved_from;
+                               it.angles = it.moved_fromangles;
+                               _Movetype_LinkEdict(it, false);
+                       });
+
+                       // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
+                       if(getblocked(this))
+                               getblocked(this)(this, it);
+                       break;
+               }
+       });
+       this.angles_x -= 360.0 * floor(this.angles_x) * (1.0 / 360.0);
+       this.angles_y -= 360.0 * floor(this.angles_y) * (1.0 / 360.0);
+       this.angles_z -= 360.0 * floor(this.angles_z) * (1.0 / 360.0);
+       IL_CLEAR(g_pushmove_moved); // clean up
+}
+
+void _Movetype_Physics_Push(entity this, float dt) // SV_Physics_Pusher
+{
+       float oldltime = this.ltime;
+       float movetime = dt;
+       if(this.nextthink < this.ltime + dt)
+       {
+               movetime = this.nextthink - this.ltime;
+               if(movetime < 0)
+                       movetime = 0;
+       }
+
+       if(movetime)
+       {
+               // advances this.ltime if not blocked
+               _Movetype_PushMove(this, dt);
+       }
+
+       if(this.nextthink > oldltime && this.nextthink <= this.ltime)
+       {
+               this.nextthink = 0;
+               getthink(this)(this);
+       }
+}
diff --git a/qcsrc/common/physics/movetypes/push.qh b/qcsrc/common/physics/movetypes/push.qh
new file mode 100644 (file)
index 0000000..f33e761
--- /dev/null
@@ -0,0 +1,12 @@
+#pragma once
+
+void _Movetype_Physics_Push(entity this, float dt);
+
+.vector moved_from, moved_fromangles;
+
+IntrusiveList g_pushmove_moved;
+STATIC_INIT(g_pushmove_moved) { g_pushmove_moved = IL_NEW(); }
+
+#ifdef CSQC
+.float ltime;
+#endif
index 4299c19cd3674a286d3b5eea310a0754f3db0ad1..d5ddf202d43b93573ef4d6af6ac927ca6bb9a43a 100644 (file)
@@ -77,7 +77,7 @@ SELFWRAP(touch, void, (), (entity this, entity toucher), (this, other))
 #ifdef GAMEQC
 SELFWRAP(blocked, void, (), (entity this, entity blocker), (this, other))
 #define setblocked(e, f) SELFWRAP_SET(blocked, e, f)
-#define blocked stopusingthis
+#define getblocked(e) SELFWRAP_GET(blocked, e)
 #endif
 
 SELFWRAP(predraw, void, (), (entity this), (this))
index 511a448b4e5d20467b240aa6b202509483cd4c34..90e796266fb30dca4221c226f10aac410b93f5da 100644 (file)
@@ -2064,12 +2064,12 @@ void Physics_Frame()
 
        IL_EACH(g_moveables, true,
        {
-               if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH || it.move_movetype == MOVETYPE_PHYSICS)
+               if(IS_CLIENT(it) || it.classname == "" /*|| it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH*/ || it.move_movetype == MOVETYPE_PHYSICS)
                        continue;
 
                //set_movetype(it, it.move_movetype);
                // inline the set_movetype function, since this is called a lot
-               it.movetype = (it.move_qcphysics) ? MOVETYPE_NONE : it.move_movetype;
+               it.movetype = (it.move_qcphysics) ? MOVETYPE_QCENTITY : it.move_movetype;
 
                if(it.move_movetype == MOVETYPE_NONE)
                        continue;
@@ -2079,6 +2079,8 @@ void Physics_Frame()
 
                if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling
                {
+                       if(it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH)
+                               continue; // these movetypes have no regular think function
                        // handle thinking here
                        if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + frametime)
                                RunThink(it);