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 const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
15 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
17 .entity move_groundentity; // FIXME add move_groundnetworkentity?
18 .float move_suspendedinair;
19 .float move_didgravity;
21 void _Movetype_CheckVelocity() // SV_CheckVelocity
25 float _Movetype_CheckWater(entity ent) // SV_CheckWater
27 vector point = ent.move_origin;
28 point.z += (ent.mins.z + 1);
30 int nativecontents = pointcontents(point);
32 if(ent.move_watertype)
33 if(ent.move_watertype != nativecontents)
35 //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
36 if(ent.contentstransition)
37 ent.contentstransition(ent.move_watertype, nativecontents);
40 ent.move_waterlevel = 0;
41 ent.move_watertype = CONTENT_EMPTY;
43 int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
44 if(supercontents & DPCONTENTS_LIQUIDSMASK)
46 ent.move_watertype = nativecontents;
47 ent.move_waterlevel = 1;
48 point.y = (ent.origin.y + ((ent.mins.z + ent.maxs.y) * 0.5));
49 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
51 ent.move_waterlevel = 2;
52 point.y = ent.origin.y + ent.view_ofs.y;
53 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
54 ent.move_waterlevel = 3;
58 return (ent.move_waterlevel > 1);
61 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
63 float contents = pointcontents(ent.move_origin);
65 if(!ent.move_watertype)
68 if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
70 ent.move_watertype = contents;
71 ent.move_waterlevel = 1;
75 else if(ent.move_watertype != contents)
77 //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));
78 if(ent.contentstransition)
79 ent.contentstransition(ent.move_watertype, contents);
82 if(contents <= CONTENT_WATER)
84 ent.move_watertype = contents;
85 ent.move_waterlevel = 1;
89 ent.move_watertype = CONTENT_EMPTY;
90 ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
94 void _Movetype_Impact(entity oth) // SV_Impact
96 entity oldother, oldself;
122 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
124 entity e, oldself, oldother;
129 for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
132 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
137 trace_allsolid = false;
138 trace_startsolid = false;
140 trace_inwater = false;
142 trace_endpos = e.origin;
143 trace_plane_normal = '0 0 1';
144 trace_plane_dist = 0;
155 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
158 if(self.solid == SOLID_BSP)
160 // TODO set the absolute bbox
169 mi = mi + self.origin;
170 ma = ma + self.origin;
172 if(self.move_flags & FL_ITEM)
195 _Movetype_LinkEdict_TouchAreaGrid();
198 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
201 org = self.move_origin + ofs;
203 int cont = self.dphitcontentsmask;
204 self.dphitcontentsmask = DPCONTENTS_SOLID;
205 tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
206 self.dphitcontentsmask = cont;
211 if(vlen(trace_endpos - self.move_origin) > 0.0001)
212 self.move_origin = trace_endpos;
216 float _Movetype_UnstickEntity() // SV_UnstickEntity
218 if(!_Movetype_TestEntityPosition('0 0 0'))
220 if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
221 if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
222 if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
223 if(!_Movetype_TestEntityPosition('0 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;
227 if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
229 for(i = 1; i <= 17; ++i)
231 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
232 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
234 dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
237 dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin));
238 _Movetype_LinkEdict(true);
242 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
244 vel = vel - ((vel * norm) * norm) * f;
246 if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
247 if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
248 if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
253 void _Movetype_PushEntityTrace(vector push)
258 end = self.move_origin + push;
260 if(self.move_nomonsters)
261 type = max(0, self.move_nomonsters);
262 else if(self.move_movetype == MOVETYPE_FLYMISSILE)
264 else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
265 type = MOVE_NOMONSTERS;
269 tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
272 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
274 _Movetype_PushEntityTrace(push);
276 if(trace_startsolid && failonstartsolid)
277 return trace_fraction;
279 self.move_origin = trace_endpos;
281 if(trace_fraction < 1)
282 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
283 _Movetype_Impact(trace_ent);
285 return trace_fraction;
288 const float MAX_CLIP_PLANES = 5;
289 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
291 if(self.move_flags & FL_ONGROUND)
293 if(self.move_velocity.z >= 1/32)
294 self.move_flags &= ~FL_ONGROUND;
295 else if(!self.move_groundentity)
297 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
299 self.move_groundentity = world;
304 self.move_suspendedinair = false;
306 _Movetype_CheckVelocity();
308 if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
310 self.move_didgravity = 1;
311 if(GRAVITY_UNAFFECTED_BY_TICRATE)
314 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
316 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
321 self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
323 self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
327 self.move_angles = self.move_angles + self.move_avelocity * dt;
329 float movetime, bump;
331 for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
334 move = self.move_velocity * movetime;
335 _Movetype_PushEntity(move, true);
341 _Movetype_UnstickEntity();
342 _Movetype_PushEntity(move, false);
347 if(trace_fraction == 1)
350 movetime *= 1 - min(1, trace_fraction);
352 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
354 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
355 self.move_flags &= ~FL_ONGROUND;
357 else if(self.move_movetype == MOVETYPE_BOUNCE)
359 float d, bouncefac, bouncestop;
361 bouncefac = self.move_bounce_factor; if(!bouncefac) bouncefac = 0.5;
362 bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
364 bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
366 bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
368 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
370 d = trace_plane_normal * self.move_velocity;
371 if(trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
373 self.move_flags |= FL_ONGROUND;
374 self.move_groundentity = trace_ent;
375 self.move_velocity = '0 0 0';
376 self.move_avelocity = '0 0 0';
379 self.move_flags &= ~FL_ONGROUND;
383 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
384 if(trace_plane_normal.z > 0.7)
386 self.move_flags |= FL_ONGROUND;
387 self.move_groundentity = trace_ent;
388 if(trace_ent.solid == SOLID_BSP)
389 self.move_suspendedinair = true;
390 self.move_velocity = '0 0 0';
391 self.move_avelocity = '0 0 0';
394 self.move_flags &= ~FL_ONGROUND;
397 // DP revision 8905 (just, WHY...)
398 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
401 // DP revision 8918 (WHY...)
402 if(self.move_flags & FL_ONGROUND)
406 if(GRAVITY_UNAFFECTED_BY_TICRATE)
407 if(self.move_didgravity > 0)
408 if(!(self.move_flags & FL_ONGROUND))
411 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
413 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
416 _Movetype_CheckWaterTransition(self);
419 void _Movetype_Physics_Frame(float movedt)
421 self.move_didgravity = -1;
422 switch(self.move_movetype)
425 case MOVETYPE_FAKEPUSH:
426 error("SV_Physics_Pusher not implemented");
430 case MOVETYPE_FOLLOW:
431 error("SV_Physics_Follow not implemented");
433 case MOVETYPE_NOCLIP:
434 _Movetype_CheckWater(self);
435 self.move_origin = self.move_origin + ticrate * self.move_velocity;
436 self.move_angles = self.move_angles + ticrate * self.move_avelocity;
437 _Movetype_LinkEdict(false);
440 error("SV_Physics_Step not implemented");
443 error("SV_Physics_Walk not implemented");
446 case MOVETYPE_BOUNCE:
447 case MOVETYPE_BOUNCEMISSILE:
448 case MOVETYPE_FLYMISSILE:
450 _Movetype_Physics_Toss(movedt);
455 void Movetype_Physics_NoMatchServer() // optimized
459 movedt = time - self.move_time;
460 self.move_time = time;
462 _Movetype_Physics_Frame(movedt);
466 self.avelocity = self.move_avelocity;
467 self.velocity = self.move_velocity;
468 self.angles = self.move_angles;
469 setorigin(self, self.move_origin);
472 void Movetype_Physics_MatchServer(bool sloppy)
474 Movetype_Physics_MatchTicrate(ticrate, sloppy);
477 void Movetype_Physics_MatchTicrate(float tr, bool sloppy) // SV_Physics_Entity
479 float n, i, dt, movedt;
483 Movetype_Physics_NoMatchServer();
487 dt = time - self.move_time;
490 n = max(0, floor(dt / tr));
492 self.move_time += n * tr;
494 if(!self.move_didgravity)
495 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
497 for(i = 0; i < n; ++i)
499 _Movetype_Physics_Frame(movedt);
504 self.avelocity = self.move_avelocity;
506 if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
508 // now continue the move from move_time to time
509 self.velocity = self.move_velocity;
511 if(self.move_didgravity > 0)
513 if(GRAVITY_UNAFFECTED_BY_TICRATE)
516 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
518 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
523 self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
525 self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
529 self.angles = self.move_angles + dt * self.avelocity;
531 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
533 setorigin(self, self.move_origin + dt * self.velocity);
537 _Movetype_PushEntityTrace(dt * self.velocity);
538 if(!trace_startsolid)
539 setorigin(self, trace_endpos);
542 if(self.move_didgravity > 0)
544 if(GRAVITY_UNAFFECTED_BY_TICRATE)
547 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
549 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
555 self.velocity = self.move_velocity;
556 self.angles = self.move_angles;
557 setorigin(self, self.move_origin);