1 #include "../common/physics.qh"
4 #include "../dpdefs/csprogsdefs.qh"
6 #include "../common/stats.qh"
7 #include "../common/util.qh"
8 #include "movetypes.qh"
9 #include "../csqcmodellib/common.qh"
10 #include "../server/t_items.qh"
13 #include "../server/autocvars.qh"
18 #define GRAVITY_UNAFFECTED_BY_TICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate
20 #define TICRATE sys_frametime
22 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
24 #define TICRATE ticrate
27 .entity move_groundentity; // FIXME add move_groundnetworkentity?
28 .float move_suspendedinair;
29 .float move_didgravity;
31 void _Movetype_CheckVelocity() // SV_CheckVelocity
35 float _Movetype_CheckWater(entity ent) // SV_CheckWater
37 vector point = ent.move_origin;
38 point_z += (ent.mins_z + 1);
40 int nativecontents = pointcontents(point);
42 if(ent.move_watertype)
43 if(ent.move_watertype != nativecontents)
45 //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
46 if(ent.contentstransition)
47 ent.contentstransition(ent.move_watertype, nativecontents);
50 ent.move_waterlevel = 0;
51 ent.move_watertype = CONTENT_EMPTY;
53 int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
54 if(supercontents & DPCONTENTS_LIQUIDSMASK)
56 ent.move_watertype = nativecontents;
57 ent.move_waterlevel = 1;
58 point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
59 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
61 ent.move_waterlevel = 2;
62 point_y = ent.origin_y + ent.view_ofs_y;
63 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
64 ent.move_waterlevel = 3;
68 return (ent.move_waterlevel > 1);
71 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
73 float contents = pointcontents(ent.move_origin);
75 if(!ent.move_watertype)
78 if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
80 ent.move_watertype = contents;
81 ent.move_waterlevel = 1;
85 else if(ent.move_watertype != contents)
87 //print(sprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents));
88 if(ent.contentstransition)
89 ent.contentstransition(ent.move_watertype, contents);
92 if(contents <= CONTENT_WATER)
94 ent.move_watertype = contents;
95 ent.move_waterlevel = 1;
99 ent.move_watertype = CONTENT_EMPTY;
100 ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
104 void _Movetype_Impact(entity oth) // SV_Impact
106 entity oldother, oldself;
132 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
134 entity e, oldself, oldother;
139 for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
142 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
147 trace_allsolid = false;
148 trace_startsolid = false;
150 trace_inwater = false;
152 trace_endpos = e.origin;
153 trace_plane_normal = '0 0 1';
154 trace_plane_dist = 0;
165 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
168 if(self.solid == SOLID_BSP)
170 // TODO set the absolute bbox
179 mi = mi + self.origin;
180 ma = ma + self.origin;
182 if(self.move_flags & FL_ITEM)
205 _Movetype_LinkEdict_TouchAreaGrid();
208 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
211 org = self.move_origin + ofs;
213 int cont = self.dphitcontentsmask;
214 self.dphitcontentsmask = DPCONTENTS_SOLID;
215 tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
216 self.dphitcontentsmask = cont;
221 if(vlen(trace_endpos - self.move_origin) > 0.0001)
222 self.move_origin = trace_endpos;
226 float _Movetype_UnstickEntity() // SV_UnstickEntity
228 if(!_Movetype_TestEntityPosition('0 0 0'))
230 if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
231 if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
232 if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
233 if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
234 if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
235 if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
236 if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
237 if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
239 for(i = 1; i <= 17; ++i)
241 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
242 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
244 dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
247 dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
248 _Movetype_LinkEdict(true);
252 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
254 vel = vel - ((vel * norm) * norm) * f;
256 if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
257 if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
258 if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
263 void _Movetype_PushEntityTrace(vector push)
268 end = self.move_origin + push;
270 if(self.move_nomonsters)
271 type = max(0, self.move_nomonsters);
272 else if(self.move_movetype == MOVETYPE_FLYMISSILE)
274 else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
275 type = MOVE_NOMONSTERS;
279 tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
282 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
284 _Movetype_PushEntityTrace(push);
286 if(trace_startsolid && failonstartsolid)
287 return trace_fraction;
289 self.move_origin = trace_endpos;
291 if(trace_fraction < 1)
292 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
293 _Movetype_Impact(trace_ent);
295 return trace_fraction;
301 void _Movetype_AngleVectorsFLU(vector myangles) // AngleVectorsFLU
303 float angle, sr, sp, sy, cr, cp, cy;
305 v_forward = v_right = v_up = '0 0 0';
307 angle = myangles_y * (M_PI*2 / 360);
310 angle = myangles_x * (M_PI*2 / 360);
323 angle = myangles_z * (M_PI*2 / 360);
328 v_right_x = sr*sp*cy+cr*-sy;
329 v_right_y = sr*sp*sy+cr*cy;
334 v_up_x = cr*sp*cy+-sr*-sy;
335 v_up_y = cr*sp*sy+-sr*cy;
357 void _Movetype_PushMove(float dt) // SV_PushMove
361 float movetime2, pushltime;
362 vector move, move1, moveangle, pushorig, pushang;
368 if(self.move_velocity == '0 0 0' && self.move_avelocity == '0 0 0')
370 self.move_ltime += dt;
376 // LordHavoc: valid pusher types
380 case SOLID_CORPSE: // LordHavoc: this would be weird...
382 // LordHavoc: no collisions
385 self.move_origin = self.move_origin + dt * self.move_velocity;
386 self.move_angles = self.move_angles + dt * self.move_avelocity;
387 self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
388 self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
389 self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
390 self.move_ltime += dt;
391 _Movetype_LinkEdict(true);
394 dprintf("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", self, self.solid);
398 rotated = dotproduct(self.move_angles, self.move_angles) + dotproduct(self.move_avelocity, self.move_avelocity) > 0;
402 move1 = self.move_velocity * movetime2;
403 moveangle = self.move_avelocity * movetime2;
406 // sets v_forward, v_right and v_up
407 _Movetype_AngleVectorsFLU(a);
409 pushorig = self.move_origin;
410 pushang = self.move_angles;
411 pushltime = self.move_ltime;
413 // move the pusher to its final position
415 self.move_origin = self.move_origin + dt * self.move_velocity;
416 self.move_angles = self.move_angles + dt * self.move_avelocity;
418 self.move_ltime += dt;
419 _Movetype_LinkEdict(true);
421 savesolid = self.solid;
423 if(self.move_movetype != MOVETYPE_FAKEPUSH)
424 for(check = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); check; check = check.chain)
426 switch(check.move_movetype)
430 case MOVETYPE_FOLLOW:
431 case MOVETYPE_NOCLIP:
432 case MOVETYPE_FLY_WORLDONLY:
438 if(check.owner == self)
441 if(self.owner == check)
444 pivot = check.mins + 0.5 * (check.maxs - check.mins);
445 //VectorClear(pivot);
450 vector org = check.move_origin - self.move_origin;
452 org2_x = dotproduct(org, v_forward);
453 org2_y = dotproduct(org, v_right);
454 org2_z = dotproduct(org, v_up);
461 // physics objects need better collisions than this code can do
462 if(check.move_movetype == 32) // MOVETYPE_PHYSICS
464 check.move_origin = check.move_origin + move;
467 _Movetype_LinkEdict(true);
472 // try moving the contacted entity
473 self.solid = SOLID_NOT;
476 if(!_Movetype_PushEntity(move, true))
479 // entity "check" got teleported
480 check.move_angles_y += trace_fraction * moveangle_y;
481 self.solid = savesolid;
482 continue; // pushed enough
485 // FIXME: turn players specially
486 check.move_angles_y += trace_fraction * moveangle_y;
487 self.solid = savesolid;
489 // this trace.fraction < 1 check causes items to fall off of pushers
490 // if they pass under or through a wall
491 // the groundentity check causes items to fall off of ledges
492 if(check.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || check.move_groundentity != self))
493 check.move_flags &= ~FL_ONGROUND;
496 self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
497 self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
498 self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
501 void _Movetype_Physics_Pusher(float dt) // SV_Physics_Pusher
503 float thinktime, oldltime, movetime;
505 oldltime = self.move_ltime;
507 thinktime = self.move_nextthink;
508 if(thinktime < self.move_ltime + dt)
510 movetime = thinktime - self.move_ltime;
518 // advances self.move_ltime if not blocked
519 _Movetype_PushMove(movetime);
521 if(thinktime > oldltime && thinktime <= self.move_ltime)
523 self.move_nextthink = 0;
524 self.move_time = time;
531 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
533 if(self.move_flags & FL_ONGROUND)
535 if(self.move_velocity_z >= 1/32)
536 self.move_flags &= ~FL_ONGROUND;
537 else if(!self.move_groundentity)
539 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
541 self.move_groundentity = world;
546 self.move_suspendedinair = false;
548 _Movetype_CheckVelocity();
550 if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
552 self.move_didgravity = 1;
553 if(GRAVITY_UNAFFECTED_BY_TICRATE)
556 self.move_velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
558 self.move_velocity_z -= 0.5 * dt * PHYS_GRAVITY;
563 self.move_velocity_z -= dt * self.gravity * PHYS_GRAVITY;
565 self.move_velocity_z -= dt * PHYS_GRAVITY;
569 self.move_angles = self.move_angles + self.move_avelocity * dt;
571 float movetime, bump;
573 for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
576 move = self.move_velocity * movetime;
577 _Movetype_PushEntity(move, true);
583 _Movetype_UnstickEntity();
584 _Movetype_PushEntity(move, false);
589 if(trace_fraction == 1)
592 movetime *= 1 - min(1, trace_fraction);
594 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
596 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
597 self.move_flags &= ~FL_ONGROUND;
599 else if(self.move_movetype == MOVETYPE_BOUNCE)
601 float d, bouncefac, bouncestop;
603 bouncefac = self.move_bounce_factor; if(!bouncefac) bouncefac = 0.5;
604 bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
606 bouncestop *= self.gravity * PHYS_GRAVITY;
608 bouncestop *= PHYS_GRAVITY;
610 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
612 d = trace_plane_normal * self.move_velocity;
613 if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
615 self.move_flags |= FL_ONGROUND;
616 self.move_groundentity = trace_ent;
617 self.move_velocity = '0 0 0';
618 self.move_avelocity = '0 0 0';
621 self.move_flags &= ~FL_ONGROUND;
625 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
626 if(trace_plane_normal_z > 0.7)
628 self.move_flags |= FL_ONGROUND;
629 self.move_groundentity = trace_ent;
630 if(trace_ent.solid == SOLID_BSP)
631 self.move_suspendedinair = true;
632 self.move_velocity = '0 0 0';
633 self.move_avelocity = '0 0 0';
636 self.move_flags &= ~FL_ONGROUND;
639 // DP revision 8905 (just, WHY...)
640 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
643 // DP revision 8918 (WHY...)
644 if(self.move_flags & FL_ONGROUND)
648 if(GRAVITY_UNAFFECTED_BY_TICRATE)
649 if(self.move_didgravity > 0)
650 if(!(self.move_flags & FL_ONGROUND))
653 self.move_velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
655 self.move_velocity_z -= 0.5 * dt * PHYS_GRAVITY;
658 _Movetype_CheckWaterTransition(self);
661 void _Movetype_Physics_Frame(float movedt)
663 self.move_didgravity = -1;
664 switch(self.move_movetype)
667 case MOVETYPE_FAKEPUSH:
668 _Movetype_Physics_Pusher(movedt);
672 case MOVETYPE_FOLLOW:
673 error("SV_Physics_Follow not implemented");
675 case MOVETYPE_NOCLIP:
676 _Movetype_CheckWater(self);
677 self.move_origin = self.move_origin + TICRATE * self.move_velocity;
678 self.move_angles = self.move_angles + TICRATE * self.move_avelocity;
679 _Movetype_LinkEdict(false);
682 error("SV_Physics_Step not implemented");
685 error("SV_Physics_Walk not implemented");
688 case MOVETYPE_BOUNCE:
689 case MOVETYPE_BOUNCEMISSILE:
690 case MOVETYPE_FLYMISSILE:
692 _Movetype_Physics_Toss(movedt);
697 void Movetype_Physics_NoMatchServer() // optimized
701 movedt = time - self.move_time;
702 self.move_time = time;
704 _Movetype_Physics_Frame(movedt);
708 self.avelocity = self.move_avelocity;
709 self.velocity = self.move_velocity;
710 self.angles = self.move_angles;
711 setorigin(self, self.move_origin);
714 void Movetype_Physics_MatchServer(bool sloppy)
716 Movetype_Physics_MatchTicrate(TICRATE, sloppy);
719 void Movetype_Physics_MatchTicrate(float tr, bool sloppy) // SV_Physics_Entity
721 float n, i, dt, movedt;
725 Movetype_Physics_NoMatchServer();
729 dt = time - self.move_time;
732 n = max(0, floor(dt / tr));
734 self.move_time += n * tr;
736 if(!self.move_didgravity)
737 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
739 for(i = 0; i < n; ++i)
741 _Movetype_Physics_Frame(movedt);
746 self.avelocity = self.move_avelocity;
748 if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
750 // now continue the move from move_time to time
751 self.velocity = self.move_velocity;
753 if(self.move_didgravity > 0)
755 if(GRAVITY_UNAFFECTED_BY_TICRATE)
758 self.velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
760 self.velocity_z -= 0.5 * dt * PHYS_GRAVITY;
765 self.velocity_z -= dt * self.gravity * PHYS_GRAVITY;
767 self.velocity_z -= dt * PHYS_GRAVITY;
771 self.angles = self.move_angles + dt * self.avelocity;
773 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
775 setorigin(self, self.move_origin + dt * self.velocity);
779 _Movetype_PushEntityTrace(dt * self.velocity);
780 if(!trace_startsolid)
781 setorigin(self, trace_endpos);
784 if(self.move_didgravity > 0)
786 if(GRAVITY_UNAFFECTED_BY_TICRATE)
789 self.velocity_z -= 0.5 * dt * self.gravity * PHYS_GRAVITY;
791 self.velocity_z -= 0.5 * dt * PHYS_GRAVITY;
797 self.velocity = self.move_velocity;
798 self.angles = self.move_angles;
799 setorigin(self, self.move_origin);