]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/physics/movetypes/push.qc
Merge branch 'master' into Lyberta/WaypointIcons
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / physics / movetypes / push.qc
diff --git a/qcsrc/common/physics/movetypes/push.qc b/qcsrc/common/physics/movetypes/push.qc
new file mode 100644 (file)
index 0000000..66ff42a
--- /dev/null
@@ -0,0 +1,222 @@
+#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
+
+       for(entity check = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); 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 || this.owner == check)
+                       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(check) || check.groundentity != this)
+               {
+                       tracebox(check.origin, check.mins, check.maxs, check.origin, MOVE_NOMONSTERS, check);
+                       if(!trace_startsolid)
+                               continue;
+               }
+               vector pivot = check.mins + 0.5 * (check.maxs - check.mins);
+               vector move;
+
+               if(rotated)
+               {
+                       vector org = check.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;
+
+               check.moved_from = check.origin;
+               check.moved_fromangles = check.angles;
+               IL_PUSH(g_pushmove_moved, check);
+
+               // physics objects need better collisions than this code can do
+               if(check.move_movetype == MOVETYPE_PHYSICS)
+               {
+                       check.origin = check.origin + move;
+                       _Movetype_LinkEdict(check, true);
+                       continue;
+               }
+
+               // try moving the contacted entity
+               int savesolid = this.solid;
+               this.solid = SOLID_NOT;
+               if(!_Movetype_PushEntity(check, move, true, true))
+               {
+                       // entity "check" got teleported
+                       check.angles_y += trace_fraction * moveangle.y;
+                       this.solid = savesolid;
+                       continue; // pushed enough
+               }
+               // FIXME: turn players specially
+               check.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.groundentity != this))
+                       UNSET_ONGROUND(check);
+
+               // if it is still inside the pusher, block
+               tracebox(check.origin, check.mins, check.maxs, check.origin, MOVE_NOMONSTERS, check);
+               if(trace_startsolid)
+               {
+                       if(_Movetype_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
+                       {
+                               // hack to invoke all necessary movement triggers
+                               _Movetype_PushEntity(check, '0 0 0', true, true);
+                               // we could fix it or entity "check" was telported
+                               continue;
+                       }
+
+                       // still inside pusher, so it's really blocked
+
+                       // fail the move
+                       if(check.mins_x == check.maxs_x)
+                               continue;
+                       if(check.solid == SOLID_NOT || check.solid == SOLID_TRIGGER)
+                       {
+                               // corpse
+                               check.mins_x = check.mins_y = 0;
+                               check.maxs = check.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,
+                       {
+                               check.origin = check.moved_from;
+                               check.angles = check.moved_fromangles;
+                               _Movetype_LinkEdict(check, 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, check);
+                       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, movetime);
+       }
+
+       if(this.nextthink > oldltime && this.nextthink <= this.ltime)
+       {
+               this.nextthink = 0;
+               getthink(this)(this);
+       }
+}