2 #include "../dpdefs/csprogsdefs.qh"
4 #include "../common/stats.qh"
5 #include "../common/util.qh"
6 #include "movetypes.qh"
7 #include "../csqcmodellib/common.qh"
8 #include "../server/t_items.qh"
14 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
16 .entity move_groundentity; // FIXME add move_groundnetworkentity?
17 .float move_suspendedinair;
18 .float move_didgravity;
20 void _Movetype_CheckVelocity() // SV_CheckVelocity
24 float _Movetype_CheckWater(entity ent) // SV_CheckWater
26 vector point = ent.move_origin;
27 point_z += (ent.mins_z + 1);
29 int nativecontents = pointcontents(point);
31 if(ent.move_watertype)
32 if(ent.move_watertype != nativecontents)
34 //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
35 if(ent.contentstransition)
36 ent.contentstransition(ent.move_watertype, nativecontents);
39 ent.move_waterlevel = 0;
40 ent.move_watertype = CONTENT_EMPTY;
42 int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
43 if(supercontents & DPCONTENTS_LIQUIDSMASK)
45 ent.move_watertype = nativecontents;
46 ent.move_waterlevel = 1;
47 point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
48 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
50 ent.move_waterlevel = 2;
51 point_y = ent.origin_y + ent.view_ofs_y;
52 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
53 ent.move_waterlevel = 3;
57 return (ent.move_waterlevel > 1);
60 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
62 float contents = pointcontents(ent.move_origin);
64 if(!ent.move_watertype)
67 if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
69 ent.move_watertype = contents;
70 ent.move_waterlevel = 1;
74 else if(ent.move_watertype != contents)
76 //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));
77 if(ent.contentstransition)
78 ent.contentstransition(ent.move_watertype, contents);
81 if(contents <= CONTENT_WATER)
83 ent.move_watertype = contents;
84 ent.move_waterlevel = 1;
88 ent.move_watertype = CONTENT_EMPTY;
89 ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
93 void _Movetype_Impact(entity oth) // SV_Impact
95 entity oldother, oldself;
121 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
123 entity e, oldself, oldother;
128 for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
131 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
136 trace_allsolid = false;
137 trace_startsolid = false;
139 trace_inwater = false;
141 trace_endpos = e.origin;
142 trace_plane_normal = '0 0 1';
143 trace_plane_dist = 0;
154 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
157 if(self.solid == SOLID_BSP)
159 // TODO set the absolute bbox
168 mi = mi + self.origin;
169 ma = ma + self.origin;
171 if(self.move_flags & FL_ITEM)
194 _Movetype_LinkEdict_TouchAreaGrid();
197 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
200 org = self.move_origin + ofs;
202 int cont = self.dphitcontentsmask;
203 self.dphitcontentsmask = DPCONTENTS_SOLID;
204 tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
205 self.dphitcontentsmask = cont;
210 if(vlen(trace_endpos - self.move_origin) > 0.0001)
211 self.move_origin = trace_endpos;
215 float _Movetype_UnstickEntity() // SV_UnstickEntity
217 if(!_Movetype_TestEntityPosition('0 0 0'))
219 if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
220 if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
221 if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
222 if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
223 if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
224 if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
225 if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
226 if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
228 for(i = 1; i <= 17; ++i)
230 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
231 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
233 dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
236 dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
237 _Movetype_LinkEdict(true);
241 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
243 vel = vel - ((vel * norm) * norm) * f;
245 if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
246 if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
247 if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
252 void _Movetype_PushEntityTrace(vector push)
257 end = self.move_origin + push;
259 if(self.move_nomonsters)
260 type = max(0, self.move_nomonsters);
261 else if(self.move_movetype == MOVETYPE_FLYMISSILE)
263 else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
264 type = MOVE_NOMONSTERS;
268 tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
271 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
273 _Movetype_PushEntityTrace(push);
275 if(trace_startsolid && failonstartsolid)
276 return trace_fraction;
278 self.move_origin = trace_endpos;
280 if(trace_fraction < 1)
281 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
282 _Movetype_Impact(trace_ent);
284 return trace_fraction;
290 void _Movetype_AngleVectorsFLU(vector myangles) // AngleVectorsFLU
292 float angle, sr, sp, sy, cr, cp, cy;
294 angle = myangles_y * (M_PI*2 / 360);
297 angle = myangles_x * (M_PI*2 / 360);
310 angle = myangles_z * (M_PI*2 / 360);
315 v_right_x = sr*sp*cy+cr*-sy;
316 v_right_y = sr*sp*sy+cr*cy;
321 v_up_x = cr*sp*cy+-sr*-sy;
322 v_up_y = cr*sp*sy+-sr*cy;
344 void _Movetype_PushMove(float dt) // SV_PushMove
350 //int numcheckentities;
353 vector move1, moveangle;
355 vector pushorig, pushang;
360 if(!vlen(self.move_velocity) && !vlen(self.move_avelocity))
368 // LordHavoc: valid pusher types
372 case SOLID_CORPSE: // LordHavoc: this would be weird...
374 // LordHavoc: no collisions
378 self.move_origin = self.move_origin + self.move_velocity * dt;
379 self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
380 self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
381 self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
383 _Movetype_LinkEdict(true);
386 printf("_Movetype_PushMove: entity %e with classname %s, unrecognized solid type %d\n", num_for_edict(self), self.classname, self.solid);
389 if(!self.modelindex || self.model == "null")
391 printf("_Movetype_PushMove: entity %e with classname %s, unusable modelindex %f\n", num_for_edict(self), self.classname, self.modelindex);
394 //pusherowner = self.owner;
396 rotated = (self.move_angles * self.move_avelocity) > 0;
398 move1 = self.move_velocity * dt;
399 moveangle = self.move_avelocity * dt;
403 // sets v_forward, v_right and v_up
404 _Movetype_AngleVectorsFLU(a);
406 pushorig = self.move_origin;
407 pushang = self.move_angles;
408 pushltime = self.ltime;
410 // move the pusher to its final position
412 self.move_origin = self.move_origin + self.move_velocity * dt;
413 self.move_angles = self.move_angles + self.move_avelocity * dt;
415 _Movetype_LinkEdict(true);
417 //pushermodel = SV_GetModelFromEdict(pusher);
418 //Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, PRVM_serveredictvector(pusher, origin)[0], PRVM_serveredictvector(pusher, origin)[1], PRVM_serveredictvector(pusher, origin)[2],
419 //PRVM_serveredictvector(pusher, angles)[0], PRVM_serveredictvector(pusher, angles)[1], PRVM_serveredictvector(pusher, angles)[2], 1);
420 //Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
422 savesolid = self.solid;
424 // see if any solid entities are inside the final position
428 if(self.move_movetype != MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
429 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
437 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
439 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
440 //check->priv.server->waterposition_forceupdate = true;
442 //int checkcontents = pointcontents(e.move_origin);
444 // if the entity is standing on the pusher, it will definitely be moved
445 // if the entity is not standing on the pusher, but is in the pusher's
446 // final position, move it
447 if(!(e.move_flags & FL_ONGROUND) || e.move_groundentity != self)
449 //vector pushermins = self.mins;
450 //vector pushermaxs = self.maxs;
451 //vector checkorigin = e.origin;
452 //vector checkmins = e.mins;
453 //vector checkmaxs = e.maxs;
454 //Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix,
455 //&pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, collision_extendmovelength.value);
456 tracebox(e.move_origin, e.mins, e.maxs, self.move_origin, MOVE_NOMONSTERS, e);
457 if(!trace_startsolid)
459 //Con_Printf("- not in solid\n");
463 vector pivot = e.mins + 0.5 * (e.maxs - e.mins);
464 //VectorClear(pivot);
469 org = e.move_origin - self.move_origin;
471 org2_x = (org * v_forward);
472 org2_y = (org * v_right);
473 org2_z = (org * v_up);
480 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
482 //VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
483 //VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
484 //moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
486 // physics objects need better collisions than this code can do
487 /*if(e.move_movetype == MOVETYPE_PHYSICS)
489 e.move_origin += move;
492 _Movetype_LinkEdict(false);
493 _Movetype_LinkEdict_TouchAreaGrid();
498 // try moving the contacted entity
499 self.solid = SOLID_NOT;
502 if(!_Movetype_PushEntity(move, true))
504 // entity "check" got teleported
505 self.angles_y += trace_fraction * moveangle_y;
506 oldself.solid = savesolid;
507 continue; // pushed enough
510 // FIXME: turn players specially
511 e.angles_y += trace_fraction * moveangle_y;
512 self.solid = savesolid;
513 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
515 // this trace.fraction < 1 check causes items to fall off of pushers
516 // if they pass under or through a wall
517 // the groundentity check causes items to fall off of ledges
518 if(e.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || e.move_groundentity != self))
519 e.move_flags &= ~FL_ONGROUND;
521 // if it is still inside the pusher, block
522 //vector pushermins = self.mins;
523 //vector pushermaxs = self.maxs;
524 //vector checkorigin = e.move_origin;
525 //vector checkmins = e.mins;
526 //vector checkmaxs = e.maxs;
527 //Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY,
528 //&pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, collision_extendmovelength.value);
529 tracebox(e.move_origin, e.mins, e.maxs, self.move_origin, MOVE_NOMONSTERS, e);
533 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
535 // hack to invoke all necessary movement triggers
537 if(!_Movetype_PushEntity(move2, true))
539 // entity "check" got teleported
546 // still inside pusher, so it's really blocked
549 if(e.mins_x == e.maxs_x)
551 if(e.solid == SOLID_NOT || e.solid == SOLID_TRIGGER)
554 e.mins_x = e.mins_y = 0;
559 self.move_origin = pushorig;
560 self.move_angles = pushang;
561 self.ltime = pushltime;
562 _Movetype_LinkEdict(true);
564 // move back any entities we already moved
565 /*for (i = 0;i < num_moved;i++)
567 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
568 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
569 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
573 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
576 self.move_time = time;
583 self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
584 self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
585 self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
588 void _Movetype_Physics_Pusher(float dt) // SV_Physics_Pusher
590 float oldltime, movetime;
592 oldltime = self.ltime;
594 if (self.nextthink < self.ltime + dt)
596 movetime = self.nextthink - self.ltime;
604 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
605 _Movetype_PushMove(movetime);
607 if (self.nextthink > oldltime && self.nextthink <= self.ltime)
611 self.move_time = time;
618 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
620 if(self.move_flags & FL_ONGROUND)
622 if(self.move_velocity_z >= 1/32)
623 self.move_flags &= ~FL_ONGROUND;
624 else if(!self.move_groundentity)
626 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
628 self.move_groundentity = world;
633 self.move_suspendedinair = false;
635 _Movetype_CheckVelocity();
637 if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
639 self.move_didgravity = 1;
640 if(GRAVITY_UNAFFECTED_BY_TICRATE)
643 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
645 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
650 self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
652 self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
656 self.move_angles = self.move_angles + self.move_avelocity * dt;
658 float movetime, bump;
660 for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
663 move = self.move_velocity * movetime;
664 _Movetype_PushEntity(move, true);
670 _Movetype_UnstickEntity();
671 _Movetype_PushEntity(move, false);
676 if(trace_fraction == 1)
679 movetime *= 1 - min(1, trace_fraction);
681 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
683 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
684 self.move_flags &= ~FL_ONGROUND;
686 else if(self.move_movetype == MOVETYPE_BOUNCE)
688 float d, bouncefac, bouncestop;
690 bouncefac = self.move_bounce_factor; if(!bouncefac) bouncefac = 0.5;
691 bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
693 bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
695 bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
697 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
699 d = trace_plane_normal * self.move_velocity;
700 if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
702 self.move_flags |= FL_ONGROUND;
703 self.move_groundentity = trace_ent;
704 self.move_velocity = '0 0 0';
705 self.move_avelocity = '0 0 0';
708 self.move_flags &= ~FL_ONGROUND;
712 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
713 if(trace_plane_normal_z > 0.7)
715 self.move_flags |= FL_ONGROUND;
716 self.move_groundentity = trace_ent;
717 if(trace_ent.solid == SOLID_BSP)
718 self.move_suspendedinair = true;
719 self.move_velocity = '0 0 0';
720 self.move_avelocity = '0 0 0';
723 self.move_flags &= ~FL_ONGROUND;
726 // DP revision 8905 (just, WHY...)
727 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
730 // DP revision 8918 (WHY...)
731 if(self.move_flags & FL_ONGROUND)
735 if(GRAVITY_UNAFFECTED_BY_TICRATE)
736 if(self.move_didgravity > 0)
737 if(!(self.move_flags & FL_ONGROUND))
740 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
742 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
745 _Movetype_CheckWaterTransition(self);
748 void _Movetype_Physics_Frame(float movedt)
750 self.move_didgravity = -1;
751 switch(self.move_movetype)
754 case MOVETYPE_FAKEPUSH:
755 _Movetype_Physics_Pusher(movedt);
759 case MOVETYPE_FOLLOW:
760 error("SV_Physics_Follow not implemented");
762 case MOVETYPE_NOCLIP:
763 _Movetype_CheckWater(self);
764 self.move_origin = self.move_origin + ticrate * self.move_velocity;
765 self.move_angles = self.move_angles + ticrate * self.move_avelocity;
766 _Movetype_LinkEdict(false);
769 error("SV_Physics_Step not implemented");
772 error("SV_Physics_Walk not implemented");
775 case MOVETYPE_BOUNCE:
776 case MOVETYPE_BOUNCEMISSILE:
777 case MOVETYPE_FLYMISSILE:
779 _Movetype_Physics_Toss(movedt);
784 void Movetype_Physics_NoMatchServer() // optimized
788 movedt = time - self.move_time;
789 self.move_time = time;
791 _Movetype_Physics_Frame(movedt);
795 self.avelocity = self.move_avelocity;
796 self.velocity = self.move_velocity;
797 self.angles = self.move_angles;
798 setorigin(self, self.move_origin);
801 void Movetype_Physics_MatchServer(bool sloppy)
803 Movetype_Physics_MatchTicrate(ticrate, sloppy);
806 void Movetype_Physics_MatchTicrate(float tr, bool sloppy) // SV_Physics_Entity
808 float n, i, dt, movedt;
812 Movetype_Physics_NoMatchServer();
816 dt = time - self.move_time;
819 n = max(0, floor(dt / tr));
821 self.move_time += n * tr;
823 if(!self.move_didgravity)
824 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
826 for(i = 0; i < n; ++i)
828 _Movetype_Physics_Frame(movedt);
833 self.avelocity = self.move_avelocity;
835 if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
837 // now continue the move from move_time to time
838 self.velocity = self.move_velocity;
840 if(self.move_didgravity > 0)
842 if(GRAVITY_UNAFFECTED_BY_TICRATE)
845 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
847 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
852 self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
854 self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
858 self.angles = self.move_angles + dt * self.avelocity;
860 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
862 setorigin(self, self.move_origin + dt * self.velocity);
866 _Movetype_PushEntityTrace(dt * self.velocity);
867 if(!trace_startsolid)
868 setorigin(self, trace_endpos);
871 if(self.move_didgravity > 0)
873 if(GRAVITY_UNAFFECTED_BY_TICRATE)
876 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
878 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
884 self.velocity = self.move_velocity;
885 self.angles = self.move_angles;
886 setorigin(self, self.move_origin);