1 const float STAT_MOVEFLAGS = 225;
2 const float MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
3 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
5 .entity move_groundentity; // FIXME add move_groundnetworkentity?
6 .float move_suspendedinair;
7 .float move_didgravity;
9 void _Movetype_CheckVelocity() // SV_CheckVelocity
13 float Mod_Q1BSP_SuperContentsFromNativeContents(float nativecontents)
15 switch(nativecontents)
20 return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE;
22 return DPCONTENTS_WATER;
24 return DPCONTENTS_SLIME;
26 return DPCONTENTS_LAVA | DPCONTENTS_NODROP;
28 return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
33 float Mod_Q1BSP_NativeContentsFromSuperContents(float supercontents)
35 if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
37 if(supercontents & DPCONTENTS_SKY)
39 if(supercontents & DPCONTENTS_LAVA)
41 if(supercontents & DPCONTENTS_SLIME)
43 if(supercontents & DPCONTENTS_WATER)
48 float _Movetype_CheckWater(entity ent) // SV_CheckWater
54 point = ent.move_origin;
55 point_z += (ent.mins_z + 1);
57 nativecontents = pointcontents(point);
59 if(ent.move_watertype)
60 if(ent.move_watertype != nativecontents)
62 if(ent.contentstransition)
63 ent.contentstransition(ent.move_watertype, nativecontents);
66 ent.move_waterlevel = 0;
67 ent.move_watertype = CONTENT_EMPTY;
69 supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
70 if(supercontents & DPCONTENTS_LIQUIDSMASK)
72 ent.move_watertype = nativecontents;
73 ent.move_waterlevel = 1;
74 point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
75 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
77 ent.move_waterlevel = 2;
78 point_y = ent.origin_y + ent.view_ofs_y;
79 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
80 ent.move_waterlevel = 3;
84 return (ent.move_waterlevel > 1);
87 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
89 float contents = pointcontents(ent.move_origin);
91 if(!ent.move_watertype)
94 if(!autocvar_sv_gameplayfix_fixedcheckwatertransition)
96 ent.move_watertype = contents;
97 ent.move_waterlevel = 1;
101 else if(ent.move_watertype != contents)
103 if(ent.contentstransition)
104 ent.contentstransition(ent.move_watertype, contents);
107 if(contents <= CONTENT_WATER)
109 ent.move_watertype = contents;
110 ent.move_waterlevel = 1;
114 ent.move_watertype = CONTENT_EMPTY;
115 ent.move_waterlevel = (autocvar_sv_gameplayfix_fixedcheckwatertransition ? 0 : contents);
119 void _Movetype_Impact(entity oth) // SV_Impact
121 entity oldother, oldself;
147 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
149 entity e, oldself, oldother;
154 for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
157 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
162 trace_allsolid = FALSE;
163 trace_startsolid = FALSE;
165 trace_inwater = FALSE;
167 trace_endpos = e.origin;
168 trace_plane_normal = '0 0 1';
169 trace_plane_dist = 0;
180 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
183 if(self.solid == SOLID_BSP)
185 // TODO set the absolute bbox
194 mi = mi + self.origin;
195 ma = ma + self.origin;
197 if(self.move_flags & FL_ITEM)
220 _Movetype_LinkEdict_TouchAreaGrid();
223 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
227 org = self.move_origin + ofs;
229 cont = self.dphitcontentsmask;
230 self.dphitcontentsmask = DPCONTENTS_SOLID;
231 tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
232 self.dphitcontentsmask = cont;
237 if(vlen(trace_endpos - self.move_origin) > 0.0001)
238 self.move_origin = trace_endpos;
242 float _Movetype_UnstickEntity() // SV_UnstickEntity
244 if(!_Movetype_TestEntityPosition('0 0 0'))
246 if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
247 if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
248 if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
249 if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
250 if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
251 if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
252 if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
253 if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
255 for(i = 1; i <= 17; ++i)
257 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
258 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
260 dprint(sprintf(_("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)));
263 dprint(sprintf(_("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)));
264 _Movetype_LinkEdict(TRUE);
268 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
270 vel = vel - ((vel * norm) * norm) * f;
272 if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
273 if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
274 if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
279 void _Movetype_PushEntityTrace(vector push)
284 end = self.move_origin + push;
286 if(self.move_nomonsters)
287 type = max(0, self.move_nomonsters);
288 else if(self.move_movetype == MOVETYPE_FLYMISSILE)
290 else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
291 type = MOVE_NOMONSTERS;
295 tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
298 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
300 _Movetype_PushEntityTrace(push);
302 if(trace_startsolid && failonstartsolid)
303 return trace_fraction;
305 self.move_origin = trace_endpos;
307 if(trace_fraction < 1)
308 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
309 _Movetype_Impact(trace_ent);
311 return trace_fraction;
314 #define MAX_CLIP_PLANES 5
315 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
317 if(self.move_flags & FL_ONGROUND)
319 if(self.move_velocity_z >= 1/32)
320 self.move_flags &= ~FL_ONGROUND;
321 else if(!self.move_groundentity)
323 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
325 self.move_groundentity = world;
330 self.move_suspendedinair = FALSE;
332 _Movetype_CheckVelocity();
334 if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
336 self.move_didgravity = 1;
337 if(GRAVITY_UNAFFECTED_BY_TICRATE)
340 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
342 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
347 self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
349 self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
353 self.move_angles = self.move_angles + self.move_avelocity * dt;
355 float movetime, bump;
357 for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
360 move = self.move_velocity * movetime;
361 _Movetype_PushEntity(move, TRUE);
367 _Movetype_UnstickEntity();
368 _Movetype_PushEntity(move, FALSE);
373 if(trace_fraction == 1)
376 movetime *= 1 - min(1, trace_fraction);
378 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
380 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
381 self.move_flags &= ~FL_ONGROUND;
383 else if(self.move_movetype == MOVETYPE_BOUNCE)
385 float d, bouncefac, bouncestop;
387 bouncefac = self.move_bounce_factor; if(!bouncefac) bouncefac = 0.5;
388 bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
390 bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
392 bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
394 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
396 d = trace_plane_normal * self.move_velocity;
397 if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
399 self.move_flags |= FL_ONGROUND;
400 self.move_groundentity = trace_ent;
401 self.move_velocity = '0 0 0';
402 self.move_avelocity = '0 0 0';
405 self.move_flags &= ~FL_ONGROUND;
409 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
410 if(trace_plane_normal_z > 0.7)
412 self.move_flags |= FL_ONGROUND;
413 self.move_groundentity = trace_ent;
414 if(trace_ent.solid == SOLID_BSP)
415 self.move_suspendedinair = TRUE;
416 self.move_velocity = '0 0 0';
417 self.move_avelocity = '0 0 0';
420 self.move_flags &= ~FL_ONGROUND;
423 // DP revision 8905 (just, WHY...)
424 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
427 // DP revision 8918 (WHY...)
428 if(self.move_flags & FL_ONGROUND)
432 if(GRAVITY_UNAFFECTED_BY_TICRATE)
433 if(self.move_didgravity > 0)
434 if(!(self.move_flags & FL_ONGROUND))
437 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
439 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
442 _Movetype_CheckWaterTransition(self);
445 void _Movetype_Physics_Frame(float movedt)
447 self.move_didgravity = -1;
448 switch(self.move_movetype)
451 case MOVETYPE_FAKEPUSH:
452 error("SV_Physics_Pusher not implemented");
456 case MOVETYPE_FOLLOW:
457 error("SV_Physics_Follow not implemented");
459 case MOVETYPE_NOCLIP:
460 _Movetype_CheckWater(self);
461 self.move_origin = self.move_origin + ticrate * self.move_velocity;
462 self.move_angles = self.move_angles + ticrate * self.move_avelocity;
463 _Movetype_LinkEdict(FALSE);
466 error("SV_Physics_Step not implemented");
469 error("SV_Physics_Walk not implemented");
472 case MOVETYPE_BOUNCE:
473 case MOVETYPE_BOUNCEMISSILE:
474 case MOVETYPE_FLYMISSILE:
476 _Movetype_Physics_Toss(movedt);
481 void Movetype_Physics_NoMatchServer() // optimized
485 movedt = time - self.move_time;
486 self.move_time = time;
488 _Movetype_Physics_Frame(movedt);
492 self.avelocity = self.move_avelocity;
493 self.velocity = self.move_velocity;
494 self.angles = self.move_angles;
495 setorigin(self, self.move_origin);
498 void Movetype_Physics_MatchServer(float sloppy)
500 Movetype_Physics_MatchTicrate(ticrate, sloppy);
503 void Movetype_Physics_MatchTicrate(float tr, float sloppy) // SV_Physics_Entity
505 float n, i, dt, movedt;
509 Movetype_Physics_NoMatchServer();
513 dt = time - self.move_time;
516 n = max(0, floor(dt / tr));
518 self.move_time += n * tr;
520 if(!self.move_didgravity)
521 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
523 for(i = 0; i < n; ++i)
525 _Movetype_Physics_Frame(movedt);
530 self.avelocity = self.move_avelocity;
532 if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
534 // now continue the move from move_time to time
535 self.velocity = self.move_velocity;
537 if(self.move_didgravity > 0)
539 if(GRAVITY_UNAFFECTED_BY_TICRATE)
542 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
544 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
549 self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
551 self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
555 self.angles = self.move_angles + dt * self.avelocity;
557 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
559 setorigin(self, self.move_origin + dt * self.velocity);
563 _Movetype_PushEntityTrace(dt * self.velocity);
564 if(!trace_startsolid)
565 setorigin(self, trace_endpos);
568 if(self.move_didgravity > 0)
570 if(GRAVITY_UNAFFECTED_BY_TICRATE)
573 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
575 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
581 self.velocity = self.move_velocity;
582 self.angles = self.move_angles;
583 setorigin(self, self.move_origin);