]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
I forgot to mention that I also fixed some other asymmetric crosshairs (3 and 4)
[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"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"};
52 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1"};
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, 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, trace.ent);
516         return trace;
517 }
518
519
520 /*
521 ============
522 SV_PushMove
523
524 ============
525 */
526 trace_t SV_ClipMoveToEntity (prvm_edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
527 void SV_PushMove (prvm_edict_t *pusher, float movetime)
528 {
529         int i, e, index;
530         prvm_edict_t *check, *ed;
531         float savesolid, movetime2, pushltime;
532         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
533         int num_moved;
534         int numcheckentities;
535         static prvm_edict_t *checkentities[MAX_EDICTS];
536         model_t *pushermodel;
537         trace_t trace;
538
539         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])
540         {
541                 pusher->fields.server->ltime += movetime;
542                 return;
543         }
544
545         switch ((int) pusher->fields.server->solid)
546         {
547         // LordHavoc: valid pusher types
548         case SOLID_BSP:
549         case SOLID_BBOX:
550         case SOLID_SLIDEBOX:
551         case SOLID_CORPSE: // LordHavoc: this would be weird...
552                 break;
553         // LordHavoc: no collisions
554         case SOLID_NOT:
555         case SOLID_TRIGGER:
556                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
557                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
558                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
559                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
560                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
561                 pusher->fields.server->ltime += movetime;
562                 SV_LinkEdict (pusher, false);
563                 return;
564         default:
565                 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
566                 return;
567         }
568         index = (int) pusher->fields.server->modelindex;
569         if (index < 1 || index >= MAX_MODELS)
570         {
571                 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
572                 return;
573         }
574         pushermodel = sv.models[index];
575
576         movetime2 = movetime;
577         VectorScale(pusher->fields.server->velocity, movetime2, move1);
578         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
579         if (moveangle[0] || moveangle[2])
580         {
581                 for (i = 0;i < 3;i++)
582                 {
583                         if (move1[i] > 0)
584                         {
585                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
586                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
587                         }
588                         else
589                         {
590                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
591                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
592                         }
593                 }
594         }
595         else if (moveangle[1])
596         {
597                 for (i = 0;i < 3;i++)
598                 {
599                         if (move1[i] > 0)
600                         {
601                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
602                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
603                         }
604                         else
605                         {
606                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
607                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
608                         }
609                 }
610         }
611         else
612         {
613                 for (i = 0;i < 3;i++)
614                 {
615                         if (move1[i] > 0)
616                         {
617                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
618                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
619                         }
620                         else
621                         {
622                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
623                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
624                         }
625                 }
626         }
627
628         VectorNegate (moveangle, a);
629         AngleVectorsFLU (a, forward, left, up);
630
631         VectorCopy (pusher->fields.server->origin, pushorig);
632         VectorCopy (pusher->fields.server->angles, pushang);
633         pushltime = pusher->fields.server->ltime;
634
635 // move the pusher to it's final position
636
637         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
638         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
639         pusher->fields.server->ltime += movetime;
640         SV_LinkEdict (pusher, false);
641
642         savesolid = pusher->fields.server->solid;
643
644 // see if any solid entities are inside the final position
645         num_moved = 0;
646
647         numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
648         for (e = 0;e < numcheckentities;e++)
649         {
650                 check = checkentities[e];
651                 if (check->fields.server->movetype == MOVETYPE_PUSH
652                  || check->fields.server->movetype == MOVETYPE_NONE
653                  || check->fields.server->movetype == MOVETYPE_FOLLOW
654                  || check->fields.server->movetype == MOVETYPE_NOCLIP
655                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
656                         continue;
657
658                 // if the entity is standing on the pusher, it will definitely be moved
659                 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
660                 {
661                         // if the entity is not inside the pusher's final position, leave it alone
662                         if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
663                                 continue;
664                 }
665
666                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
667                 {
668                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
669                         org2[0] = DotProduct (org, forward);
670                         org2[1] = DotProduct (org, left);
671                         org2[2] = DotProduct (org, up);
672                         VectorSubtract (org2, org, move);
673                         VectorAdd (move, move1, move);
674                 }
675                 else
676                         VectorCopy (move1, move);
677
678                 // remove the onground flag for non-players
679                 if (check->fields.server->movetype != MOVETYPE_WALK)
680                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
681
682                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
683                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
684                 sv.moved_edicts[num_moved++] = check;
685
686                 // try moving the contacted entity
687                 pusher->fields.server->solid = SOLID_NOT;
688                 trace = SV_PushEntity (check, move);
689                 // FIXME: turn players specially
690                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
691                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
692
693                 // if it is still inside the pusher, block
694                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
695                 {
696                         // try moving the contacted entity a tiny bit further to account for precision errors
697                         pusher->fields.server->solid = SOLID_NOT;
698                         VectorScale(move, 0.1, move);
699                         SV_PushEntity (check, move);
700                         pusher->fields.server->solid = savesolid;
701                         if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
702                         {
703                                 // still inside pusher, so it's really blocked
704
705                                 // fail the move
706                                 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
707                                         continue;
708                                 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
709                                 {
710                                         // corpse
711                                         check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
712                                         VectorCopy (check->fields.server->mins, check->fields.server->maxs);
713                                         continue;
714                                 }
715
716                                 VectorCopy (pushorig, pusher->fields.server->origin);
717                                 VectorCopy (pushang, pusher->fields.server->angles);
718                                 pusher->fields.server->ltime = pushltime;
719                                 SV_LinkEdict (pusher, false);
720
721                                 // move back any entities we already moved
722                                 for (i = 0;i < num_moved;i++)
723                                 {
724                                         ed = sv.moved_edicts[i];
725                                         VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
726                                         VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
727                                         SV_LinkEdict (ed, false);
728                                 }
729
730                                 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
731                                 if (pusher->fields.server->blocked)
732                                 {
733                                         prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
734                                         prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
735                                         PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
736                                 }
737                                 break;
738                         }
739                 }
740         }
741         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
742         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
743         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
744 }
745
746 /*
747 ================
748 SV_Physics_Pusher
749
750 ================
751 */
752 void SV_Physics_Pusher (prvm_edict_t *ent)
753 {
754         float thinktime, oldltime, movetime;
755
756         oldltime = ent->fields.server->ltime;
757
758         thinktime = ent->fields.server->nextthink;
759         if (thinktime < ent->fields.server->ltime + sv.frametime)
760         {
761                 movetime = thinktime - ent->fields.server->ltime;
762                 if (movetime < 0)
763                         movetime = 0;
764         }
765         else
766                 movetime = sv.frametime;
767
768         if (movetime)
769                 // advances ent->fields.server->ltime if not blocked
770                 SV_PushMove (ent, movetime);
771
772         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
773         {
774                 ent->fields.server->nextthink = 0;
775                 prog->globals.server->time = sv.time;
776                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
777                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
778                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
779         }
780 }
781
782
783 /*
784 ===============================================================================
785
786 CLIENT MOVEMENT
787
788 ===============================================================================
789 */
790
791 /*
792 =============
793 SV_CheckStuck
794
795 This is a big hack to try and fix the rare case of getting stuck in the world
796 clipping hull.
797 =============
798 */
799 void SV_CheckStuck (prvm_edict_t *ent)
800 {
801         int i, j, z;
802         vec3_t org;
803
804         if (!SV_TestEntityPosition(ent))
805         {
806                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
807                 return;
808         }
809
810         VectorCopy (ent->fields.server->origin, org);
811         VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
812         if (!SV_TestEntityPosition(ent))
813         {
814                 Con_DPrint("Unstuck.\n");
815                 SV_LinkEdict (ent, true);
816                 return;
817         }
818
819         for (z=0 ; z< 18 ; z++)
820                 for (i=-1 ; i <= 1 ; i++)
821                         for (j=-1 ; j <= 1 ; j++)
822                         {
823                                 ent->fields.server->origin[0] = org[0] + i;
824                                 ent->fields.server->origin[1] = org[1] + j;
825                                 ent->fields.server->origin[2] = org[2] + z;
826                                 if (!SV_TestEntityPosition(ent))
827                                 {
828                                         Con_DPrint("Unstuck.\n");
829                                         SV_LinkEdict (ent, true);
830                                         return;
831                                 }
832                         }
833
834         VectorCopy (org, ent->fields.server->origin);
835         Con_DPrint("player is stuck.\n");
836 }
837
838
839 /*
840 =============
841 SV_CheckWater
842 =============
843 */
844 qboolean SV_CheckWater (prvm_edict_t *ent)
845 {
846         int cont;
847         vec3_t point;
848
849         point[0] = ent->fields.server->origin[0];
850         point[1] = ent->fields.server->origin[1];
851         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
852
853         ent->fields.server->waterlevel = 0;
854         ent->fields.server->watertype = CONTENTS_EMPTY;
855         cont = SV_PointSuperContents(point);
856         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
857         {
858                 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
859                 ent->fields.server->waterlevel = 1;
860                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
861                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
862                 {
863                         ent->fields.server->waterlevel = 2;
864                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
865                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
866                                 ent->fields.server->waterlevel = 3;
867                 }
868         }
869
870         return ent->fields.server->waterlevel > 1;
871 }
872
873 /*
874 ============
875 SV_WallFriction
876
877 ============
878 */
879 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
880 {
881         float d, i;
882         vec3_t forward, into, side;
883
884         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
885         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
886         {
887                 // cut the tangential velocity
888                 i = DotProduct (stepnormal, ent->fields.server->velocity);
889                 VectorScale (stepnormal, i, into);
890                 VectorSubtract (ent->fields.server->velocity, into, side);
891                 ent->fields.server->velocity[0] = side[0] * (1 + d);
892                 ent->fields.server->velocity[1] = side[1] * (1 + d);
893         }
894 }
895
896 /*
897 =====================
898 SV_TryUnstick
899
900 Player has come to a dead stop, possibly due to the problem with limited
901 float precision at some angle joins in the BSP hull.
902
903 Try fixing by pushing one pixel in each direction.
904
905 This is a hack, but in the interest of good gameplay...
906 ======================
907 */
908 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
909 {
910         int i, clip;
911         vec3_t oldorg, dir;
912
913         VectorCopy (ent->fields.server->origin, oldorg);
914         VectorClear (dir);
915
916         for (i=0 ; i<8 ; i++)
917         {
918                 // try pushing a little in an axial direction
919                 switch (i)
920                 {
921                         case 0: dir[0] = 2; dir[1] = 0; break;
922                         case 1: dir[0] = 0; dir[1] = 2; break;
923                         case 2: dir[0] = -2; dir[1] = 0; break;
924                         case 3: dir[0] = 0; dir[1] = -2; break;
925                         case 4: dir[0] = 2; dir[1] = 2; break;
926                         case 5: dir[0] = -2; dir[1] = 2; break;
927                         case 6: dir[0] = 2; dir[1] = -2; break;
928                         case 7: dir[0] = -2; dir[1] = -2; break;
929                 }
930
931                 SV_PushEntity (ent, dir);
932
933                 // retry the original move
934                 ent->fields.server->velocity[0] = oldvel[0];
935                 ent->fields.server->velocity[1] = oldvel[1];
936                 ent->fields.server->velocity[2] = 0;
937                 clip = SV_FlyMove (ent, 0.1, NULL);
938
939                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
940                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
941                 {
942                         Con_DPrint("TryUnstick - success.\n");
943                         return clip;
944                 }
945
946                 // go back to the original pos and try again
947                 VectorCopy (oldorg, ent->fields.server->origin);
948         }
949
950         // still not moving
951         VectorClear (ent->fields.server->velocity);
952         Con_DPrint("TryUnstick - failure.\n");
953         return 7;
954 }
955
956 /*
957 =====================
958 SV_WalkMove
959
960 Only used by players
961 ======================
962 */
963 void SV_WalkMove (prvm_edict_t *ent)
964 {
965         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
966         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
967         trace_t downtrace;
968
969         SV_CheckVelocity(ent);
970
971         // do a regular slide move unless it looks like you ran into a step
972         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
973         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
974
975         VectorCopy (ent->fields.server->origin, start_origin);
976         VectorCopy (ent->fields.server->velocity, start_velocity);
977
978         clip = SV_FlyMove (ent, sv.frametime, NULL);
979
980         SV_CheckVelocity(ent);
981
982         VectorCopy(ent->fields.server->origin, originalmove_origin);
983         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
984         originalmove_clip = clip;
985         originalmove_flags = (int)ent->fields.server->flags;
986         originalmove_groundentity = ent->fields.server->groundentity;
987
988         if ((int)ent->fields.server->flags & FL_WATERJUMP)
989                 return;
990
991         if (sv_nostep.integer)
992                 return;
993
994         // if move didn't block on a step, return
995         if (clip & 2)
996         {
997                 // if move was not trying to move into the step, return
998                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
999                         return;
1000
1001                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1002                 {
1003                         // return if gibbed by a trigger
1004                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1005                                 return;
1006
1007                         // only step up while jumping if that is enabled
1008                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1009                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1010                                         return;
1011                 }
1012
1013                 // try moving up and forward to go up a step
1014                 // back to start pos
1015                 VectorCopy (start_origin, ent->fields.server->origin);
1016                 VectorCopy (start_velocity, ent->fields.server->velocity);
1017
1018                 // move up
1019                 VectorClear (upmove);
1020                 upmove[2] = sv_stepheight.value;
1021                 // FIXME: don't link?
1022                 SV_PushEntity(ent, upmove);
1023
1024                 // move forward
1025                 ent->fields.server->velocity[2] = 0;
1026                 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1027                 ent->fields.server->velocity[2] += start_velocity[2];
1028
1029                 SV_CheckVelocity(ent);
1030
1031                 // check for stuckness, possibly due to the limited precision of floats
1032                 // in the clipping hulls
1033                 if (clip
1034                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1035                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1036                 {
1037                         //Con_Printf("wall\n");
1038                         // stepping up didn't make any progress, revert to original move
1039                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1040                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1041                         //clip = originalmove_clip;
1042                         ent->fields.server->flags = originalmove_flags;
1043                         ent->fields.server->groundentity = originalmove_groundentity;
1044                         // now try to unstick if needed
1045                         //clip = SV_TryUnstick (ent, oldvel);
1046                         return;
1047                 }
1048
1049                 //Con_Printf("step - ");
1050
1051                 // extra friction based on view angle
1052                 if (clip & 2 && sv_wallfriction.integer)
1053                         SV_WallFriction (ent, stepnormal);
1054         }
1055         // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1056         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)))
1057                 return;
1058
1059         // move down
1060         VectorClear (downmove);
1061         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1062         // FIXME: don't link?
1063         downtrace = SV_PushEntity (ent, downmove);
1064
1065         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1066         {
1067                 // LordHavoc: disabled this check so you can walk on monsters/players
1068                 //if (ent->fields.server->solid == SOLID_BSP)
1069                 {
1070                         //Con_Printf("onground\n");
1071                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1072                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1073                 }
1074         }
1075         else
1076         {
1077                 //Con_Printf("slope\n");
1078                 // if the push down didn't end up on good ground, use the move without
1079                 // the step up.  This happens near wall / slope combinations, and can
1080                 // cause the player to hop up higher on a slope too steep to climb
1081                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1082                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1083                 //clip = originalmove_clip;
1084                 ent->fields.server->flags = originalmove_flags;
1085                 ent->fields.server->groundentity = originalmove_groundentity;
1086         }
1087
1088         SV_CheckVelocity(ent);
1089 }
1090
1091 //============================================================================
1092
1093 /*
1094 =============
1095 SV_Physics_Follow
1096
1097 Entities that are "stuck" to another entity
1098 =============
1099 */
1100 void SV_Physics_Follow (prvm_edict_t *ent)
1101 {
1102         vec3_t vf, vr, vu, angles, v;
1103         prvm_edict_t *e;
1104
1105         // regular thinking
1106         if (!SV_RunThink (ent))
1107                 return;
1108
1109         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1110         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1111         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])
1112         {
1113                 // quick case for no rotation
1114                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1115         }
1116         else
1117         {
1118                 angles[0] = -ent->fields.server->punchangle[0];
1119                 angles[1] =  ent->fields.server->punchangle[1];
1120                 angles[2] =  ent->fields.server->punchangle[2];
1121                 AngleVectors (angles, vf, vr, vu);
1122                 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];
1123                 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];
1124                 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];
1125                 angles[0] = -e->fields.server->angles[0];
1126                 angles[1] =  e->fields.server->angles[1];
1127                 angles[2] =  e->fields.server->angles[2];
1128                 AngleVectors (angles, vf, vr, vu);
1129                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1130                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1131                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1132         }
1133         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1134         SV_LinkEdict (ent, true);
1135 }
1136
1137 /*
1138 ==============================================================================
1139
1140 TOSS / BOUNCE
1141
1142 ==============================================================================
1143 */
1144
1145 /*
1146 =============
1147 SV_CheckWaterTransition
1148
1149 =============
1150 */
1151 void SV_CheckWaterTransition (prvm_edict_t *ent)
1152 {
1153         int cont;
1154         cont = SV_PointQ1Contents(ent->fields.server->origin);
1155         if (!ent->fields.server->watertype)
1156         {
1157                 // just spawned here
1158                 ent->fields.server->watertype = cont;
1159                 ent->fields.server->waterlevel = 1;
1160                 return;
1161         }
1162
1163         // check if the entity crossed into or out of water
1164         if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1165                 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1166
1167         if (cont <= CONTENTS_WATER)
1168         {
1169                 ent->fields.server->watertype = cont;
1170                 ent->fields.server->waterlevel = 1;
1171         }
1172         else
1173         {
1174                 ent->fields.server->watertype = CONTENTS_EMPTY;
1175                 ent->fields.server->waterlevel = 0;
1176         }
1177 }
1178
1179 /*
1180 =============
1181 SV_Physics_Toss
1182
1183 Toss, bounce, and fly movement.  When onground, do nothing.
1184 =============
1185 */
1186 void SV_Physics_Toss (prvm_edict_t *ent)
1187 {
1188         trace_t trace;
1189         vec3_t move;
1190
1191         // don't stick to ground if onground and moving upward
1192         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1193                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1194
1195 // if onground, return without moving
1196         if ((int)ent->fields.server->flags & FL_ONGROUND)
1197         {
1198                 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1199                         return;
1200                 // if ent was supported by a brush model on previous frame,
1201                 // and groundentity is now freed, set groundentity to 0 (floating)
1202                 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1203                 {
1204                         // leave it suspended in the air
1205                         ent->fields.server->groundentity = 0;
1206                         return;
1207                 }
1208         }
1209         ent->priv.server->suspendedinairflag = false;
1210
1211         SV_CheckVelocity (ent);
1212
1213 // add gravity
1214         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1215                 SV_AddGravity (ent);
1216
1217 // move angles
1218         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1219
1220 // move origin
1221         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1222         trace = SV_PushEntity (ent, move);
1223         if (ent->priv.server->free)
1224                 return;
1225
1226         if (trace.fraction < 1)
1227         {
1228                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1229                 {
1230                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1231                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1232                 }
1233                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1234                 {
1235                         float d;
1236                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1237                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1238                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1239                         {
1240                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1241                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1242                                 {
1243                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1244                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1245                                         VectorClear (ent->fields.server->velocity);
1246                                         VectorClear (ent->fields.server->avelocity);
1247                                 }
1248                                 else
1249                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1250                         }
1251                         else
1252                         {
1253                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1254                                 {
1255                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1256                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1257                                         VectorClear (ent->fields.server->velocity);
1258                                         VectorClear (ent->fields.server->avelocity);
1259                                 }
1260                                 else
1261                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1262                         }
1263                 }
1264                 else
1265                 {
1266                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1267                         if (trace.plane.normal[2] > 0.7)
1268                         {
1269                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1270                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1271                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1272                                         ent->priv.server->suspendedinairflag = true;
1273                                 VectorClear (ent->fields.server->velocity);
1274                                 VectorClear (ent->fields.server->avelocity);
1275                         }
1276                         else
1277                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1278                 }
1279         }
1280
1281 // check for in water
1282         SV_CheckWaterTransition (ent);
1283 }
1284
1285 /*
1286 ===============================================================================
1287
1288 STEPPING MOVEMENT
1289
1290 ===============================================================================
1291 */
1292
1293 /*
1294 =============
1295 SV_Physics_Step
1296
1297 Monsters freefall when they don't have a ground entity, otherwise
1298 all movement is done with discrete steps.
1299
1300 This is also used for objects that have become still on the ground, but
1301 will fall if the floor is pulled out from under them.
1302 =============
1303 */
1304 void SV_Physics_Step (prvm_edict_t *ent)
1305 {
1306         // don't stick to ground if onground and moving upward
1307         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1308                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1309
1310         // freefall if not onground/fly/swim
1311         if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1312         {
1313                 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1314
1315                 SV_AddGravity(ent);
1316                 SV_CheckVelocity(ent);
1317                 SV_FlyMove(ent, sv.frametime, NULL);
1318                 SV_LinkEdict(ent, true);
1319
1320                 // just hit ground
1321                 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1322                         SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1323         }
1324
1325 // regular thinking
1326         SV_RunThink(ent);
1327
1328         SV_CheckWaterTransition(ent);
1329 }
1330
1331 //============================================================================
1332
1333 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1334 {
1335         int i = ent - prog->edicts;
1336         if (i >= 1 && i <= svs.maxclients)
1337         {
1338                 // make sure the velocity is sane (not a NaN)
1339                 SV_CheckVelocity(ent);
1340                 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1341                 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1342                 {
1343                         prog->globals.server->time = sv.time;
1344                         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1345                         PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1346                 }
1347                 else
1348                         SV_ClientThink ();
1349                 // make sure the velocity is sane (not a NaN)
1350                 SV_CheckVelocity(ent);
1351                 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1352                 // player_run/player_stand1 does not horribly malfunction if the
1353                 // velocity becomes a number that is both == 0 and != 0
1354                 // (sounds to me like NaN but to be absolutely safe...)
1355                 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1356                         VectorClear(ent->fields.server->velocity);
1357                 // call standard client pre-think
1358                 prog->globals.server->time = sv.time;
1359                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1360                 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1361                 SV_CheckVelocity (ent);
1362         }
1363
1364         // LordHavoc: merged client and normal entity physics
1365         switch ((int) ent->fields.server->movetype)
1366         {
1367         case MOVETYPE_PUSH:
1368         case MOVETYPE_FAKEPUSH:
1369                 SV_Physics_Pusher (ent);
1370                 break;
1371         case MOVETYPE_NONE:
1372                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1373                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1374                         SV_RunThink (ent);
1375                 break;
1376         case MOVETYPE_FOLLOW:
1377                 SV_Physics_Follow (ent);
1378                 break;
1379         case MOVETYPE_NOCLIP:
1380                 if (SV_RunThink(ent))
1381                 {
1382                         SV_CheckWater(ent);
1383                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1384                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1385                 }
1386                 // relink normal entities here, players always get relinked so don't relink twice
1387                 if (!(i > 0 && i <= svs.maxclients))
1388                         SV_LinkEdict(ent, false);
1389                 break;
1390         case MOVETYPE_STEP:
1391                 SV_Physics_Step (ent);
1392                 break;
1393         case MOVETYPE_WALK:
1394                 if (SV_RunThink (ent))
1395                 {
1396                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1397                                 SV_AddGravity (ent);
1398                         SV_CheckStuck (ent);
1399                         SV_WalkMove (ent);
1400                         // relink normal entities here, players always get relinked so don't relink twice
1401                         if (!(i > 0 && i <= svs.maxclients))
1402                                 SV_LinkEdict (ent, true);
1403                 }
1404                 break;
1405         case MOVETYPE_TOSS:
1406         case MOVETYPE_BOUNCE:
1407         case MOVETYPE_BOUNCEMISSILE:
1408         case MOVETYPE_FLYMISSILE:
1409                 // regular thinking
1410                 if (SV_RunThink (ent) && runmove)
1411                         SV_Physics_Toss (ent);
1412                 break;
1413         case MOVETYPE_FLY:
1414                 if (SV_RunThink (ent) && runmove)
1415                 {
1416                         if (i > 0 && i <= svs.maxclients)
1417                         {
1418                                 SV_CheckWater (ent);
1419                                 SV_WalkMove (ent);
1420                         }
1421                         else
1422                                 SV_Physics_Toss (ent);
1423                 }
1424                 break;
1425         default:
1426                 Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1427                 break;
1428         }
1429
1430         if (i >= 1 && i <= svs.maxclients)
1431         {
1432                 SV_CheckVelocity (ent);
1433
1434                 // call standard player post-think
1435                 SV_LinkEdict (ent, true);
1436
1437                 SV_CheckVelocity (ent);
1438
1439                 prog->globals.server->time = sv.time;
1440                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1441                 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1442         }
1443 }
1444
1445
1446 /*
1447 ================
1448 SV_Physics
1449
1450 ================
1451 */
1452 void SV_Physics (void)
1453 {
1454         int i, newnum_edicts;
1455         prvm_edict_t *ent;
1456         qbyte runmove[MAX_EDICTS];
1457
1458 // let the progs know that a new frame has started
1459         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1460         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1461         prog->globals.server->time = sv.time;
1462         prog->globals.server->frametime = sv.frametime;
1463         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1464
1465         newnum_edicts = 0;
1466         for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1467                 if ((runmove[i] = !ent->priv.server->free))
1468                         newnum_edicts = i + 1;
1469         prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1470
1471 //
1472 // treat each object in turn
1473 //
1474
1475         for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1476         {
1477                 if (ent->priv.server->free)
1478                         continue;
1479
1480                 if (prog->globals.server->force_retouch)
1481                         SV_LinkEdict (ent, true);       // force retouch even for stationary
1482
1483                 if (i >= 1 && i <= svs.maxclients)
1484                 {
1485                         host_client = svs.clients + i - 1;
1486                         // don't do physics on disconnected clients, FrikBot relies on this
1487                         if (!host_client->spawned)
1488                         {
1489                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1490                                 continue;
1491                         }
1492                         // connected slot
1493                         if (host_client->movesequence)
1494                                 continue; // return if running asynchronously
1495                 }
1496                 else if (sv_freezenonclients.integer)
1497                         continue;
1498
1499                 SV_Physics_Entity(ent, runmove[i]);
1500         }
1501
1502         if (prog->globals.server->force_retouch > 0)
1503                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1504
1505         // LordHavoc: endframe support
1506         if (EndFrameQC)
1507         {
1508                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1509                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1510                 prog->globals.server->time = sv.time;
1511                 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1512         }
1513
1514         if (!sv_freezenonclients.integer)
1515                 sv.time += sv.frametime;
1516 }
1517
1518
1519 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1520 {
1521         int i;
1522         float gravity, savesolid;
1523         vec3_t move, end;
1524         prvm_edict_t tempent, *tent;
1525         entvars_t vars;
1526         prvm_eval_t *val;
1527         trace_t trace;
1528
1529         // copy the vars over
1530         memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1531         // set up the temp entity to point to the copied vars
1532         tent = &tempent;
1533         tent->fields.server = &vars;
1534
1535         savesolid = tossent->fields.server->solid;
1536         tossent->fields.server->solid = SOLID_NOT;
1537
1538         // this has to fetch the field from the original edict, since our copy is truncated
1539         val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1540         if (val != NULL && val->_float != 0)
1541                 gravity = val->_float;
1542         else
1543                 gravity = 1.0;
1544         gravity *= sv_gravity.value * 0.05;
1545
1546         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1547         {
1548                 SV_CheckVelocity (tent);
1549                 tent->fields.server->velocity[2] -= gravity;
1550                 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1551                 VectorScale (tent->fields.server->velocity, 0.05, move);
1552                 VectorAdd (tent->fields.server->origin, move, end);
1553                 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1554                 VectorCopy (trace.endpos, tent->fields.server->origin);
1555
1556                 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1557                         break;
1558         }
1559         tossent->fields.server->solid = savesolid;
1560         trace.fraction = 0; // not relevant
1561         return trace;
1562 }
1563