]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
e505b28949676e0c6c359d6dc7f5ddeef6369156
[xonotic/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
52 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
53
54 #define MOVE_EPSILON    0.01
55
56 void SV_Physics_Toss (prvm_edict_t *ent);
57
58 void SV_Phys_Init (void)
59 {
60         Cvar_RegisterVariable(&sv_stepheight);
61         Cvar_RegisterVariable(&sv_jumpstep);
62         Cvar_RegisterVariable(&sv_wallfriction);
63         Cvar_RegisterVariable(&sv_newflymove);
64         Cvar_RegisterVariable(&sv_freezenonclients);
65
66         Cvar_RegisterVariable(&sv_playerphysicsqc);
67 }
68
69 /*
70 ================
71 SV_CheckAllEnts
72 ================
73 */
74 void SV_CheckAllEnts (void)
75 {
76         int e;
77         prvm_edict_t *check;
78
79         // see if any solid entities are inside the final position
80         check = PRVM_NEXT_EDICT(prog->edicts);
81         for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
82         {
83                 if (check->priv.server->free)
84                         continue;
85                 if (check->fields.server->movetype == MOVETYPE_PUSH
86                  || check->fields.server->movetype == MOVETYPE_NONE
87                  || check->fields.server->movetype == MOVETYPE_FOLLOW
88                  || check->fields.server->movetype == MOVETYPE_NOCLIP)
89                         continue;
90
91                 if (SV_TestEntityPosition (check))
92                         Con_Print("entity in invalid position\n");
93         }
94 }
95
96 /*
97 ================
98 SV_CheckVelocity
99 ================
100 */
101 void SV_CheckVelocity (prvm_edict_t *ent)
102 {
103         int i;
104         float wishspeed;
105
106 //
107 // bound velocity
108 //
109         for (i=0 ; i<3 ; i++)
110         {
111                 if (IS_NAN(ent->fields.server->velocity[i]))
112                 {
113                         Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
114                         ent->fields.server->velocity[i] = 0;
115                 }
116                 if (IS_NAN(ent->fields.server->origin[i]))
117                 {
118                         Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
119                         ent->fields.server->origin[i] = 0;
120                 }
121         }
122
123         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
124         wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
125         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
126         {
127                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
128                 ent->fields.server->velocity[0] *= wishspeed;
129                 ent->fields.server->velocity[1] *= wishspeed;
130                 ent->fields.server->velocity[2] *= wishspeed;
131         }
132 }
133
134 /*
135 =============
136 SV_RunThink
137
138 Runs thinking code if time.  There is some play in the exact time the think
139 function will be called, because it is called before any movement is done
140 in a frame.  Not used for pushmove objects, because they must be exact.
141 Returns false if the entity removed itself.
142 =============
143 */
144 qboolean SV_RunThink (prvm_edict_t *ent)
145 {
146         float thinktime;
147
148         thinktime = ent->fields.server->nextthink;
149         if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
150                 return true;
151
152         // don't let things stay in the past.
153         // it is possible to start that way by a trigger with a local time.
154         if (thinktime < sv.time)
155                 thinktime = sv.time;
156
157         ent->fields.server->nextthink = 0;
158         prog->globals.server->time = thinktime;
159         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
160         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
161         PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
162         return !ent->priv.server->free;
163 }
164
165 /*
166 ==================
167 SV_Impact
168
169 Two entities have touched, so run their touch functions
170 ==================
171 */
172 void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
173 {
174         int old_self, old_other;
175
176         old_self = prog->globals.server->self;
177         old_other = prog->globals.server->other;
178
179         prog->globals.server->time = sv.time;
180         if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
181         {
182                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
183                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
184                 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
185         }
186
187         if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
188         {
189                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
190                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
191                 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
192         }
193
194         prog->globals.server->self = old_self;
195         prog->globals.server->other = old_other;
196 }
197
198
199 /*
200 ==================
201 ClipVelocity
202
203 Slide off of the impacting object
204 returns the blocked flags (1 = floor, 2 = step / wall)
205 ==================
206 */
207 #define STOP_EPSILON 0.1
208 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
209 {
210         int i;
211         float backoff;
212
213         backoff = -DotProduct (in, normal) * overbounce;
214         VectorMA(in, backoff, normal, out);
215
216         for (i = 0;i < 3;i++)
217                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
218                         out[i] = 0;
219 }
220
221
222 /*
223 ============
224 SV_FlyMove
225
226 The basic solid body movement clip that slides along multiple planes
227 Returns the clipflags if the velocity was modified (hit something solid)
228 1 = floor
229 2 = wall / step
230 4 = dead stop
231 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
232 ============
233 */
234 // LordHavoc: increased from 5 to 32
235 #define MAX_CLIP_PLANES 32
236 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
237 {
238         int blocked, bumpcount;
239         int i, j, impact, numplanes;
240         float d, time_left;
241         vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
242         trace_t trace;
243         blocked = 0;
244         VectorCopy(ent->fields.server->velocity, original_velocity);
245         VectorCopy(ent->fields.server->velocity, primal_velocity);
246         numplanes = 0;
247         time_left = time;
248         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
249         {
250                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
251                         break;
252
253                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
254                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
255 #if 0
256                 //if (trace.fraction < 0.002)
257                 {
258 #if 1
259                         vec3_t start;
260                         trace_t testtrace;
261                         VectorCopy(ent->fields.server->origin, start);
262                         start[2] += 3;//0.03125;
263                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
264                         end[2] += 3;//0.03125;
265                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
266                         if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
267                         {
268                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
269                                 trace = testtrace;
270                         }
271 #endif
272 #if 0
273                         //j = -1;
274                         for (i = 0;i < numplanes;i++)
275                         {
276                                 VectorCopy(ent->fields.server->origin, start);
277                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
278                                 VectorMA(start, 3, planes[i], start);
279                                 VectorMA(end, 3, planes[i], end);
280                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
281                                 if (trace.fraction < testtrace.fraction)
282                                 {
283                                         trace = testtrace;
284                                         VectorCopy(start, ent->fields.server->origin);
285                                         //j = i;
286                                 }
287                         }
288                         //if (j >= 0)
289                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
290 #endif
291                 }
292 #endif
293
294 #if 0
295                 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
296                 if (trace.fraction < 1)
297                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
298                 Con_Print("\n");
299 #endif
300
301                 /*
302                 if (trace.startsolid)
303                 {
304                         // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
305                         // entity is trapped in another solid
306                         VectorClear(ent->fields.server->velocity);
307                         return 3;
308                 }
309                 */
310
311                 // break if it moved the entire distance
312                 if (trace.fraction == 1)
313                 {
314                         VectorCopy(trace.endpos, ent->fields.server->origin);
315                         break;
316                 }
317
318                 if (!trace.ent)
319                 {
320                         Con_Printf ("SV_FlyMove: !trace.ent");
321                         trace.ent = prog->edicts;
322                 }
323
324                 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
325                         impact = false;
326                 else
327                 {
328                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
329                         impact = true;
330                 }
331
332                 if (trace.plane.normal[2])
333                 {
334                         if (trace.plane.normal[2] > 0.7)
335                         {
336                                 // floor
337                                 blocked |= 1;
338                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
339                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
340                         }
341                 }
342                 else
343                 {
344                         // step
345                         blocked |= 2;
346                         // save the trace for player extrafriction
347                         if (stepnormal)
348                                 VectorCopy(trace.plane.normal, stepnormal);
349                 }
350
351                 if (trace.fraction >= 0.001)
352                 {
353                         // actually covered some distance
354                         VectorCopy(trace.endpos, ent->fields.server->origin);
355                         VectorCopy(ent->fields.server->velocity, original_velocity);
356                         numplanes = 0;
357                 }
358
359                 // run the impact function
360                 if (impact)
361                 {
362                         SV_Impact(ent, (prvm_edict_t *)trace.ent);
363
364                         // break if removed by the impact function
365                         if (ent->priv.server->free)
366                                 break;
367                 }
368
369                 time_left *= 1 - trace.fraction;
370
371                 // clipped to another plane
372                 if (numplanes >= MAX_CLIP_PLANES)
373                 {
374                         // this shouldn't really happen
375                         VectorClear(ent->fields.server->velocity);
376                         blocked = 3;
377                         break;
378                 }
379
380                 /*
381                 for (i = 0;i < numplanes;i++)
382                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
383                                 break;
384                 if (i < numplanes)
385                 {
386                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
387                         continue;
388                 }
389                 */
390
391                 VectorCopy(trace.plane.normal, planes[numplanes]);
392                 numplanes++;
393
394                 if (sv_newflymove.integer)
395                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
396                 else
397                 {
398                         // modify original_velocity so it parallels all of the clip planes
399                         for (i = 0;i < numplanes;i++)
400                         {
401                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
402                                 for (j = 0;j < numplanes;j++)
403                                 {
404                                         if (j != i)
405                                         {
406                                                 // not ok
407                                                 if (DotProduct(new_velocity, planes[j]) < 0)
408                                                         break;
409                                         }
410                                 }
411                                 if (j == numplanes)
412                                         break;
413                         }
414
415                         if (i != numplanes)
416                         {
417                                 // go along this plane
418                                 VectorCopy(new_velocity, ent->fields.server->velocity);
419                         }
420                         else
421                         {
422                                 // go along the crease
423                                 if (numplanes != 2)
424                                 {
425                                         VectorClear(ent->fields.server->velocity);
426                                         blocked = 7;
427                                         break;
428                                 }
429                                 CrossProduct(planes[0], planes[1], dir);
430                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
431                                 VectorNormalize(dir);
432                                 d = DotProduct(dir, ent->fields.server->velocity);
433                                 VectorScale(dir, d, ent->fields.server->velocity);
434                         }
435                 }
436
437                 // if current velocity is against the original velocity,
438                 // stop dead to avoid tiny occilations in sloping corners
439                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
440                 {
441                         VectorClear(ent->fields.server->velocity);
442                         break;
443                 }
444         }
445
446         //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
447
448         /*
449         if ((blocked & 1) == 0 && bumpcount > 1)
450         {
451                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
452                 // flag ONGROUND if there's ground under it
453                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
454         }
455         */
456         return blocked;
457 }
458
459 /*
460 ============
461 SV_AddGravity
462
463 ============
464 */
465 void SV_AddGravity (prvm_edict_t *ent)
466 {
467         float ent_gravity;
468         prvm_eval_t *val;
469
470         val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
471         if (val!=0 && val->_float)
472                 ent_gravity = val->_float;
473         else
474                 ent_gravity = 1.0;
475         ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
476 }
477
478
479 /*
480 ===============================================================================
481
482 PUSHMOVE
483
484 ===============================================================================
485 */
486
487 /*
488 ============
489 SV_PushEntity
490
491 Does not change the entities velocity at all
492 ============
493 */
494 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
495 {
496         int type;
497         trace_t trace;
498         vec3_t end;
499
500         VectorAdd (ent->fields.server->origin, push, end);
501
502         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
503                 type = MOVE_MISSILE;
504         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
505                 type = MOVE_NOMONSTERS; // only clip against bmodels
506         else
507                 type = MOVE_NORMAL;
508
509         trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
510
511         VectorCopy (trace.endpos, ent->fields.server->origin);
512         SV_LinkEdict (ent, true);
513
514         if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
515                 SV_Impact (ent, (prvm_edict_t *)trace.ent);
516         return trace;
517 }
518
519
520 /*
521 ============
522 SV_PushMove
523
524 ============
525 */
526 void SV_PushMove (prvm_edict_t *pusher, float movetime)
527 {
528         int i, e, index;
529         float savesolid, movetime2, pushltime;
530         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
531         int num_moved;
532         int numcheckentities;
533         static prvm_edict_t *checkentities[MAX_EDICTS];
534         model_t *pushermodel;
535         trace_t trace;
536
537         if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
538         {
539                 pusher->fields.server->ltime += movetime;
540                 return;
541         }
542
543         switch ((int) pusher->fields.server->solid)
544         {
545         // LordHavoc: valid pusher types
546         case SOLID_BSP:
547         case SOLID_BBOX:
548         case SOLID_SLIDEBOX:
549         case SOLID_CORPSE: // LordHavoc: this would be weird...
550                 break;
551         // LordHavoc: no collisions
552         case SOLID_NOT:
553         case SOLID_TRIGGER:
554                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
555                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
556                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
557                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
558                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
559                 pusher->fields.server->ltime += movetime;
560                 SV_LinkEdict (pusher, false);
561                 return;
562         default:
563                 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
564                 return;
565         }
566         index = (int) pusher->fields.server->modelindex;
567         if (index < 1 || index >= MAX_MODELS)
568         {
569                 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
570                 return;
571         }
572         pushermodel = sv.models[index];
573
574         movetime2 = movetime;
575         VectorScale(pusher->fields.server->velocity, movetime2, move1);
576         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
577         if (moveangle[0] || moveangle[2])
578         {
579                 for (i = 0;i < 3;i++)
580                 {
581                         if (move1[i] > 0)
582                         {
583                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
584                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
585                         }
586                         else
587                         {
588                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
589                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
590                         }
591                 }
592         }
593         else if (moveangle[1])
594         {
595                 for (i = 0;i < 3;i++)
596                 {
597                         if (move1[i] > 0)
598                         {
599                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
600                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
601                         }
602                         else
603                         {
604                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
605                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
606                         }
607                 }
608         }
609         else
610         {
611                 for (i = 0;i < 3;i++)
612                 {
613                         if (move1[i] > 0)
614                         {
615                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
616                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
617                         }
618                         else
619                         {
620                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
621                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
622                         }
623                 }
624         }
625
626         VectorNegate (moveangle, a);
627         AngleVectorsFLU (a, forward, left, up);
628
629         VectorCopy (pusher->fields.server->origin, pushorig);
630         VectorCopy (pusher->fields.server->angles, pushang);
631         pushltime = pusher->fields.server->ltime;
632
633 // move the pusher to its final position
634
635         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
636         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
637         pusher->fields.server->ltime += movetime;
638         SV_LinkEdict (pusher, false);
639
640         savesolid = pusher->fields.server->solid;
641
642 // see if any solid entities are inside the final position
643         num_moved = 0;
644
645         numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
646         for (e = 0;e < numcheckentities;e++)
647         {
648                 prvm_edict_t *check = checkentities[e];
649                 if (check->fields.server->movetype == MOVETYPE_NONE
650                  || check->fields.server->movetype == MOVETYPE_PUSH
651                  || check->fields.server->movetype == MOVETYPE_FOLLOW
652                  || check->fields.server->movetype == MOVETYPE_NOCLIP
653                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
654                         continue;
655
656                 // if the entity is standing on the pusher, it will definitely be moved
657                 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
658                 {
659                         // if the entity is not inside the pusher's final position, leave it alone
660                         if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
661                                 continue;
662                         // remove the onground flag for non-players
663                         if (check->fields.server->movetype != MOVETYPE_WALK)
664                                 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
665                 }
666
667
668                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
669                 {
670                         vec3_t org2;
671                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
672                         org2[0] = DotProduct (org, forward);
673                         org2[1] = DotProduct (org, left);
674                         org2[2] = DotProduct (org, up);
675                         VectorSubtract (org2, org, move);
676                         VectorAdd (move, move1, move);
677                 }
678                 else
679                         VectorCopy (move1, move);
680
681                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
682                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
683                 sv.moved_edicts[num_moved++] = check;
684
685                 // try moving the contacted entity
686                 pusher->fields.server->solid = SOLID_NOT;
687                 trace = SV_PushEntity (check, move);
688                 // FIXME: turn players specially
689                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
690                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
691
692                 // if it is still inside the pusher, block
693                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
694                 {
695                         // try moving the contacted entity a tiny bit further to account for precision errors
696                         vec3_t move2;
697                         pusher->fields.server->solid = SOLID_NOT;
698                         VectorScale(move, 1.1, move2);
699                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
700                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
701                         SV_PushEntity (check, move2);
702                         pusher->fields.server->solid = savesolid;
703                         if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
704                         {
705                                 // try moving the contacted entity a tiny bit less to account for precision errors
706                                 pusher->fields.server->solid = SOLID_NOT;
707                                 VectorScale(move, 0.9, move2);
708                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
709                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
710                                 SV_PushEntity (check, move2);
711                                 pusher->fields.server->solid = savesolid;
712                                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
713                                 {
714                                         // still inside pusher, so it's really blocked
715
716                                         // fail the move
717                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
718                                                 continue;
719                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
720                                         {
721                                                 // corpse
722                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
723                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
724                                                 continue;
725                                         }
726
727                                         VectorCopy (pushorig, pusher->fields.server->origin);
728                                         VectorCopy (pushang, pusher->fields.server->angles);
729                                         pusher->fields.server->ltime = pushltime;
730                                         SV_LinkEdict (pusher, false);
731
732                                         // move back any entities we already moved
733                                         for (i = 0;i < num_moved;i++)
734                                         {
735                                                 prvm_edict_t *ed = sv.moved_edicts[i];
736                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
737                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
738                                                 SV_LinkEdict (ed, false);
739                                         }
740
741                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
742                                         if (pusher->fields.server->blocked)
743                                         {
744                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
745                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
746                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
747                                         }
748                                         break;
749                                 }
750                         }
751                 }
752         }
753         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
754         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
755         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
756 }
757
758 /*
759 ================
760 SV_Physics_Pusher
761
762 ================
763 */
764 void SV_Physics_Pusher (prvm_edict_t *ent)
765 {
766         float thinktime, oldltime, movetime;
767
768         oldltime = ent->fields.server->ltime;
769
770         thinktime = ent->fields.server->nextthink;
771         if (thinktime < ent->fields.server->ltime + sv.frametime)
772         {
773                 movetime = thinktime - ent->fields.server->ltime;
774                 if (movetime < 0)
775                         movetime = 0;
776         }
777         else
778                 movetime = sv.frametime;
779
780         if (movetime)
781                 // advances ent->fields.server->ltime if not blocked
782                 SV_PushMove (ent, movetime);
783
784         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
785         {
786                 ent->fields.server->nextthink = 0;
787                 prog->globals.server->time = sv.time;
788                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
789                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
790                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
791         }
792 }
793
794
795 /*
796 ===============================================================================
797
798 CLIENT MOVEMENT
799
800 ===============================================================================
801 */
802
803 /*
804 =============
805 SV_CheckStuck
806
807 This is a big hack to try and fix the rare case of getting stuck in the world
808 clipping hull.
809 =============
810 */
811 void SV_CheckStuck (prvm_edict_t *ent)
812 {
813         int i, j, z;
814         vec3_t org;
815
816         if (!SV_TestEntityPosition(ent))
817         {
818                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
819                 return;
820         }
821
822         VectorCopy (ent->fields.server->origin, org);
823         VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
824         if (!SV_TestEntityPosition(ent))
825         {
826                 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
827                 SV_LinkEdict (ent, true);
828                 return;
829         }
830
831         for (z=0 ; z< 18 ; z++)
832                 for (i=-1 ; i <= 1 ; i++)
833                         for (j=-1 ; j <= 1 ; j++)
834                         {
835                                 ent->fields.server->origin[0] = org[0] + i;
836                                 ent->fields.server->origin[1] = org[1] + j;
837                                 ent->fields.server->origin[2] = org[2] + z;
838                                 if (!SV_TestEntityPosition(ent))
839                                 {
840                                         Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
841                                         SV_LinkEdict (ent, true);
842                                         return;
843                                 }
844                         }
845
846         VectorCopy (org, ent->fields.server->origin);
847         Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
848 }
849
850 void SV_UnstickEntity (prvm_edict_t *ent)
851 {
852         int i, j, z;
853         vec3_t org;
854
855         VectorCopy (ent->fields.server->origin, org);
856
857         for (z=0 ; z< 18 ; z += 6)
858                 for (i=-1 ; i <= 1 ; i++)
859                         for (j=-1 ; j <= 1 ; j++)
860                         {
861                                 ent->fields.server->origin[0] = org[0] + i;
862                                 ent->fields.server->origin[1] = org[1] + j;
863                                 ent->fields.server->origin[2] = org[2] + z;
864                                 if (!SV_TestEntityPosition(ent))
865                                 {
866                                         Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
867                                         SV_LinkEdict (ent, true);
868                                         return;
869                                 }
870                         }
871
872         VectorCopy (org, ent->fields.server->origin);
873         Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
874 }
875
876
877 /*
878 =============
879 SV_CheckWater
880 =============
881 */
882 qboolean SV_CheckWater (prvm_edict_t *ent)
883 {
884         int cont;
885         vec3_t point;
886
887         point[0] = ent->fields.server->origin[0];
888         point[1] = ent->fields.server->origin[1];
889         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
890
891         ent->fields.server->waterlevel = 0;
892         ent->fields.server->watertype = CONTENTS_EMPTY;
893         cont = SV_PointSuperContents(point);
894         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
895         {
896                 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
897                 ent->fields.server->waterlevel = 1;
898                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
899                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
900                 {
901                         ent->fields.server->waterlevel = 2;
902                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
903                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
904                                 ent->fields.server->waterlevel = 3;
905                 }
906         }
907
908         return ent->fields.server->waterlevel > 1;
909 }
910
911 /*
912 ============
913 SV_WallFriction
914
915 ============
916 */
917 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
918 {
919         float d, i;
920         vec3_t forward, into, side;
921
922         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
923         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
924         {
925                 // cut the tangential velocity
926                 i = DotProduct (stepnormal, ent->fields.server->velocity);
927                 VectorScale (stepnormal, i, into);
928                 VectorSubtract (ent->fields.server->velocity, into, side);
929                 ent->fields.server->velocity[0] = side[0] * (1 + d);
930                 ent->fields.server->velocity[1] = side[1] * (1 + d);
931         }
932 }
933
934 /*
935 =====================
936 SV_TryUnstick
937
938 Player has come to a dead stop, possibly due to the problem with limited
939 float precision at some angle joins in the BSP hull.
940
941 Try fixing by pushing one pixel in each direction.
942
943 This is a hack, but in the interest of good gameplay...
944 ======================
945 */
946 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
947 {
948         int i, clip;
949         vec3_t oldorg, dir;
950
951         VectorCopy (ent->fields.server->origin, oldorg);
952         VectorClear (dir);
953
954         for (i=0 ; i<8 ; i++)
955         {
956                 // try pushing a little in an axial direction
957                 switch (i)
958                 {
959                         case 0: dir[0] = 2; dir[1] = 0; break;
960                         case 1: dir[0] = 0; dir[1] = 2; break;
961                         case 2: dir[0] = -2; dir[1] = 0; break;
962                         case 3: dir[0] = 0; dir[1] = -2; break;
963                         case 4: dir[0] = 2; dir[1] = 2; break;
964                         case 5: dir[0] = -2; dir[1] = 2; break;
965                         case 6: dir[0] = 2; dir[1] = -2; break;
966                         case 7: dir[0] = -2; dir[1] = -2; break;
967                 }
968
969                 SV_PushEntity (ent, dir);
970
971                 // retry the original move
972                 ent->fields.server->velocity[0] = oldvel[0];
973                 ent->fields.server->velocity[1] = oldvel[1];
974                 ent->fields.server->velocity[2] = 0;
975                 clip = SV_FlyMove (ent, 0.1, NULL);
976
977                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
978                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
979                 {
980                         Con_DPrint("TryUnstick - success.\n");
981                         return clip;
982                 }
983
984                 // go back to the original pos and try again
985                 VectorCopy (oldorg, ent->fields.server->origin);
986         }
987
988         // still not moving
989         VectorClear (ent->fields.server->velocity);
990         Con_DPrint("TryUnstick - failure.\n");
991         return 7;
992 }
993
994 /*
995 =====================
996 SV_WalkMove
997
998 Only used by players
999 ======================
1000 */
1001 void SV_WalkMove (prvm_edict_t *ent)
1002 {
1003         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1004         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1005         trace_t downtrace;
1006
1007         SV_CheckVelocity(ent);
1008
1009         // do a regular slide move unless it looks like you ran into a step
1010         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1011         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1012
1013         VectorCopy (ent->fields.server->origin, start_origin);
1014         VectorCopy (ent->fields.server->velocity, start_velocity);
1015
1016         clip = SV_FlyMove (ent, sv.frametime, NULL);
1017
1018         SV_CheckVelocity(ent);
1019
1020         VectorCopy(ent->fields.server->origin, originalmove_origin);
1021         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1022         originalmove_clip = clip;
1023         originalmove_flags = (int)ent->fields.server->flags;
1024         originalmove_groundentity = ent->fields.server->groundentity;
1025
1026         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1027                 return;
1028
1029         if (sv_nostep.integer)
1030                 return;
1031
1032         // if move didn't block on a step, return
1033         if (clip & 2)
1034         {
1035                 // if move was not trying to move into the step, return
1036                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1037                         return;
1038
1039                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1040                 {
1041                         // return if gibbed by a trigger
1042                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1043                                 return;
1044
1045                         // only step up while jumping if that is enabled
1046                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1047                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1048                                         return;
1049                 }
1050
1051                 // try moving up and forward to go up a step
1052                 // back to start pos
1053                 VectorCopy (start_origin, ent->fields.server->origin);
1054                 VectorCopy (start_velocity, ent->fields.server->velocity);
1055
1056                 // move up
1057                 VectorClear (upmove);
1058                 upmove[2] = sv_stepheight.value;
1059                 // FIXME: don't link?
1060                 SV_PushEntity(ent, upmove);
1061
1062                 // move forward
1063                 ent->fields.server->velocity[2] = 0;
1064                 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1065                 ent->fields.server->velocity[2] += start_velocity[2];
1066
1067                 SV_CheckVelocity(ent);
1068
1069                 // check for stuckness, possibly due to the limited precision of floats
1070                 // in the clipping hulls
1071                 if (clip
1072                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1073                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1074                 {
1075                         //Con_Printf("wall\n");
1076                         // stepping up didn't make any progress, revert to original move
1077                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1078                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1079                         //clip = originalmove_clip;
1080                         ent->fields.server->flags = originalmove_flags;
1081                         ent->fields.server->groundentity = originalmove_groundentity;
1082                         // now try to unstick if needed
1083                         //clip = SV_TryUnstick (ent, oldvel);
1084                         return;
1085                 }
1086
1087                 //Con_Printf("step - ");
1088
1089                 // extra friction based on view angle
1090                 if (clip & 2 && sv_wallfriction.integer)
1091                         SV_WallFriction (ent, stepnormal);
1092         }
1093         // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1094         else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
1095                 return;
1096
1097         // move down
1098         VectorClear (downmove);
1099         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1100         // FIXME: don't link?
1101         downtrace = SV_PushEntity (ent, downmove);
1102
1103         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1104         {
1105                 // LordHavoc: disabled this check so you can walk on monsters/players
1106                 //if (ent->fields.server->solid == SOLID_BSP)
1107                 {
1108                         //Con_Printf("onground\n");
1109                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1110                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1111                 }
1112         }
1113         else
1114         {
1115                 //Con_Printf("slope\n");
1116                 // if the push down didn't end up on good ground, use the move without
1117                 // the step up.  This happens near wall / slope combinations, and can
1118                 // cause the player to hop up higher on a slope too steep to climb
1119                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1120                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1121                 //clip = originalmove_clip;
1122                 ent->fields.server->flags = originalmove_flags;
1123                 ent->fields.server->groundentity = originalmove_groundentity;
1124         }
1125
1126         SV_CheckVelocity(ent);
1127 }
1128
1129 //============================================================================
1130
1131 /*
1132 =============
1133 SV_Physics_Follow
1134
1135 Entities that are "stuck" to another entity
1136 =============
1137 */
1138 void SV_Physics_Follow (prvm_edict_t *ent)
1139 {
1140         vec3_t vf, vr, vu, angles, v;
1141         prvm_edict_t *e;
1142
1143         // regular thinking
1144         if (!SV_RunThink (ent))
1145                 return;
1146
1147         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1148         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1149         if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
1150         {
1151                 // quick case for no rotation
1152                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1153         }
1154         else
1155         {
1156                 angles[0] = -ent->fields.server->punchangle[0];
1157                 angles[1] =  ent->fields.server->punchangle[1];
1158                 angles[2] =  ent->fields.server->punchangle[2];
1159                 AngleVectors (angles, vf, vr, vu);
1160                 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
1161                 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
1162                 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
1163                 angles[0] = -e->fields.server->angles[0];
1164                 angles[1] =  e->fields.server->angles[1];
1165                 angles[2] =  e->fields.server->angles[2];
1166                 AngleVectors (angles, vf, vr, vu);
1167                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1168                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1169                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1170         }
1171         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1172         SV_LinkEdict (ent, true);
1173 }
1174
1175 /*
1176 ==============================================================================
1177
1178 TOSS / BOUNCE
1179
1180 ==============================================================================
1181 */
1182
1183 /*
1184 =============
1185 SV_CheckWaterTransition
1186
1187 =============
1188 */
1189 void SV_CheckWaterTransition (prvm_edict_t *ent)
1190 {
1191         int cont;
1192         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1193         if (!ent->fields.server->watertype)
1194         {
1195                 // just spawned here
1196                 ent->fields.server->watertype = cont;
1197                 ent->fields.server->waterlevel = 1;
1198                 return;
1199         }
1200
1201         // check if the entity crossed into or out of water
1202         if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1203                 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1204
1205         if (cont <= CONTENTS_WATER)
1206         {
1207                 ent->fields.server->watertype = cont;
1208                 ent->fields.server->waterlevel = 1;
1209         }
1210         else
1211         {
1212                 ent->fields.server->watertype = CONTENTS_EMPTY;
1213                 ent->fields.server->waterlevel = 0;
1214         }
1215 }
1216
1217 /*
1218 =============
1219 SV_Physics_Toss
1220
1221 Toss, bounce, and fly movement.  When onground, do nothing.
1222 =============
1223 */
1224 void SV_Physics_Toss (prvm_edict_t *ent)
1225 {
1226         trace_t trace;
1227         vec3_t move;
1228
1229 // if onground, return without moving
1230         if ((int)ent->fields.server->flags & FL_ONGROUND)
1231         {
1232                 // don't stick to ground if onground and moving upward
1233                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1234                         ent->fields.server->flags -= FL_ONGROUND;
1235                 else
1236                 {
1237                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1238                         if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1239                                 return;
1240                         // if ent was supported by a brush model on previous frame,
1241                         // and groundentity is now freed, set groundentity to 0 (floating)
1242                         if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1243                         {
1244                                 // leave it suspended in the air
1245                                 ent->fields.server->groundentity = 0;
1246                                 return;
1247                         }
1248                 }
1249         }
1250         ent->priv.server->suspendedinairflag = false;
1251
1252         SV_CheckVelocity (ent);
1253
1254 // add gravity
1255         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1256                 SV_AddGravity (ent);
1257
1258 // move angles
1259         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1260
1261 // move origin
1262         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1263         trace = SV_PushEntity (ent, move);
1264         if (ent->priv.server->free)
1265                 return;
1266         if (trace.startsolid)
1267         {
1268                 // try to unstick the entity
1269                 SV_UnstickEntity(ent);
1270                 trace = SV_PushEntity (ent, move);
1271                 if (ent->priv.server->free)
1272                         return;
1273         }
1274
1275         if (trace.fraction < 1)
1276         {
1277                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1278                 {
1279                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1280                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1281                 }
1282                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1283                 {
1284                         float d;
1285                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1286                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1287                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1288                         {
1289                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1290                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1291                                 {
1292                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1293                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1294                                         VectorClear (ent->fields.server->velocity);
1295                                         VectorClear (ent->fields.server->avelocity);
1296                                 }
1297                                 else
1298                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1299                         }
1300                         else
1301                         {
1302                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1303                                 {
1304                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1305                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1306                                         VectorClear (ent->fields.server->velocity);
1307                                         VectorClear (ent->fields.server->avelocity);
1308                                 }
1309                                 else
1310                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1311                         }
1312                 }
1313                 else
1314                 {
1315                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1316                         if (trace.plane.normal[2] > 0.7)
1317                         {
1318                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1319                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1320                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1321                                         ent->priv.server->suspendedinairflag = true;
1322                                 VectorClear (ent->fields.server->velocity);
1323                                 VectorClear (ent->fields.server->avelocity);
1324                         }
1325                         else
1326                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1327                 }
1328         }
1329
1330 // check for in water
1331         SV_CheckWaterTransition (ent);
1332 }
1333
1334 /*
1335 ===============================================================================
1336
1337 STEPPING MOVEMENT
1338
1339 ===============================================================================
1340 */
1341
1342 /*
1343 =============
1344 SV_Physics_Step
1345
1346 Monsters freefall when they don't have a ground entity, otherwise
1347 all movement is done with discrete steps.
1348
1349 This is also used for objects that have become still on the ground, but
1350 will fall if the floor is pulled out from under them.
1351 =============
1352 */
1353 void SV_Physics_Step (prvm_edict_t *ent)
1354 {
1355         int flags = (int)ent->fields.server->flags;
1356         // don't fall at all if fly/swim
1357         if (!(flags & (FL_FLY | FL_SWIM)))
1358         {
1359                 if (flags & FL_ONGROUND)
1360                 {
1361                         // freefall if onground and moving upward
1362                         // freefall if not standing on a world surface (it may be a lift)
1363                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1364                         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1365                         {
1366                                 ent->fields.server->flags -= FL_ONGROUND;
1367                                 SV_AddGravity(ent);
1368                                 SV_CheckVelocity(ent);
1369                                 SV_FlyMove(ent, sv.frametime, NULL);
1370                                 SV_LinkEdict(ent, true);
1371                         }
1372                 }
1373                 else
1374                 {
1375                         // freefall if not onground
1376                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1377
1378                         SV_AddGravity(ent);
1379                         SV_CheckVelocity(ent);
1380                         SV_FlyMove(ent, sv.frametime, NULL);
1381                         SV_LinkEdict(ent, true);
1382
1383                         // just hit ground
1384                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1385                                 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1386                 }
1387         }
1388
1389 // regular thinking
1390         SV_RunThink(ent);
1391
1392         SV_CheckWaterTransition(ent);
1393 }
1394
1395 //============================================================================
1396
1397 static void SV_Physics_Entity (prvm_edict_t *ent)
1398 {
1399         // don't run a move on newly spawned projectiles as it messes up movement
1400         // interpolation and rocket trails
1401         qboolean runmove = ent->priv.server->move;
1402         ent->priv.server->move = true;
1403         switch ((int) ent->fields.server->movetype)
1404         {
1405         case MOVETYPE_PUSH:
1406         case MOVETYPE_FAKEPUSH:
1407                 SV_Physics_Pusher (ent);
1408                 break;
1409         case MOVETYPE_NONE:
1410                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1411                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1412                         SV_RunThink (ent);
1413                 break;
1414         case MOVETYPE_FOLLOW:
1415                 SV_Physics_Follow (ent);
1416                 break;
1417         case MOVETYPE_NOCLIP:
1418                 if (SV_RunThink(ent))
1419                 {
1420                         SV_CheckWater(ent);
1421                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1422                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1423                 }
1424                 SV_LinkEdict(ent, false);
1425                 break;
1426         case MOVETYPE_STEP:
1427                 SV_Physics_Step (ent);
1428                 break;
1429         case MOVETYPE_WALK:
1430                 if (SV_RunThink (ent))
1431                 {
1432                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1433                                 SV_AddGravity (ent);
1434                         SV_CheckStuck (ent);
1435                         SV_WalkMove (ent);
1436                         SV_LinkEdict (ent, true);
1437                 }
1438                 break;
1439         case MOVETYPE_TOSS:
1440         case MOVETYPE_BOUNCE:
1441         case MOVETYPE_BOUNCEMISSILE:
1442         case MOVETYPE_FLYMISSILE:
1443         case MOVETYPE_FLY:
1444                 // regular thinking
1445                 if (SV_RunThink (ent) && runmove)
1446                         SV_Physics_Toss (ent);
1447                 break;
1448         default:
1449                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1450                 break;
1451         }
1452 }
1453
1454 void SV_ApplyClientMove (void);
1455 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1456 {
1457         SV_ApplyClientMove();
1458         // make sure the velocity is sane (not a NaN)
1459         SV_CheckVelocity(ent);
1460         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1461         if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1462         {
1463                 prog->globals.server->time = sv.time;
1464                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1465                 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1466         }
1467         else
1468                 SV_ClientThink ();
1469         // make sure the velocity is sane (not a NaN)
1470         SV_CheckVelocity(ent);
1471         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1472         // player_run/player_stand1 does not horribly malfunction if the
1473         // velocity becomes a number that is both == 0 and != 0
1474         // (sounds to me like NaN but to be absolutely safe...)
1475         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1476                 VectorClear(ent->fields.server->velocity);
1477         // call standard client pre-think
1478         prog->globals.server->time = sv.time;
1479         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1480         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1481         SV_CheckVelocity (ent);
1482
1483         switch ((int) ent->fields.server->movetype)
1484         {
1485         case MOVETYPE_PUSH:
1486         case MOVETYPE_FAKEPUSH:
1487                 SV_Physics_Pusher (ent);
1488                 break;
1489         case MOVETYPE_NONE:
1490                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1491                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1492                         SV_RunThink (ent);
1493                 break;
1494         case MOVETYPE_FOLLOW:
1495                 SV_Physics_Follow (ent);
1496                 break;
1497         case MOVETYPE_NOCLIP:
1498                 if (SV_RunThink(ent))
1499                 {
1500                         SV_CheckWater(ent);
1501                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1502                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1503                 }
1504                 break;
1505         case MOVETYPE_STEP:
1506                 SV_Physics_Step (ent);
1507                 break;
1508         case MOVETYPE_WALK:
1509                 if (SV_RunThink (ent))
1510                 {
1511                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1512                                 SV_AddGravity (ent);
1513                         SV_CheckStuck (ent);
1514                         SV_WalkMove (ent);
1515                 }
1516                 break;
1517         case MOVETYPE_TOSS:
1518         case MOVETYPE_BOUNCE:
1519         case MOVETYPE_BOUNCEMISSILE:
1520         case MOVETYPE_FLYMISSILE:
1521                 // regular thinking
1522                 if (SV_RunThink (ent))
1523                         SV_Physics_Toss (ent);
1524                 break;
1525         case MOVETYPE_FLY:
1526                 if (SV_RunThink (ent))
1527                 {
1528                         SV_CheckWater (ent);
1529                         SV_WalkMove (ent);
1530                 }
1531                 break;
1532         default:
1533                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1534                 break;
1535         }
1536
1537         SV_CheckVelocity (ent);
1538
1539         // call standard player post-think
1540         SV_LinkEdict (ent, true);
1541
1542         SV_CheckVelocity (ent);
1543
1544         prog->globals.server->time = sv.time;
1545         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1546         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1547 }
1548
1549 /*
1550 ================
1551 SV_Physics
1552
1553 ================
1554 */
1555 void SV_Physics (void)
1556 {
1557         int i;
1558         prvm_edict_t *ent;
1559
1560 // let the progs know that a new frame has started
1561         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1562         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1563         prog->globals.server->time = sv.time;
1564         prog->globals.server->frametime = sv.frametime;
1565         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1566
1567 //
1568 // treat each object in turn
1569 //
1570
1571         // if force_retouch, relink all the entities
1572         if (prog->globals.server->force_retouch > 0)
1573                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1574                         if (!ent->priv.server->free)
1575                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
1576
1577         // run physics on the client entities
1578         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1579         {
1580                 if (!ent->priv.server->free)
1581                 {
1582                         // don't do physics on disconnected clients, FrikBot relies on this
1583                         if (!host_client->spawned)
1584                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1585                         // don't run physics here if running asynchronously
1586                         else if (!host_client->movesequence)
1587                                 SV_Physics_ClientEntity(ent);
1588                 }
1589         }
1590
1591         // run physics on all the non-client entities
1592         if (!sv_freezenonclients.integer)
1593                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1594                         if (!ent->priv.server->free)
1595                                 SV_Physics_Entity(ent);
1596
1597         if (prog->globals.server->force_retouch > 0)
1598                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1599
1600         // LordHavoc: endframe support
1601         if (EndFrameQC)
1602         {
1603                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1604                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1605                 prog->globals.server->time = sv.time;
1606                 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1607         }
1608
1609         // decrement prog->num_edicts if the highest number entities died
1610         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1611
1612         if (!sv_freezenonclients.integer)
1613                 sv.time += sv.frametime;
1614 }
1615
1616
1617 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1618 {
1619         int i;
1620         float gravity, savesolid;
1621         vec3_t move, end;
1622         prvm_edict_t tempent, *tent;
1623         entvars_t vars;
1624         prvm_eval_t *val;
1625         trace_t trace;
1626
1627         // copy the vars over
1628         memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1629         // set up the temp entity to point to the copied vars
1630         tent = &tempent;
1631         tent->fields.server = &vars;
1632
1633         savesolid = tossent->fields.server->solid;
1634         tossent->fields.server->solid = SOLID_NOT;
1635
1636         // this has to fetch the field from the original edict, since our copy is truncated
1637         val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1638         if (val != NULL && val->_float != 0)
1639                 gravity = val->_float;
1640         else
1641                 gravity = 1.0;
1642         gravity *= sv_gravity.value * 0.05;
1643
1644         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1645         {
1646                 SV_CheckVelocity (tent);
1647                 tent->fields.server->velocity[2] -= gravity;
1648                 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1649                 VectorScale (tent->fields.server->velocity, 0.05, move);
1650                 VectorAdd (tent->fields.server->origin, move, end);
1651                 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1652                 VectorCopy (trace.endpos, tent->fields.server->origin);
1653
1654                 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1655                         break;
1656         }
1657         tossent->fields.server->solid = savesolid;
1658         trace.fraction = 0; // not relevant
1659         return trace;
1660 }
1661