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 _Movetype_CheckWater(entity ent) // SV_CheckWater
19 point = ent.move_origin;
20 point_z += (ent.mins_z + 1);
22 nativecontents = pointcontents(point);
24 if(ent.move_watertype)
25 if(ent.move_watertype != nativecontents)
27 //print(sprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", ent.move_watertype, nativecontents));
28 if(ent.contentstransition)
29 ent.contentstransition(ent.move_watertype, nativecontents);
32 ent.move_waterlevel = 0;
33 ent.move_watertype = CONTENT_EMPTY;
35 supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents);
36 if(supercontents & DPCONTENTS_LIQUIDSMASK)
38 ent.move_watertype = nativecontents;
39 ent.move_waterlevel = 1;
40 point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
41 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
43 ent.move_waterlevel = 2;
44 point_y = ent.origin_y + ent.view_ofs_y;
45 if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
46 ent.move_waterlevel = 3;
50 return (ent.move_waterlevel > 1);
53 void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition
55 float contents = pointcontents(ent.move_origin);
57 if(!ent.move_watertype)
60 if(!autocvar_cl_gameplayfix_fixedcheckwatertransition)
62 ent.move_watertype = contents;
63 ent.move_waterlevel = 1;
67 else if(ent.move_watertype != contents)
69 //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));
70 if(ent.contentstransition)
71 ent.contentstransition(ent.move_watertype, contents);
74 if(contents <= CONTENT_WATER)
76 ent.move_watertype = contents;
77 ent.move_waterlevel = 1;
81 ent.move_watertype = CONTENT_EMPTY;
82 ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents);
86 void _Movetype_Impact(entity oth) // SV_Impact
88 entity oldother, oldself;
114 void _Movetype_LinkEdict_TouchAreaGrid() // SV_LinkEdict_TouchAreaGrid
116 entity e, oldself, oldother;
121 for(e = findradius(0.5 * (self.absmin + self.absmax), 0.5 * vlen(self.absmax - self.absmin)); e; e = e.chain)
124 if(boxesoverlap(e.absmin, e.absmax, oldself.absmin, oldself.absmax))
129 trace_allsolid = FALSE;
130 trace_startsolid = FALSE;
132 trace_inwater = FALSE;
134 trace_endpos = e.origin;
135 trace_plane_normal = '0 0 1';
136 trace_plane_dist = 0;
147 void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict
150 if(self.solid == SOLID_BSP)
152 // TODO set the absolute bbox
161 mi = mi + self.origin;
162 ma = ma + self.origin;
164 if(self.move_flags & FL_ITEM)
187 _Movetype_LinkEdict_TouchAreaGrid();
190 float _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
194 org = self.move_origin + ofs;
196 cont = self.dphitcontentsmask;
197 self.dphitcontentsmask = DPCONTENTS_SOLID;
198 tracebox(self.move_origin, self.mins, self.maxs, self.move_origin, MOVE_NOMONSTERS, self);
199 self.dphitcontentsmask = cont;
204 if(vlen(trace_endpos - self.move_origin) > 0.0001)
205 self.move_origin = trace_endpos;
209 float _Movetype_UnstickEntity() // SV_UnstickEntity
211 if(!_Movetype_TestEntityPosition('0 0 0'))
213 if(!_Movetype_TestEntityPosition('-1 0 0')) goto success;
214 if(!_Movetype_TestEntityPosition('1 0 0')) goto success;
215 if(!_Movetype_TestEntityPosition('0 -1 0')) goto success;
216 if(!_Movetype_TestEntityPosition('0 1 0')) goto success;
217 if(!_Movetype_TestEntityPosition('-1 -1 0')) goto success;
218 if(!_Movetype_TestEntityPosition('1 -1 0')) goto success;
219 if(!_Movetype_TestEntityPosition('-1 1 0')) goto success;
220 if(!_Movetype_TestEntityPosition('1 1 0')) goto success;
222 for(i = 1; i <= 17; ++i)
224 if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success;
225 if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success;
227 dprintf(_("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin));
230 dprintf(_("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin));
231 _Movetype_LinkEdict(TRUE);
235 vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
237 vel = vel - ((vel * norm) * norm) * f;
239 if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
240 if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
241 if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
246 void _Movetype_PushEntityTrace(vector push)
251 end = self.move_origin + push;
253 if(self.move_nomonsters)
254 type = max(0, self.move_nomonsters);
255 else if(self.move_movetype == MOVETYPE_FLYMISSILE)
257 else if(self.solid == SOLID_TRIGGER || self.solid == SOLID_NOT)
258 type = MOVE_NOMONSTERS;
262 tracebox(self.move_origin, self.mins, self.maxs, end, type, self);
265 float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity
267 _Movetype_PushEntityTrace(push);
269 if(trace_startsolid && failonstartsolid)
270 return trace_fraction;
272 self.move_origin = trace_endpos;
274 if(trace_fraction < 1)
275 if(self.solid >= SOLID_TRIGGER && (!(self.move_flags & FL_ONGROUND) || (self.move_groundentity != trace_ent)))
276 _Movetype_Impact(trace_ent);
278 return trace_fraction;
281 #define MAX_CLIP_PLANES 5
282 void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
284 if(self.move_flags & FL_ONGROUND)
286 if(self.move_velocity_z >= 1/32)
287 self.move_flags &= ~FL_ONGROUND;
288 else if(!self.move_groundentity)
290 else if(self.move_suspendedinair && wasfreed(self.move_groundentity))
292 self.move_groundentity = world;
297 self.move_suspendedinair = FALSE;
299 _Movetype_CheckVelocity();
301 if(self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS)
303 self.move_didgravity = 1;
304 if(GRAVITY_UNAFFECTED_BY_TICRATE)
307 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
309 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
314 self.move_velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
316 self.move_velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
320 self.move_angles = self.move_angles + self.move_avelocity * dt;
322 float movetime, bump;
324 for(bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump)
327 move = self.move_velocity * movetime;
328 _Movetype_PushEntity(move, TRUE);
334 _Movetype_UnstickEntity();
335 _Movetype_PushEntity(move, FALSE);
340 if(trace_fraction == 1)
343 movetime *= 1 - min(1, trace_fraction);
345 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
347 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 2.0);
348 self.move_flags &= ~FL_ONGROUND;
350 else if(self.move_movetype == MOVETYPE_BOUNCE)
352 float d, bouncefac, bouncestop;
354 bouncefac = self.move_bounce_factor; if(!bouncefac) bouncefac = 0.5;
355 bouncestop = self.move_bounce_stopspeed; if(!bouncestop) bouncestop = 60 / 800;
357 bouncestop *= self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
359 bouncestop *= getstatf(STAT_MOVEVARS_GRAVITY);
361 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
363 d = trace_plane_normal * self.move_velocity;
364 if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
366 self.move_flags |= FL_ONGROUND;
367 self.move_groundentity = trace_ent;
368 self.move_velocity = '0 0 0';
369 self.move_avelocity = '0 0 0';
372 self.move_flags &= ~FL_ONGROUND;
376 self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
377 if(trace_plane_normal_z > 0.7)
379 self.move_flags |= FL_ONGROUND;
380 self.move_groundentity = trace_ent;
381 if(trace_ent.solid == SOLID_BSP)
382 self.move_suspendedinair = TRUE;
383 self.move_velocity = '0 0 0';
384 self.move_avelocity = '0 0 0';
387 self.move_flags &= ~FL_ONGROUND;
390 // DP revision 8905 (just, WHY...)
391 if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
394 // DP revision 8918 (WHY...)
395 if(self.move_flags & FL_ONGROUND)
399 if(GRAVITY_UNAFFECTED_BY_TICRATE)
400 if(self.move_didgravity > 0)
401 if(!(self.move_flags & FL_ONGROUND))
404 self.move_velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
406 self.move_velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
409 _Movetype_CheckWaterTransition(self);
412 void _Movetype_Physics_Frame(float movedt)
414 self.move_didgravity = -1;
415 switch(self.move_movetype)
418 case MOVETYPE_FAKEPUSH:
419 error("SV_Physics_Pusher not implemented");
423 case MOVETYPE_FOLLOW:
424 error("SV_Physics_Follow not implemented");
426 case MOVETYPE_NOCLIP:
427 _Movetype_CheckWater(self);
428 self.move_origin = self.move_origin + ticrate * self.move_velocity;
429 self.move_angles = self.move_angles + ticrate * self.move_avelocity;
430 _Movetype_LinkEdict(FALSE);
433 error("SV_Physics_Step not implemented");
436 error("SV_Physics_Walk not implemented");
439 case MOVETYPE_BOUNCE:
440 case MOVETYPE_BOUNCEMISSILE:
441 case MOVETYPE_FLYMISSILE:
443 _Movetype_Physics_Toss(movedt);
448 void Movetype_Physics_NoMatchServer() // optimized
452 movedt = time - self.move_time;
453 self.move_time = time;
455 _Movetype_Physics_Frame(movedt);
459 self.avelocity = self.move_avelocity;
460 self.velocity = self.move_velocity;
461 self.angles = self.move_angles;
462 setorigin(self, self.move_origin);
465 void Movetype_Physics_MatchServer(float sloppy)
467 Movetype_Physics_MatchTicrate(ticrate, sloppy);
470 void Movetype_Physics_MatchTicrate(float tr, float sloppy) // SV_Physics_Entity
472 float n, i, dt, movedt;
476 Movetype_Physics_NoMatchServer();
480 dt = time - self.move_time;
483 n = max(0, floor(dt / tr));
485 self.move_time += n * tr;
487 if(!self.move_didgravity)
488 self.move_didgravity = ((self.move_movetype == MOVETYPE_BOUNCE || self.move_movetype == MOVETYPE_TOSS) && !(self.move_flags & FL_ONGROUND));
490 for(i = 0; i < n; ++i)
492 _Movetype_Physics_Frame(movedt);
497 self.avelocity = self.move_avelocity;
499 if(dt > 0 && self.move_movetype != MOVETYPE_NONE && !(self.move_flags & FL_ONGROUND))
501 // now continue the move from move_time to time
502 self.velocity = self.move_velocity;
504 if(self.move_didgravity > 0)
506 if(GRAVITY_UNAFFECTED_BY_TICRATE)
509 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
511 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
516 self.velocity_z -= dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
518 self.velocity_z -= dt * getstatf(STAT_MOVEVARS_GRAVITY);
522 self.angles = self.move_angles + dt * self.avelocity;
524 if(sloppy || self.movetype == MOVETYPE_NOCLIP)
526 setorigin(self, self.move_origin + dt * self.velocity);
530 _Movetype_PushEntityTrace(dt * self.velocity);
531 if(!trace_startsolid)
532 setorigin(self, trace_endpos);
535 if(self.move_didgravity > 0)
537 if(GRAVITY_UNAFFECTED_BY_TICRATE)
540 self.velocity_z -= 0.5 * dt * self.gravity * getstatf(STAT_MOVEVARS_GRAVITY);
542 self.velocity_z -= 0.5 * dt * getstatf(STAT_MOVEVARS_GRAVITY);
548 self.velocity = self.move_velocity;
549 self.angles = self.move_angles;
550 setorigin(self, self.move_origin);