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 #include "prvm_cmds.h"
24 
25 /*
26 
27 
28 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.
29 
30 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
31 
32 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
33 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
34 corpses are SOLID_NOT and MOVETYPE_TOSS
35 crates are SOLID_BBOX and MOVETYPE_TOSS
36 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
37 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 
39 solid_edge items only clip against bsp models.
40 
41 */
42 
43 #define	MOVE_EPSILON	0.01
44 
45 void SV_Physics_Toss (prvm_edict_t *ent);
46 
SV_GetPitchSign(prvm_prog_t * prog,prvm_edict_t * ent)47 int SV_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
48 {
49 	dp_model_t *model;
50 	if (
51 			(model = SV_GetModelFromEdict(ent))
52 			?
53 			model->type == mod_alias
54 			:
55 			(
56 			 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
57 			 ||
58 			 ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
59 			)
60 	   )
61 		return -1;
62 	return 1;
63 }
64 
65 /*
66 ===============================================================================
67 
68 LINE TESTING IN HULLS
69 
70 ===============================================================================
71 */
72 
SV_GenericHitSuperContentsMask(const prvm_edict_t * passedict)73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
74 {
75 	prvm_prog_t *prog = SVVM_prog;
76 	if (passedict)
77 	{
78 		int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
79 		if (dphitcontentsmask)
80 			return dphitcontentsmask;
81 		else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
82 		{
83 			if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
84 				return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85 			else
86 				return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87 		}
88 		else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
89 			return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90 		else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
91 			return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92 		else
93 			return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
94 	}
95 	else
96 		return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
97 }
98 
99 /*
100 ==================
101 SV_TracePoint
102 ==================
103 */
SV_TracePoint(const vec3_t start,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask)104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask)
105 {
106 	prvm_prog_t *prog = SVVM_prog;
107 	int i, bodysupercontents;
108 	int passedictprog;
109 	float pitchsign = 1;
110 	prvm_edict_t *traceowner, *touch;
111 	trace_t trace;
112 	// temporary storage because prvm_vec_t may differ from vec_t
113 	vec3_t touchmins, touchmaxs;
114 	// bounding box of entire move area
115 	vec3_t clipboxmins, clipboxmaxs;
116 	// size when clipping against monsters
117 	vec3_t clipmins2, clipmaxs2;
118 	// start and end origin of move
119 	vec3_t clipstart;
120 	// trace results
121 	trace_t cliptrace;
122 	// matrices to transform into/out of other entity's space
123 	matrix4x4_t matrix, imatrix;
124 	// model of other entity
125 	dp_model_t *model;
126 	// list of entities to test for collisions
127 	int numtouchedicts;
128 	static prvm_edict_t *touchedicts[MAX_EDICTS];
129 
130 	//return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
131 
132 	VectorCopy(start, clipstart);
133 	VectorClear(clipmins2);
134 	VectorClear(clipmaxs2);
135 #if COLLISIONPARANOID >= 3
136 	Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
137 #endif
138 
139 	// clip to world
140 	Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask);
141 	cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
142 	if (cliptrace.startsolid || cliptrace.fraction < 1)
143 		cliptrace.ent = prog->edicts;
144 	if (type == MOVE_WORLDONLY)
145 		goto finished;
146 
147 	if (type == MOVE_MISSILE)
148 	{
149 		// LordHavoc: modified this, was = -15, now -= 15
150 		for (i = 0;i < 3;i++)
151 		{
152 			clipmins2[i] -= 15;
153 			clipmaxs2[i] += 15;
154 		}
155 	}
156 
157 	// create the bounding box of the entire move
158 	for (i = 0;i < 3;i++)
159 	{
160 		clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
161 		clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
162 	}
163 
164 	// debug override to test against everything
165 	if (sv_debugmove.integer)
166 	{
167 		clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
168 		clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
169 	}
170 
171 	// if the passedict is world, make it NULL (to avoid two checks each time)
172 	if (passedict == prog->edicts)
173 		passedict = NULL;
174 	// precalculate prog value for passedict for comparisons
175 	passedictprog = PRVM_EDICT_TO_PROG(passedict);
176 	// precalculate passedict's owner edict pointer for comparisons
177 	traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
178 
179 	// clip to entities
180 	// because this uses World_EntitiestoBox, we know all entity boxes overlap
181 	// the clip region, so we can skip culling checks in the loop below
182 	numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
183 	if (numtouchedicts > MAX_EDICTS)
184 	{
185 		// this never happens
186 		Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
187 		numtouchedicts = MAX_EDICTS;
188 	}
189 	for (i = 0;i < numtouchedicts;i++)
190 	{
191 		touch = touchedicts[i];
192 
193 		if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
194 			continue;
195 		if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
196 			continue;
197 
198 		if (passedict)
199 		{
200 			// don't clip against self
201 			if (passedict == touch)
202 				continue;
203 			// don't clip owned entities against owner
204 			if (traceowner == touch)
205 				continue;
206 			// don't clip owner against owned entities
207 			if (passedictprog == PRVM_serveredictedict(touch, owner))
208 				continue;
209 			// don't clip points against points (they can't collide)
210 			if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
211 				continue;
212 		}
213 
214 		bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
215 
216 		// might interact, so do an exact clip
217 		model = NULL;
218 		if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
219 		{
220 			model = SV_GetModelFromEdict(touch);
221 			pitchsign = SV_GetPitchSign(prog, touch);
222 		}
223 		if (model)
224 			Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
225 		else
226 			Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
227 		Matrix4x4_Invert_Simple(&imatrix, &matrix);
228 		VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
229 		VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
230 		VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
231 		VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
232 		VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
233 		if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
234 			Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, skipsupercontentsmask, 0.0f);
235 		else
236 			Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask);
237 
238 		Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
239 	}
240 
241 finished:
242 	return cliptrace;
243 }
244 
245 /*
246 ==================
247 SV_TraceLine
248 ==================
249 */
SV_TraceLine(const vec3_t start,const vec3_t end,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask,float extend)250 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend)
251 {
252 	prvm_prog_t *prog = SVVM_prog;
253 	int i, bodysupercontents;
254 	int passedictprog;
255 	float pitchsign = 1;
256 	prvm_edict_t *traceowner, *touch;
257 	trace_t trace;
258 	// temporary storage because prvm_vec_t may differ from vec_t
259 	vec3_t touchmins, touchmaxs;
260 	// bounding box of entire move area
261 	vec3_t clipboxmins, clipboxmaxs;
262 	// size when clipping against monsters
263 	vec3_t clipmins2, clipmaxs2;
264 	// start and end origin of move
265 	vec3_t clipstart, clipend;
266 	// trace results
267 	trace_t cliptrace;
268 	// matrices to transform into/out of other entity's space
269 	matrix4x4_t matrix, imatrix;
270 	// model of other entity
271 	dp_model_t *model;
272 	// list of entities to test for collisions
273 	int numtouchedicts;
274 	static prvm_edict_t *touchedicts[MAX_EDICTS];
275 	if (VectorCompare(start, end))
276 		return SV_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
277 
278 	//return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
279 
280 	VectorCopy(start, clipstart);
281 	VectorCopy(end, clipend);
282 	VectorClear(clipmins2);
283 	VectorClear(clipmaxs2);
284 #if COLLISIONPARANOID >= 3
285 	Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
286 #endif
287 
288 	// clip to world
289 	Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, extend, false);
290 	cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
291 	if (cliptrace.startsolid || cliptrace.fraction < 1)
292 		cliptrace.ent = prog->edicts;
293 	if (type == MOVE_WORLDONLY)
294 		goto finished;
295 
296 	if (type == MOVE_MISSILE)
297 	{
298 		// LordHavoc: modified this, was = -15, now -= 15
299 		for (i = 0;i < 3;i++)
300 		{
301 			clipmins2[i] -= 15;
302 			clipmaxs2[i] += 15;
303 		}
304 	}
305 
306 	// create the bounding box of the entire move
307 	for (i = 0;i < 3;i++)
308 	{
309 		clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
310 		clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
311 	}
312 
313 	// debug override to test against everything
314 	if (sv_debugmove.integer)
315 	{
316 		clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
317 		clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
318 	}
319 
320 	// if the passedict is world, make it NULL (to avoid two checks each time)
321 	if (passedict == prog->edicts)
322 		passedict = NULL;
323 	// precalculate prog value for passedict for comparisons
324 	passedictprog = PRVM_EDICT_TO_PROG(passedict);
325 	// precalculate passedict's owner edict pointer for comparisons
326 	traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
327 
328 	// clip to entities
329 	// because this uses World_EntitiestoBox, we know all entity boxes overlap
330 	// the clip region, so we can skip culling checks in the loop below
331 	numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
332 	if (numtouchedicts > MAX_EDICTS)
333 	{
334 		// this never happens
335 		Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
336 		numtouchedicts = MAX_EDICTS;
337 	}
338 	for (i = 0;i < numtouchedicts;i++)
339 	{
340 		touch = touchedicts[i];
341 
342 		if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
343 			continue;
344 		if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
345 			continue;
346 
347 		if (passedict)
348 		{
349 			// don't clip against self
350 			if (passedict == touch)
351 				continue;
352 			// don't clip owned entities against owner
353 			if (traceowner == touch)
354 				continue;
355 			// don't clip owner against owned entities
356 			if (passedictprog == PRVM_serveredictedict(touch, owner))
357 				continue;
358 			// don't clip points against points (they can't collide)
359 			if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
360 				continue;
361 		}
362 
363 		bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
364 
365 		// might interact, so do an exact clip
366 		model = NULL;
367 		if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
368 		{
369 			model = SV_GetModelFromEdict(touch);
370 			pitchsign = SV_GetPitchSign(prog, touch);
371 		}
372 		if (model)
373 			Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
374 		else
375 			Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
376 		Matrix4x4_Invert_Simple(&imatrix, &matrix);
377 		VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
378 		VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
379 		VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
380 		VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
381 		VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
382 		if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
383 			Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
384 		else
385 			Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, extend, false);
386 
387 		Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
388 	}
389 
390 finished:
391 	return cliptrace;
392 }
393 
394 /*
395 ==================
396 SV_Move
397 ==================
398 */
399 #if COLLISIONPARANOID >= 1
SV_TraceBox_(const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask,float extend)400 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend)
401 #else
402 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend)
403 #endif
404 {
405 	prvm_prog_t *prog = SVVM_prog;
406 	vec3_t hullmins, hullmaxs;
407 	int i, bodysupercontents;
408 	int passedictprog;
409 	float pitchsign = 1;
410 	qboolean pointtrace;
411 	prvm_edict_t *traceowner, *touch;
412 	trace_t trace;
413 	// temporary storage because prvm_vec_t may differ from vec_t
414 	vec3_t touchmins, touchmaxs;
415 	// bounding box of entire move area
416 	vec3_t clipboxmins, clipboxmaxs;
417 	// size of the moving object
418 	vec3_t clipmins, clipmaxs;
419 	// size when clipping against monsters
420 	vec3_t clipmins2, clipmaxs2;
421 	// start and end origin of move
422 	vec3_t clipstart, clipend;
423 	// trace results
424 	trace_t cliptrace;
425 	// matrices to transform into/out of other entity's space
426 	matrix4x4_t matrix, imatrix;
427 	// model of other entity
428 	dp_model_t *model;
429 	// list of entities to test for collisions
430 	int numtouchedicts;
431 	static prvm_edict_t *touchedicts[MAX_EDICTS];
432 	if (VectorCompare(mins, maxs))
433 	{
434 		vec3_t shiftstart, shiftend;
435 		VectorAdd(start, mins, shiftstart);
436 		VectorAdd(end, mins, shiftend);
437 		if (VectorCompare(start, end))
438 			trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
439 		else
440 			trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, extend);
441 		VectorSubtract(trace.endpos, mins, trace.endpos);
442 		return trace;
443 	}
444 
445 	VectorCopy(start, clipstart);
446 	VectorCopy(end, clipend);
447 	VectorCopy(mins, clipmins);
448 	VectorCopy(maxs, clipmaxs);
449 	VectorCopy(mins, clipmins2);
450 	VectorCopy(maxs, clipmaxs2);
451 #if COLLISIONPARANOID >= 3
452 	Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
453 #endif
454 
455 	// clip to world
456 	Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
457 	cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
458 	if (cliptrace.startsolid || cliptrace.fraction < 1)
459 		cliptrace.ent = prog->edicts;
460 	if (type == MOVE_WORLDONLY)
461 		goto finished;
462 
463 	if (type == MOVE_MISSILE)
464 	{
465 		// LordHavoc: modified this, was = -15, now -= 15
466 		for (i = 0;i < 3;i++)
467 		{
468 			clipmins2[i] -= 15;
469 			clipmaxs2[i] += 15;
470 		}
471 	}
472 
473 	// get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
474 	if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
475 		sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
476 	else
477 	{
478 		VectorCopy(clipmins, hullmins);
479 		VectorCopy(clipmaxs, hullmaxs);
480 	}
481 
482 	// create the bounding box of the entire move
483 	for (i = 0;i < 3;i++)
484 	{
485 		clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
486 		clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
487 	}
488 
489 	// debug override to test against everything
490 	if (sv_debugmove.integer)
491 	{
492 		clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
493 		clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
494 	}
495 
496 	// if the passedict is world, make it NULL (to avoid two checks each time)
497 	if (passedict == prog->edicts)
498 		passedict = NULL;
499 	// precalculate prog value for passedict for comparisons
500 	passedictprog = PRVM_EDICT_TO_PROG(passedict);
501 	// figure out whether this is a point trace for comparisons
502 	pointtrace = VectorCompare(clipmins, clipmaxs);
503 	// precalculate passedict's owner edict pointer for comparisons
504 	traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
505 
506 	// clip to entities
507 	// because this uses World_EntitiestoBox, we know all entity boxes overlap
508 	// the clip region, so we can skip culling checks in the loop below
509 	numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
510 	if (numtouchedicts > MAX_EDICTS)
511 	{
512 		// this never happens
513 		Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
514 		numtouchedicts = MAX_EDICTS;
515 	}
516 	for (i = 0;i < numtouchedicts;i++)
517 	{
518 		touch = touchedicts[i];
519 
520 		if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
521 			continue;
522 		if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
523 			continue;
524 
525 		if (passedict)
526 		{
527 			// don't clip against self
528 			if (passedict == touch)
529 				continue;
530 			// don't clip owned entities against owner
531 			if (traceowner == touch)
532 				continue;
533 			// don't clip owner against owned entities
534 			if (passedictprog == PRVM_serveredictedict(touch, owner))
535 				continue;
536 			// don't clip points against points (they can't collide)
537 			if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
538 				continue;
539 		}
540 
541 		bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
542 
543 		// might interact, so do an exact clip
544 		model = NULL;
545 		if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
546 		{
547 			model = SV_GetModelFromEdict(touch);
548 			pitchsign = SV_GetPitchSign(prog, touch);
549 		}
550 		if (model)
551 			Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
552 		else
553 			Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
554 		Matrix4x4_Invert_Simple(&imatrix, &matrix);
555 		VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
556 		VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
557 		VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
558 		VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
559 		VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
560 		if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
561 			Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
562 		else
563 			Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
564 
565 		Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
566 	}
567 
568 finished:
569 	return cliptrace;
570 }
571 
572 #if COLLISIONPARANOID >= 1
SV_TraceBox(const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask)573 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask)
574 {
575 	prvm_prog_t *prog = SVVM_prog;
576 	int endstuck;
577 	trace_t trace;
578 	vec3_t temp;
579 	trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
580 	if (passedict)
581 	{
582 		VectorCopy(trace.endpos, temp);
583 		endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask).startsolid;
584 #if COLLISIONPARANOID < 3
585 		if (trace.startsolid || endstuck)
586 #endif
587 			Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, PRVM_serveredictvector(passedict, origin)[0], PRVM_serveredictvector(passedict, origin)[1], PRVM_serveredictvector(passedict, origin)[2], end[0] - PRVM_serveredictvector(passedict, origin)[0], end[1] - PRVM_serveredictvector(passedict, origin)[1], end[2] - PRVM_serveredictvector(passedict, origin)[2], trace.fraction, trace.endpos[0] - PRVM_serveredictvector(passedict, origin)[0], trace.endpos[1] - PRVM_serveredictvector(passedict, origin)[1], trace.endpos[2] - PRVM_serveredictvector(passedict, origin)[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
588 	}
589 	return trace;
590 }
591 #endif
592 
SV_PointSuperContents(const vec3_t point)593 int SV_PointSuperContents(const vec3_t point)
594 {
595 	prvm_prog_t *prog = SVVM_prog;
596 	int supercontents = 0;
597 	int i;
598 	prvm_edict_t *touch;
599 	vec3_t transformed;
600 	// matrices to transform into/out of other entity's space
601 	matrix4x4_t matrix, imatrix;
602 	// model of other entity
603 	dp_model_t *model;
604 	int frame;
605 	// list of entities to test for collisions
606 	int numtouchedicts;
607 	static prvm_edict_t *touchedicts[MAX_EDICTS];
608 
609 	// get world supercontents at this point
610 	if (sv.worldmodel && sv.worldmodel->PointSuperContents)
611 		supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
612 
613 	// if sv_gameplayfix_swiminbmodels is off we're done
614 	if (!sv_gameplayfix_swiminbmodels.integer)
615 		return supercontents;
616 
617 	// get list of entities at this point
618 	numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
619 	if (numtouchedicts > MAX_EDICTS)
620 	{
621 		// this never happens
622 		Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
623 		numtouchedicts = MAX_EDICTS;
624 	}
625 	for (i = 0;i < numtouchedicts;i++)
626 	{
627 		touch = touchedicts[i];
628 
629 		// we only care about SOLID_BSP for pointcontents
630 		if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
631 			continue;
632 
633 		// might interact, so do an exact clip
634 		model = SV_GetModelFromEdict(touch);
635 		if (!model || !model->PointSuperContents)
636 			continue;
637 		Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
638 		Matrix4x4_Invert_Simple(&imatrix, &matrix);
639 		Matrix4x4_Transform(&imatrix, point, transformed);
640 		frame = (int)PRVM_serveredictfloat(touch, frame);
641 		supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
642 	}
643 
644 	return supercontents;
645 }
646 
647 /*
648 ===============================================================================
649 
650 Linking entities into the world culling system
651 
652 ===============================================================================
653 */
654 
SV_EntitiesInBox(const vec3_t mins,const vec3_t maxs,int maxedicts,prvm_edict_t ** resultedicts)655 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
656 {
657 	prvm_prog_t *prog = SVVM_prog;
658 	vec3_t paddedmins, paddedmaxs;
659 	if (maxedicts < 1 || resultedicts == NULL)
660 		return 0;
661 	// LordHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
662 	//VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
663 	//VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
664 	VectorCopy(mins, paddedmins);
665 	VectorCopy(maxs, paddedmaxs);
666 	if (sv_areadebug.integer)
667 	{
668 		int numresultedicts = 0;
669 		int edictindex;
670 		prvm_edict_t *ed;
671 		for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
672 		{
673 			ed = PRVM_EDICT_NUM(edictindex);
674 			if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
675 			{
676 				resultedicts[numresultedicts++] = ed;
677 				if (numresultedicts == maxedicts)
678 					break;
679 			}
680 		}
681 		return numresultedicts;
682 	}
683 	else
684 		return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
685 }
686 
SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t * touch,prvm_edict_t * ent)687 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
688 {
689 	prvm_prog_t *prog = SVVM_prog;
690 	PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
691 	PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
692 	PRVM_serverglobalfloat(time) = sv.time;
693 	PRVM_serverglobalfloat(trace_allsolid) = false;
694 	PRVM_serverglobalfloat(trace_startsolid) = false;
695 	PRVM_serverglobalfloat(trace_fraction) = 1;
696 	PRVM_serverglobalfloat(trace_inwater) = false;
697 	PRVM_serverglobalfloat(trace_inopen) = true;
698 	VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
699 	VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
700 	PRVM_serverglobalfloat(trace_plane_dist) = 0;
701 	PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
702 	PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
703 	PRVM_serverglobalfloat(trace_dphitcontents) = 0;
704 	PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
705 	PRVM_serverglobalstring(trace_dphittexturename) = 0;
706 	prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
707 }
708 
SV_LinkEdict_TouchAreaGrid(prvm_edict_t * ent)709 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
710 {
711 	prvm_prog_t *prog = SVVM_prog;
712 	int i, numtouchedicts, old_self, old_other;
713 	prvm_edict_t *touch;
714 	static prvm_edict_t *touchedicts[MAX_EDICTS];
715 
716 	if (ent == prog->edicts)
717 		return;		// don't add the world
718 
719 	if (ent->priv.server->free)
720 		return;
721 
722 	if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
723 		return;
724 
725 	// build a list of edicts to touch, because the link loop can be corrupted
726 	// by IncreaseEdicts called during touch functions
727 	numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
728 	if (numtouchedicts > MAX_EDICTS)
729 	{
730 		// this never happens
731 		Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
732 		numtouchedicts = MAX_EDICTS;
733 	}
734 
735 	old_self = PRVM_serverglobaledict(self);
736 	old_other = PRVM_serverglobaledict(other);
737 	for (i = 0;i < numtouchedicts;i++)
738 	{
739 		touch = touchedicts[i];
740 		if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
741 		{
742 			SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
743 		}
744 	}
745 	PRVM_serverglobaledict(self) = old_self;
746 	PRVM_serverglobaledict(other) = old_other;
747 }
748 
RotateBBox(const vec3_t mins,const vec3_t maxs,const vec3_t angles,vec3_t rotatedmins,vec3_t rotatedmaxs)749 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
750 {
751 	vec3_t v, u;
752 	matrix4x4_t m;
753 	Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
754 
755 	v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
756 		VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
757 	v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
758 		if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
759 		if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
760 	v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
761 		if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
762 		if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
763 	v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
764 		if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
765 		if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
766 	v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
767 		if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
768 		if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
769 	v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
770 		if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
771 		if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
772 	v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
773 		if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
774 		if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
775 	v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
776 		if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
777 		if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
778 }
779 
780 /*
781 ===============
782 SV_LinkEdict
783 
784 ===============
785 */
SV_LinkEdict(prvm_edict_t * ent)786 void SV_LinkEdict (prvm_edict_t *ent)
787 {
788 	prvm_prog_t *prog = SVVM_prog;
789 	dp_model_t *model;
790 	vec3_t mins, maxs, entmins, entmaxs, entangles;
791 	int modelindex;
792 
793 	if (ent == prog->edicts)
794 		return;		// don't add the world
795 
796 	if (ent->priv.server->free)
797 		return;
798 
799 	modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
800 	if (modelindex < 0 || modelindex >= MAX_MODELS)
801 	{
802 		Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
803 		modelindex = 0;
804 	}
805 	model = SV_GetModelByIndex(modelindex);
806 
807 	VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
808 	VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
809 	VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
810 
811 // set the abs box
812 
813 	if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
814 	{
815 		// TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
816 		// TODO special handling for spheres?
817 		VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
818 		VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
819 		VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
820 		RotateBBox(entmins, entmaxs, entangles, mins, maxs);
821 		VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
822 		VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
823 	}
824 	else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
825 	{
826 		if (model != NULL)
827 		{
828 			if (!model->TraceBox)
829 				Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
830 
831 			if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
832 			{
833 				VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
834 				VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
835 			}
836 			else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
837 			{
838 				VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
839 				VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
840 			}
841 			else
842 			{
843 				VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
844 				VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
845 			}
846 		}
847 		else
848 		{
849 			// SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
850 			VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
851 			VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
852 		}
853 	}
854 	else
855 	{
856 		VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
857 		VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
858 	}
859 
860 //
861 // to make items easier to pick up and allow them to be grabbed off
862 // of shelves, the abs sizes are expanded
863 //
864 	if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
865 	{
866 		mins[0] -= 15;
867 		mins[1] -= 15;
868 		mins[2] -= 1;
869 		maxs[0] += 15;
870 		maxs[1] += 15;
871 		maxs[2] += 1;
872 	}
873 	else
874 	{
875 		// because movement is clipped an epsilon away from an actual edge,
876 		// we must fully check even when bounding boxes don't quite touch
877 		mins[0] -= 1;
878 		mins[1] -= 1;
879 		mins[2] -= 1;
880 		maxs[0] += 1;
881 		maxs[1] += 1;
882 		maxs[2] += 1;
883 	}
884 
885 	VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
886 	VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
887 
888 	World_LinkEdict(&sv.world, ent, mins, maxs);
889 }
890 
891 /*
892 ===============================================================================
893 
894 Utility functions
895 
896 ===============================================================================
897 */
898 
899 /*
900 ============
901 SV_TestEntityPosition
902 
903 returns true if the entity is in solid currently
904 ============
905 */
SV_TestEntityPosition(prvm_edict_t * ent,vec3_t offset)906 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
907 {
908 	prvm_prog_t *prog = SVVM_prog;
909 	int contents;
910 	vec3_t org, entorigin, entmins, entmaxs;
911 	trace_t trace;
912 	contents = SV_GenericHitSuperContentsMask(ent);
913 	VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
914 	VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
915 	VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
916 	VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
917 	trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents, 0, collision_extendmovelength.value);
918 	if (trace.startsupercontents & contents)
919 		return true;
920 	else
921 	{
922 		if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
923 		{
924 			// q1bsp/hlbsp use hulls and if the entity does not exactly match
925 			// a hull size it is incorrectly tested, so this code tries to
926 			// 'fix' it slightly...
927 			// FIXME: this breaks entities larger than the hull size
928 			int i;
929 			vec3_t v, m1, m2, s;
930 			VectorAdd(org, entmins, m1);
931 			VectorAdd(org, entmaxs, m2);
932 			VectorSubtract(m2, m1, s);
933 #define EPSILON (1.0f / 32.0f)
934 			if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
935 			if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
936 			if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
937 			for (i = 0;i < 8;i++)
938 			{
939 				v[0] = (i & 1) ? m2[0] : m1[0];
940 				v[1] = (i & 2) ? m2[1] : m1[1];
941 				v[2] = (i & 4) ? m2[2] : m1[2];
942 				if (SV_PointSuperContents(v) & contents)
943 					return true;
944 			}
945 		}
946 	}
947 	// if the trace found a better position for the entity, move it there
948 	if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
949 	{
950 #if 0
951 		// please switch back to this code when trace.endpos sometimes being in solid bug is fixed
952 		VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
953 #else
954 		// verify if the endpos is REALLY outside solid
955 		VectorCopy(trace.endpos, org);
956 		trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, contents, 0, collision_extendmovelength.value);
957 		if(trace.startsolid)
958 			Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
959 		else
960 			VectorCopy(org, PRVM_serveredictvector(ent, origin));
961 #endif
962 	}
963 	return false;
964 }
965 
966 // DRESK - Support for Entity Contents Transition Event
967 /*
968 ================
969 SV_CheckContentsTransition
970 
971 returns true if entity had a valid contentstransition function call
972 ================
973 */
SV_CheckContentsTransition(prvm_edict_t * ent,const int nContents)974 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
975 {
976 	prvm_prog_t *prog = SVVM_prog;
977 	int bValidFunctionCall;
978 
979 	// Default Valid Function Call to False
980 	bValidFunctionCall = false;
981 
982 	if(PRVM_serveredictfloat(ent, watertype) != nContents)
983 	{ // Changed Contents
984 		// Acquire Contents Transition Function from QC
985 		if(PRVM_serveredictfunction(ent, contentstransition))
986 		{ // Valid Function; Execute
987 			// Assign Valid Function
988 			bValidFunctionCall = true;
989 			// Prepare Parameters (Original Contents, New Contents)
990 			// Original Contents
991 			PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
992 			// New Contents
993 			PRVM_G_FLOAT(OFS_PARM1) = nContents;
994 			// Assign Self
995 			PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
996 			// Set Time
997 			PRVM_serverglobalfloat(time) = sv.time;
998 			// Execute VM Function
999 			prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1000 		}
1001 	}
1002 
1003 	// Return if Function Call was Valid
1004 	return bValidFunctionCall;
1005 }
1006 
1007 
1008 /*
1009 ================
1010 SV_CheckVelocity
1011 ================
1012 */
SV_CheckVelocity(prvm_edict_t * ent)1013 void SV_CheckVelocity (prvm_edict_t *ent)
1014 {
1015 	prvm_prog_t *prog = SVVM_prog;
1016 	int i;
1017 	float wishspeed;
1018 
1019 //
1020 // bound velocity
1021 //
1022 	for (i=0 ; i<3 ; i++)
1023 	{
1024 		if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1025 		{
1026 			Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1027 			PRVM_serveredictvector(ent, velocity)[i] = 0;
1028 		}
1029 		if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1030 		{
1031 			Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1032 			PRVM_serveredictvector(ent, origin)[i] = 0;
1033 		}
1034 	}
1035 
1036 	// LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1037 	// player_run/player_stand1 does not horribly malfunction if the
1038 	// velocity becomes a denormalized float
1039 	if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1040 		VectorClear(PRVM_serveredictvector(ent, velocity));
1041 
1042 	// LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1043 	wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1044 	if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1045 	{
1046 		wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1047 		PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1048 		PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1049 		PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1050 	}
1051 }
1052 
1053 /*
1054 =============
1055 SV_RunThink
1056 
1057 Runs thinking code if time.  There is some play in the exact time the think
1058 function will be called, because it is called before any movement is done
1059 in a frame.  Not used for pushmove objects, because they must be exact.
1060 Returns false if the entity removed itself.
1061 =============
1062 */
SV_RunThink(prvm_edict_t * ent)1063 static qboolean SV_RunThink (prvm_edict_t *ent)
1064 {
1065 	prvm_prog_t *prog = SVVM_prog;
1066 	int iterations;
1067 
1068 	// don't let things stay in the past.
1069 	// it is possible to start that way by a trigger with a local time.
1070 	if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1071 		return true;
1072 
1073 	for (iterations = 0;iterations < 128  && !ent->priv.server->free;iterations++)
1074 	{
1075 		PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1076 		PRVM_serveredictfloat(ent, nextthink) = 0;
1077 		PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1078 		PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1079 		prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1080 		// mods often set nextthink to time to cause a think every frame,
1081 		// we don't want to loop in that case, so exit if the new nextthink is
1082 		// <= the time the qc was told, also exit if it is past the end of the
1083 		// frame
1084 		if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1085 			break;
1086 	}
1087 	return !ent->priv.server->free;
1088 }
1089 
1090 /*
1091 ==================
1092 SV_Impact
1093 
1094 Two entities have touched, so run their touch functions
1095 ==================
1096 */
SV_Impact(prvm_edict_t * e1,trace_t * trace)1097 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1098 {
1099 	prvm_prog_t *prog = SVVM_prog;
1100 	int restorevm_tempstringsbuf_cursize;
1101 	int old_self, old_other;
1102 	prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1103 
1104 	old_self = PRVM_serverglobaledict(self);
1105 	old_other = PRVM_serverglobaledict(other);
1106 	restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1107 
1108 	VM_SetTraceGlobals(prog, trace);
1109 
1110 	if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1111 	{
1112 		PRVM_serverglobalfloat(time) = sv.time;
1113 		PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1114 		PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1115 		prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1116 	}
1117 
1118 	if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1119 	{
1120 		PRVM_serverglobalfloat(time) = sv.time;
1121 		PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1122 		PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1123 		VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1124 		VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1125 		PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1126 		PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1127 		PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1128 		PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1129 		PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1130 		PRVM_serverglobalstring(trace_dphittexturename) = 0;
1131 		prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1132 	}
1133 
1134 	PRVM_serverglobaledict(self) = old_self;
1135 	PRVM_serverglobaledict(other) = old_other;
1136 	prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1137 }
1138 
1139 
1140 /*
1141 ==================
1142 ClipVelocity
1143 
1144 Slide off of the impacting object
1145 returns the blocked flags (1 = floor, 2 = step / wall)
1146 ==================
1147 */
1148 #define STOP_EPSILON 0.1
ClipVelocity(prvm_vec3_t in,vec3_t normal,prvm_vec3_t out,prvm_vec_t overbounce)1149 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1150 {
1151 	int i;
1152 	float backoff;
1153 
1154 	backoff = -DotProduct (in, normal) * overbounce;
1155 	VectorMA(in, backoff, normal, out);
1156 
1157 	for (i = 0;i < 3;i++)
1158 		if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1159 			out[i] = 0;
1160 }
1161 
1162 
1163 /*
1164 ============
1165 SV_FlyMove
1166 
1167 The basic solid body movement clip that slides along multiple planes
1168 Returns the clipflags if the velocity was modified (hit something solid)
1169 1 = floor
1170 2 = wall / step
1171 4 = dead stop
1172 8 = teleported by touch method
1173 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1174 ============
1175 */
1176 static float SV_Gravity (prvm_edict_t *ent);
1177 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1178 #define MAX_CLIP_PLANES 5
SV_FlyMove(prvm_edict_t * ent,float time,qboolean applygravity,float * stepnormal,int hitsupercontentsmask,int skipsupercontentsmask,float stepheight)1179 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, float stepheight)
1180 {
1181 	prvm_prog_t *prog = SVVM_prog;
1182 	int blocked, bumpcount;
1183 	int i, j, numplanes;
1184 	float d, time_left, gravity;
1185 	vec3_t dir, push, planes[MAX_CLIP_PLANES];
1186 	prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1187 #if 0
1188 	vec3_t end;
1189 #endif
1190 	trace_t trace;
1191 	if (time <= 0)
1192 		return 0;
1193 	gravity = 0;
1194 
1195 	VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1196 
1197 	if(applygravity)
1198 	{
1199 		gravity = SV_Gravity(ent);
1200 
1201 		if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1202 		{
1203 			if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1204 				PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1205 			else
1206 				PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1207 		}
1208 	}
1209 
1210 	blocked = 0;
1211 	VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1212 	VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1213 	numplanes = 0;
1214 	time_left = time;
1215 	for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1216 	{
1217 		if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1218 			break;
1219 
1220 		VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1221 		if(!SV_PushEntity(&trace, ent, push, false))
1222 		{
1223 			// we got teleported by a touch function
1224 			// let's abort the move
1225 			blocked |= 8;
1226 			break;
1227 		}
1228 
1229 		// this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1230 		// abort move if we're stuck in the world (and didn't make it out)
1231 		if (trace.worldstartsolid && trace.allsolid)
1232 		{
1233 			VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1234 			return 3;
1235 		}
1236 
1237 		if (trace.fraction == 1)
1238 			break;
1239 		if (trace.plane.normal[2])
1240 		{
1241 			if (trace.plane.normal[2] > 0.7)
1242 			{
1243 				// floor
1244 				blocked |= 1;
1245 
1246 				if (!trace.ent)
1247 				{
1248 					Con_Printf ("SV_FlyMove: !trace.ent");
1249 					trace.ent = prog->edicts;
1250 				}
1251 
1252 				PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1253 				PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1254 			}
1255 		}
1256 		else if (stepheight)
1257 		{
1258 			// step - handle it immediately
1259 			vec3_t org;
1260 			vec3_t steppush;
1261 			trace_t steptrace;
1262 			trace_t steptrace2;
1263 			trace_t steptrace3;
1264 			//Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1265 			VectorSet(steppush, 0, 0, stepheight);
1266 			VectorCopy(PRVM_serveredictvector(ent, origin), org);
1267 			if(!SV_PushEntity(&steptrace, ent, steppush, false))
1268 			{
1269 				blocked |= 8;
1270 				break;
1271 			}
1272 			//Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1273 			if(!SV_PushEntity(&steptrace2, ent, push, false))
1274 			{
1275 				blocked |= 8;
1276 				break;
1277 			}
1278 			//Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1279 			VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1280 			if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1281 			{
1282 				blocked |= 8;
1283 				break;
1284 			}
1285 			//Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1286 			// accept the new position if it made some progress...
1287 			if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1288 			{
1289 				//Con_Printf("accepted (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1290 				trace = steptrace2;
1291 				VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1292 				time_left *= 1 - trace.fraction;
1293 				numplanes = 0;
1294 				continue;
1295 			}
1296 			else
1297 			{
1298 				//Con_Printf("REJECTED (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1299 				VectorCopy(org, PRVM_serveredictvector(ent, origin));
1300 			}
1301 		}
1302 		else
1303 		{
1304 			// step - return it to caller
1305 			blocked |= 2;
1306 			// save the trace for player extrafriction
1307 			if (stepnormal)
1308 				VectorCopy(trace.plane.normal, stepnormal);
1309 		}
1310 		if (trace.fraction >= 0.001)
1311 		{
1312 			// actually covered some distance
1313 			VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1314 			numplanes = 0;
1315 		}
1316 
1317 		time_left *= 1 - trace.fraction;
1318 
1319 		// clipped to another plane
1320 		if (numplanes >= MAX_CLIP_PLANES)
1321 		{
1322 			// this shouldn't really happen
1323 			VectorClear(PRVM_serveredictvector(ent, velocity));
1324 			blocked = 3;
1325 			break;
1326 		}
1327 
1328 		/*
1329 		for (i = 0;i < numplanes;i++)
1330 			if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1331 				break;
1332 		if (i < numplanes)
1333 		{
1334 			VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1335 			continue;
1336 		}
1337 		*/
1338 
1339 		VectorCopy(trace.plane.normal, planes[numplanes]);
1340 		numplanes++;
1341 
1342 		// modify original_velocity so it parallels all of the clip planes
1343 		for (i = 0;i < numplanes;i++)
1344 		{
1345 			ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1346 			for (j = 0;j < numplanes;j++)
1347 			{
1348 				if (j != i)
1349 				{
1350 					// not ok
1351 					if (DotProduct(new_velocity, planes[j]) < 0)
1352 						break;
1353 				}
1354 			}
1355 			if (j == numplanes)
1356 				break;
1357 		}
1358 
1359 		if (i != numplanes)
1360 		{
1361 			// go along this plane
1362 			VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1363 		}
1364 		else
1365 		{
1366 			// go along the crease
1367 			if (numplanes != 2)
1368 			{
1369 				VectorClear(PRVM_serveredictvector(ent, velocity));
1370 				blocked = 7;
1371 				break;
1372 			}
1373 			CrossProduct(planes[0], planes[1], dir);
1374 			// LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1375 			VectorNormalize(dir);
1376 			d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1377 			VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1378 		}
1379 
1380 		// if current velocity is against the original velocity,
1381 		// stop dead to avoid tiny occilations in sloping corners
1382 		if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1383 		{
1384 			VectorClear(PRVM_serveredictvector(ent, velocity));
1385 			break;
1386 		}
1387 	}
1388 
1389 	//Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, PRVM_serveredictvector(ent, velocity)[0], PRVM_serveredictvector(ent, velocity)[1], PRVM_serveredictvector(ent, velocity)[2]);
1390 
1391 	/*
1392 	if ((blocked & 1) == 0 && bumpcount > 1)
1393 	{
1394 		// LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1395 		// flag ONGROUND if there's ground under it
1396 		trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask);
1397 	}
1398 	*/
1399 
1400 	// LordHavoc: this came from QW and allows you to get out of water more easily
1401 	if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1402 		VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1403 
1404 	if(applygravity)
1405 	{
1406 		if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1407 		{
1408 			if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1409 				PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1410 		}
1411 	}
1412 
1413 	return blocked;
1414 }
1415 
1416 /*
1417 ============
1418 SV_Gravity
1419 
1420 ============
1421 */
SV_Gravity(prvm_edict_t * ent)1422 static float SV_Gravity (prvm_edict_t *ent)
1423 {
1424 	prvm_prog_t *prog = SVVM_prog;
1425 	float ent_gravity;
1426 
1427 	ent_gravity = PRVM_serveredictfloat(ent, gravity);
1428 	if (!ent_gravity)
1429 		ent_gravity = 1.0f;
1430 	return ent_gravity * sv_gravity.value * sv.frametime;
1431 }
1432 
1433 
1434 /*
1435 ===============================================================================
1436 
1437 PUSHMOVE
1438 
1439 ===============================================================================
1440 */
1441 
SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t * ent,vec3_t pivot)1442 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1443 {
1444 	prvm_prog_t *prog = SVVM_prog;
1445 	int bump;
1446 	trace_t stucktrace;
1447 	vec3_t stuckorigin;
1448 	vec3_t stuckmins, stuckmaxs;
1449 	vec3_t goodmins, goodmaxs;
1450 	vec3_t testorigin;
1451 	vec_t nudge;
1452 	vec3_t move;
1453 	VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1454 	VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1455 	VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1456 	VectorCopy(pivot, goodmins);
1457 	VectorCopy(pivot, goodmaxs);
1458 	for (bump = 0;bump < 6;bump++)
1459 	{
1460 		int coord = 2-(bump >> 1);
1461 		//int coord = (bump >> 1);
1462 		int dir = (bump & 1);
1463 		int subbump;
1464 
1465 		for(subbump = 0; ; ++subbump)
1466 		{
1467 			VectorCopy(stuckorigin, testorigin);
1468 			if(dir)
1469 			{
1470 				// pushing maxs
1471 				testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1472 			}
1473 			else
1474 			{
1475 				// pushing mins
1476 				testorigin[coord] += stuckmins[coord] - goodmins[coord];
1477 			}
1478 
1479 			stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, collision_extendmovelength.value);
1480 			if (stucktrace.bmodelstartsolid)
1481 			{
1482 				// BAD BAD, can't fix that
1483 				return false;
1484 			}
1485 
1486 			if (stucktrace.fraction >= 1)
1487 				break; // it WORKS!
1488 
1489 			if(subbump >= 10)
1490 			{
1491 				// BAD BAD, can't fix that
1492 				return false;
1493 			}
1494 
1495 			// we hit something... let's move out of it
1496 			VectorSubtract(stucktrace.endpos, testorigin, move);
1497 			nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1498 			VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1499 		}
1500 		/*
1501 		if(subbump > 0)
1502 			Con_Printf("subbump: %d\n", subbump);
1503 		*/
1504 
1505 		if(dir)
1506 		{
1507 			// pushing maxs
1508 			goodmaxs[coord] = stuckmaxs[coord];
1509 		}
1510 		else
1511 		{
1512 			// pushing mins
1513 			goodmins[coord] = stuckmins[coord];
1514 		}
1515 	}
1516 
1517 	// WE WIN
1518 	VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1519 
1520 	return true;
1521 }
1522 
SV_NudgeOutOfSolid(prvm_edict_t * ent)1523 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1524 {
1525 	prvm_prog_t *prog = SVVM_prog;
1526 	int bump, pass;
1527 	trace_t stucktrace;
1528 	vec3_t stuckorigin;
1529 	vec3_t stuckmins, stuckmaxs;
1530 	vec_t nudge;
1531 	vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1532 	if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1533 		separation = 0.0f; // when using hulls, it can not be enlarged
1534 	VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1535 	VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1536 	stuckmins[0] -= separation;
1537 	stuckmins[1] -= separation;
1538 	stuckmins[2] -= separation;
1539 	stuckmaxs[0] += separation;
1540 	stuckmaxs[1] += separation;
1541 	stuckmaxs[2] += separation;
1542 	// first pass we try to get it out of brush entities
1543 	// second pass we try to get it out of world only (can't win them all)
1544 	for (pass = 0;pass < 2;pass++)
1545 	{
1546 		VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1547 		for (bump = 0;bump < 10;bump++)
1548 		{
1549 			stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, collision_extendmovelength.value);
1550 			if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1551 			{
1552 				// found a good location, use it
1553 				VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1554 				return true;
1555 			}
1556 			nudge = -stucktrace.startdepth;
1557 			VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1558 		}
1559 	}
1560 	return false;
1561 }
1562 
1563 /*
1564 ============
1565 SV_PushEntity
1566 
1567 Does not change the entities velocity at all
1568 The trace struct is filled with the trace that has been done.
1569 Returns true if the push did not result in the entity being teleported by QC code.
1570 ============
1571 */
SV_PushEntity(trace_t * trace,prvm_edict_t * ent,vec3_t push,qboolean dolink)1572 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1573 {
1574 	prvm_prog_t *prog = SVVM_prog;
1575 	int solid;
1576 	int movetype;
1577 	int type;
1578 	vec3_t mins, maxs;
1579 	vec3_t start;
1580 	vec3_t end;
1581 
1582 	solid = (int)PRVM_serveredictfloat(ent, solid);
1583 	movetype = (int)PRVM_serveredictfloat(ent, movetype);
1584 	VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1585 	VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1586 
1587 	// move start position out of solids
1588 	if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1589 	{
1590 		SV_NudgeOutOfSolid(ent);
1591 	}
1592 
1593 	VectorCopy(PRVM_serveredictvector(ent, origin), start);
1594 	VectorAdd(start, push, end);
1595 
1596 	if (movetype == MOVETYPE_FLYMISSILE)
1597 		type = MOVE_MISSILE;
1598 	else if (movetype == MOVETYPE_FLY_WORLDONLY)
1599 		type = MOVE_WORLDONLY;
1600 	else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1601 		type = MOVE_NOMONSTERS; // only clip against bmodels
1602 	else
1603 		type = MOVE_NORMAL;
1604 
1605 	*trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, collision_extendmovelength.value);
1606 	// fail the move if stuck in world
1607 	if (trace->worldstartsolid)
1608 		return true;
1609 
1610 	VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1611 
1612 	ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1613 
1614 	SV_LinkEdict(ent);
1615 
1616 #if 0
1617 	if(!trace->startsolid)
1618 	if(SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, origin), type, ent, SV_GenericHitSuperContentsMask(ent), 0).startsolid)
1619 	{
1620 		Con_Printf("something eeeeevil happened\n");
1621 	}
1622 #endif
1623 
1624 	if (dolink)
1625 		SV_LinkEdict_TouchAreaGrid(ent);
1626 
1627 	if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
1628 		SV_Impact (ent, trace);
1629 
1630 	if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1631 	{
1632 		ent->priv.required->mark = 0;
1633 		return false;
1634 	}
1635 	else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1636 	{
1637 		ent->priv.required->mark = 0;
1638 		return true;
1639 	}
1640 	else
1641 	{
1642 		Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1643 		return true;
1644 	}
1645 }
1646 
1647 
1648 /*
1649 ============
1650 SV_PushMove
1651 
1652 ============
1653 */
SV_PushMove(prvm_edict_t * pusher,float movetime)1654 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1655 {
1656 	prvm_prog_t *prog = SVVM_prog;
1657 	int i, e, index;
1658 	int pusherowner, pusherprog;
1659 	int checkcontents;
1660 	qboolean rotated;
1661 	float savesolid, movetime2, pushltime;
1662 	vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1663 	int num_moved;
1664 	int numcheckentities;
1665 	static prvm_edict_t *checkentities[MAX_EDICTS];
1666 	dp_model_t *pushermodel;
1667 	trace_t trace, trace2;
1668 	matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1669 	static unsigned short moved_edicts[MAX_EDICTS];
1670 	vec3_t pivot;
1671 
1672 	if (!PRVM_serveredictvector(pusher, velocity)[0] && !PRVM_serveredictvector(pusher, velocity)[1] && !PRVM_serveredictvector(pusher, velocity)[2] && !PRVM_serveredictvector(pusher, avelocity)[0] && !PRVM_serveredictvector(pusher, avelocity)[1] && !PRVM_serveredictvector(pusher, avelocity)[2])
1673 	{
1674 		PRVM_serveredictfloat(pusher, ltime) += movetime;
1675 		return;
1676 	}
1677 
1678 	switch ((int) PRVM_serveredictfloat(pusher, solid))
1679 	{
1680 	// LordHavoc: valid pusher types
1681 	case SOLID_BSP:
1682 	case SOLID_BBOX:
1683 	case SOLID_SLIDEBOX:
1684 	case SOLID_CORPSE: // LordHavoc: this would be weird...
1685 		break;
1686 	// LordHavoc: no collisions
1687 	case SOLID_NOT:
1688 	case SOLID_TRIGGER:
1689 		VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1690 		VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1691 		PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1692 		PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1693 		PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1694 		PRVM_serveredictfloat(pusher, ltime) += movetime;
1695 		SV_LinkEdict(pusher);
1696 		return;
1697 	default:
1698 		Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1699 		return;
1700 	}
1701 	index = (int) PRVM_serveredictfloat(pusher, modelindex);
1702 	if (index < 1 || index >= MAX_MODELS)
1703 	{
1704 		Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1705 		return;
1706 	}
1707 	pushermodel = SV_GetModelByIndex(index);
1708 	pusherowner = PRVM_serveredictedict(pusher, owner);
1709 	pusherprog = PRVM_EDICT_TO_PROG(pusher);
1710 
1711 	rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1712 
1713 	movetime2 = movetime;
1714 	VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1715 	VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1716 	if (moveangle[0] || moveangle[2])
1717 	{
1718 		for (i = 0;i < 3;i++)
1719 		{
1720 			if (move1[i] > 0)
1721 			{
1722 				mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1723 				maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1724 			}
1725 			else
1726 			{
1727 				mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1728 				maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1729 			}
1730 		}
1731 	}
1732 	else if (moveangle[1])
1733 	{
1734 		for (i = 0;i < 3;i++)
1735 		{
1736 			if (move1[i] > 0)
1737 			{
1738 				mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1739 				maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1740 			}
1741 			else
1742 			{
1743 				mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1744 				maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1745 			}
1746 		}
1747 	}
1748 	else
1749 	{
1750 		for (i = 0;i < 3;i++)
1751 		{
1752 			if (move1[i] > 0)
1753 			{
1754 				mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1755 				maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1756 			}
1757 			else
1758 			{
1759 				mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1760 				maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1761 			}
1762 		}
1763 	}
1764 
1765 	VectorNegate (moveangle, a);
1766 	AngleVectorsFLU (a, forward, left, up);
1767 
1768 	VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1769 	VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1770 	pushltime = PRVM_serveredictfloat(pusher, ltime);
1771 
1772 // move the pusher to its final position
1773 
1774 	VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1775 	VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1776 	PRVM_serveredictfloat(pusher, ltime) += movetime;
1777 	SV_LinkEdict(pusher);
1778 
1779 	pushermodel = SV_GetModelFromEdict(pusher);
1780 	Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, PRVM_serveredictvector(pusher, origin)[0], PRVM_serveredictvector(pusher, origin)[1], PRVM_serveredictvector(pusher, origin)[2], PRVM_serveredictvector(pusher, angles)[0], PRVM_serveredictvector(pusher, angles)[1], PRVM_serveredictvector(pusher, angles)[2], 1);
1781 	Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1782 
1783 	savesolid = PRVM_serveredictfloat(pusher, solid);
1784 
1785 // see if any solid entities are inside the final position
1786 	num_moved = 0;
1787 
1788 	if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1789 		numcheckentities = 0;
1790 	else // MOVETYPE_PUSH
1791 	        numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1792 	for (e = 0;e < numcheckentities;e++)
1793 	{
1794 		prvm_edict_t *check = checkentities[e];
1795 		int movetype = (int)PRVM_serveredictfloat(check, movetype);
1796 		switch(movetype)
1797 		{
1798 		case MOVETYPE_NONE:
1799 		case MOVETYPE_PUSH:
1800 		case MOVETYPE_FOLLOW:
1801 		case MOVETYPE_NOCLIP:
1802 		case MOVETYPE_FLY_WORLDONLY:
1803 			continue;
1804 		default:
1805 			break;
1806 		}
1807 
1808 		if (PRVM_serveredictedict(check, owner) == pusherprog)
1809 			continue;
1810 
1811 		if (pusherowner == PRVM_EDICT_TO_PROG(check))
1812 			continue;
1813 
1814 		//Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1815 
1816 		// tell any MOVETYPE_STEP entity that it may need to check for water transitions
1817 		check->priv.server->waterposition_forceupdate = true;
1818 
1819 		checkcontents = SV_GenericHitSuperContentsMask(check);
1820 
1821 		// if the entity is standing on the pusher, it will definitely be moved
1822 		// if the entity is not standing on the pusher, but is in the pusher's
1823 		// final position, move it
1824 		if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1825 		{
1826 			VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1827 			VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1828 			VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1829 			VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1830 			VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1831 			Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, 0, collision_extendmovelength.value);
1832 			//trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1833 			if (!trace.startsolid)
1834 			{
1835 				//Con_Printf("- not in solid\n");
1836 				continue;
1837 			}
1838 		}
1839 
1840 		VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1841 		//VectorClear(pivot);
1842 
1843 		if (rotated)
1844 		{
1845 			vec3_t org2;
1846 			VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1847 			VectorAdd (org, pivot, org);
1848 			org2[0] = DotProduct (org, forward);
1849 			org2[1] = DotProduct (org, left);
1850 			org2[2] = DotProduct (org, up);
1851 			VectorSubtract (org2, org, move);
1852 			VectorAdd (move, move1, move);
1853 		}
1854 		else
1855 			VectorCopy (move1, move);
1856 
1857 		//Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1858 
1859 		VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1860 		VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1861 		moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1862 
1863 		// physics objects need better collisions than this code can do
1864 		if (movetype == MOVETYPE_PHYSICS)
1865 		{
1866 			VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1867 			SV_LinkEdict(check);
1868 			SV_LinkEdict_TouchAreaGrid(check);
1869 			continue;
1870 		}
1871 
1872 		// try moving the contacted entity
1873 		PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1874 		if(!SV_PushEntity (&trace, check, move, true))
1875 		{
1876 			// entity "check" got teleported
1877 			PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1878 			PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1879 			continue; // pushed enough
1880 		}
1881 		// FIXME: turn players specially
1882 		PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1883 		PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1884 		//Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1885 
1886 		// this trace.fraction < 1 check causes items to fall off of pushers
1887 		// if they pass under or through a wall
1888 		// the groundentity check causes items to fall off of ledges
1889 		if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1890 			PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1891 
1892 		// if it is still inside the pusher, block
1893 		VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1894 		VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1895 		VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1896 		VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1897 		VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1898 		Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, 0, collision_extendmovelength.value);
1899 		if (trace.startsolid)
1900 		{
1901 			vec3_t move2;
1902 			if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1903 			{
1904 				// hack to invoke all necessary movement triggers
1905 				VectorClear(move2);
1906 				if(!SV_PushEntity(&trace2, check, move2, true))
1907 				{
1908 					// entity "check" got teleported
1909 					continue;
1910 				}
1911 				// we could fix it
1912 				continue;
1913 			}
1914 
1915 			// still inside pusher, so it's really blocked
1916 
1917 			// fail the move
1918 			if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1919 				continue;
1920 			if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1921 			{
1922 				// corpse
1923 				PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1924 				VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1925 				continue;
1926 			}
1927 
1928 			VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1929 			VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1930 			PRVM_serveredictfloat(pusher, ltime) = pushltime;
1931 			SV_LinkEdict(pusher);
1932 
1933 			// move back any entities we already moved
1934 			for (i = 0;i < num_moved;i++)
1935 			{
1936 				prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1937 				VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1938 				VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1939 				SV_LinkEdict(ed);
1940 			}
1941 
1942 			// if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1943 			if (PRVM_serveredictfunction(pusher, blocked))
1944 			{
1945 				PRVM_serverglobalfloat(time) = sv.time;
1946 				PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1947 				PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1948 				prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1949 			}
1950 			break;
1951 		}
1952 	}
1953 	PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1954 	PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1955 	PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1956 }
1957 
1958 /*
1959 ================
1960 SV_Physics_Pusher
1961 
1962 ================
1963 */
SV_Physics_Pusher(prvm_edict_t * ent)1964 static void SV_Physics_Pusher (prvm_edict_t *ent)
1965 {
1966 	prvm_prog_t *prog = SVVM_prog;
1967 	float thinktime, oldltime, movetime;
1968 
1969 	oldltime = PRVM_serveredictfloat(ent, ltime);
1970 
1971 	thinktime = PRVM_serveredictfloat(ent, nextthink);
1972 	if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1973 	{
1974 		movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1975 		if (movetime < 0)
1976 			movetime = 0;
1977 	}
1978 	else
1979 		movetime = sv.frametime;
1980 
1981 	if (movetime)
1982 		// advances PRVM_serveredictfloat(ent, ltime) if not blocked
1983 		SV_PushMove (ent, movetime);
1984 
1985 	if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1986 	{
1987 		PRVM_serveredictfloat(ent, nextthink) = 0;
1988 		PRVM_serverglobalfloat(time) = sv.time;
1989 		PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1990 		PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1991 		prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1992 	}
1993 }
1994 
1995 
1996 /*
1997 ===============================================================================
1998 
1999 CLIENT MOVEMENT
2000 
2001 ===============================================================================
2002 */
2003 
2004 static float unstickoffsets[] =
2005 {
2006 	// poutting -/+z changes first as they are least weird
2007 	 0,  0,  -1,
2008 	 0,  0,  1,
2009 	 // x or y changes
2010 	-1,  0,  0,
2011 	 1,  0,  0,
2012 	 0, -1,  0,
2013 	 0,  1,  0,
2014 	 // x and y changes
2015 	-1, -1,  0,
2016 	 1, -1,  0,
2017 	-1,  1,  0,
2018 	 1,  1,  0,
2019 };
2020 
2021 typedef enum unstickresult_e
2022 {
2023 	UNSTICK_STUCK = 0,
2024 	UNSTICK_GOOD = 1,
2025 	UNSTICK_UNSTUCK = 2
2026 }
2027 unstickresult_t;
2028 
SV_UnstickEntityReturnOffset(prvm_edict_t * ent,vec3_t offset)2029 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2030 {
2031 	prvm_prog_t *prog = SVVM_prog;
2032 	int i, maxunstick;
2033 
2034 	// if not stuck in a bmodel, just return
2035 	if (!SV_TestEntityPosition(ent, vec3_origin))
2036 		return UNSTICK_GOOD;
2037 
2038 	for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2039 	{
2040 		if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2041 		{
2042 			VectorCopy(unstickoffsets + i, offset);
2043 			SV_LinkEdict(ent);
2044 			//SV_LinkEdict_TouchAreaGrid(ent);
2045 			return UNSTICK_UNSTUCK;
2046 		}
2047 	}
2048 
2049 	maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2050 	// magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2051 
2052 	for(i = 2; i <= maxunstick; ++i)
2053 	{
2054 		VectorClear(offset);
2055 		offset[2] = -i;
2056 		if (!SV_TestEntityPosition(ent, offset))
2057 		{
2058 			SV_LinkEdict(ent);
2059 			//SV_LinkEdict_TouchAreaGrid(ent);
2060 			return UNSTICK_UNSTUCK;
2061 		}
2062 		offset[2] = i;
2063 		if (!SV_TestEntityPosition(ent, offset))
2064 		{
2065 			SV_LinkEdict(ent);
2066 			//SV_LinkEdict_TouchAreaGrid(ent);
2067 			return UNSTICK_UNSTUCK;
2068 		}
2069 	}
2070 
2071 	return UNSTICK_STUCK;
2072 }
2073 
SV_UnstickEntity(prvm_edict_t * ent)2074 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2075 {
2076 	prvm_prog_t *prog = SVVM_prog;
2077 	vec3_t offset;
2078 	switch(SV_UnstickEntityReturnOffset(ent, offset))
2079 	{
2080 		case UNSTICK_GOOD:
2081 			return true;
2082 		case UNSTICK_UNSTUCK:
2083 			Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2084 			return true;
2085 		case UNSTICK_STUCK:
2086 			if (developer_extra.integer)
2087 				Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2088 			return false;
2089 		default:
2090 			Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2091 			return false;
2092 	}
2093 }
2094 
2095 /*
2096 =============
2097 SV_CheckStuck
2098 
2099 This is a big hack to try and fix the rare case of getting stuck in the world
2100 clipping hull.
2101 =============
2102 */
SV_CheckStuck(prvm_edict_t * ent)2103 static void SV_CheckStuck (prvm_edict_t *ent)
2104 {
2105 	prvm_prog_t *prog = SVVM_prog;
2106 	vec3_t offset;
2107 
2108 	switch(SV_UnstickEntityReturnOffset(ent, offset))
2109 	{
2110 		case UNSTICK_GOOD:
2111 			VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2112 			break;
2113 		case UNSTICK_UNSTUCK:
2114 			Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2115 			break;
2116 		case UNSTICK_STUCK:
2117 			VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2118 			if (!SV_TestEntityPosition(ent, offset))
2119 			{
2120 				Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2121 				SV_LinkEdict(ent);
2122 				//SV_LinkEdict_TouchAreaGrid(ent);
2123 			}
2124 			else
2125 				Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2126 			break;
2127 		default:
2128 			Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2129 	}
2130 }
2131 
2132 
2133 /*
2134 =============
2135 SV_CheckWater
2136 =============
2137 */
SV_CheckWater(prvm_edict_t * ent)2138 static qboolean SV_CheckWater (prvm_edict_t *ent)
2139 {
2140 	prvm_prog_t *prog = SVVM_prog;
2141 	int cont;
2142 	int nNativeContents;
2143 	vec3_t point;
2144 
2145 	point[0] = PRVM_serveredictvector(ent, origin)[0];
2146 	point[1] = PRVM_serveredictvector(ent, origin)[1];
2147 	point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2148 
2149 	// DRESK - Support for Entity Contents Transition Event
2150 	// NOTE: Some logic needed to be slightly re-ordered
2151 	// to not affect performance and allow for the feature.
2152 
2153 	// Acquire Super Contents Prior to Resets
2154 	cont = SV_PointSuperContents(point);
2155 	// Acquire Native Contents Here
2156 	nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2157 
2158 	// DRESK - Support for Entity Contents Transition Event
2159 	if(PRVM_serveredictfloat(ent, watertype))
2160 		// Entity did NOT Spawn; Check
2161 		SV_CheckContentsTransition(ent, nNativeContents);
2162 
2163 
2164 	PRVM_serveredictfloat(ent, waterlevel) = 0;
2165 	PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2166 	cont = SV_PointSuperContents(point);
2167 	if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2168 	{
2169 		PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2170 		PRVM_serveredictfloat(ent, waterlevel) = 1;
2171 		point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2172 		if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2173 		{
2174 			PRVM_serveredictfloat(ent, waterlevel) = 2;
2175 			point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2176 			if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2177 				PRVM_serveredictfloat(ent, waterlevel) = 3;
2178 		}
2179 	}
2180 
2181 	return PRVM_serveredictfloat(ent, waterlevel) > 1;
2182 }
2183 
2184 /*
2185 ============
2186 SV_WallFriction
2187 
2188 ============
2189 */
SV_WallFriction(prvm_edict_t * ent,float * stepnormal)2190 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2191 {
2192 	prvm_prog_t *prog = SVVM_prog;
2193 	float d, i;
2194 	vec3_t forward, into, side, v_angle;
2195 
2196 	VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2197 	AngleVectors (v_angle, forward, NULL, NULL);
2198 	if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2199 	{
2200 		// cut the tangential velocity
2201 		i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2202 		VectorScale (stepnormal, i, into);
2203 		VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2204 		PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2205 		PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2206 	}
2207 }
2208 
2209 #if 0
2210 /*
2211 =====================
2212 SV_TryUnstick
2213 
2214 Player has come to a dead stop, possibly due to the problem with limited
2215 float precision at some angle joins in the BSP hull.
2216 
2217 Try fixing by pushing one pixel in each direction.
2218 
2219 This is a hack, but in the interest of good gameplay...
2220 ======================
2221 */
2222 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2223 {
2224 	int i, clip;
2225 	vec3_t oldorg, dir;
2226 
2227 	VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2228 	VectorClear (dir);
2229 
2230 	for (i=0 ; i<8 ; i++)
2231 	{
2232 		// try pushing a little in an axial direction
2233 		switch (i)
2234 		{
2235 			case 0: dir[0] = 2; dir[1] = 0; break;
2236 			case 1: dir[0] = 0; dir[1] = 2; break;
2237 			case 2: dir[0] = -2; dir[1] = 0; break;
2238 			case 3: dir[0] = 0; dir[1] = -2; break;
2239 			case 4: dir[0] = 2; dir[1] = 2; break;
2240 			case 5: dir[0] = -2; dir[1] = 2; break;
2241 			case 6: dir[0] = 2; dir[1] = -2; break;
2242 			case 7: dir[0] = -2; dir[1] = -2; break;
2243 		}
2244 
2245 		SV_PushEntity (&trace, ent, dir, false, true);
2246 
2247 		// retry the original move
2248 		PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2249 		PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2250 		PRVM_serveredictvector(ent, velocity)[2] = 0;
2251 		clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2252 
2253 		if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2254 		 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2255 		{
2256 			Con_DPrint("TryUnstick - success.\n");
2257 			return clip;
2258 		}
2259 
2260 		// go back to the original pos and try again
2261 		VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2262 	}
2263 
2264 	// still not moving
2265 	VectorClear (PRVM_serveredictvector(ent, velocity));
2266 	Con_DPrint("TryUnstick - failure.\n");
2267 	return 7;
2268 }
2269 #endif
2270 
2271 /*
2272 =====================
2273 SV_WalkMove
2274 
2275 Only used by players
2276 ======================
2277 */
SV_WalkMove(prvm_edict_t * ent)2278 static void SV_WalkMove (prvm_edict_t *ent)
2279 {
2280 	prvm_prog_t *prog = SVVM_prog;
2281 	int clip;
2282 	int oldonground;
2283 	//int originalmove_clip;
2284 	int originalmove_flags;
2285 	int originalmove_groundentity;
2286 	int hitsupercontentsmask;
2287 	int skipsupercontentsmask;
2288 	int type;
2289 	vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2290 	trace_t downtrace, trace;
2291 	qboolean applygravity;
2292 
2293 	// if frametime is 0 (due to client sending the same timestamp twice),
2294 	// don't move
2295 	if (sv.frametime <= 0)
2296 		return;
2297 
2298 	if (sv_gameplayfix_unstickplayers.integer)
2299 		SV_CheckStuck (ent);
2300 
2301 	applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2302 
2303 	hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2304 	skipsupercontentsmask = 0;
2305 
2306 	SV_CheckVelocity(ent);
2307 
2308 	// do a regular slide move unless it looks like you ran into a step
2309 	oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2310 
2311 	VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2312 	VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2313 
2314 	clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2315 
2316 	if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2317 	if(!(clip & 1))
2318 	{
2319 		// only try this if there was no floor in the way in the trace (no,
2320 		// this check seems to be not REALLY necessary, because if clip & 1,
2321 		// our trace will hit that thing too)
2322 		VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2323 		VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2324 		if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2325 			type = MOVE_MISSILE;
2326 		else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2327 			type = MOVE_WORLDONLY;
2328 		else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2329 			type = MOVE_NOMONSTERS; // only clip against bmodels
2330 		else
2331 			type = MOVE_NORMAL;
2332 		VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2333 		VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2334 		trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, collision_extendmovelength.value);
2335 		if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2336 			clip |= 1; // but we HAVE found a floor
2337 	}
2338 
2339 	// if the move did not hit the ground at any point, we're not on ground
2340 	if(!(clip & 1))
2341 		PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2342 
2343 	SV_CheckVelocity(ent);
2344 	SV_LinkEdict(ent);
2345 	SV_LinkEdict_TouchAreaGrid(ent);
2346 
2347 	if(clip & 8) // teleport
2348 		return;
2349 
2350 	if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2351 		return;
2352 
2353 	if (sv_nostep.integer)
2354 		return;
2355 
2356 	VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2357 	VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2358 	//originalmove_clip = clip;
2359 	originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2360 	originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2361 
2362 	// if move didn't block on a step, return
2363 	if (clip & 2)
2364 	{
2365 		// if move was not trying to move into the step, return
2366 		if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2367 			return;
2368 
2369 		if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2370 		{
2371 			// return if gibbed by a trigger
2372 			if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2373 				return;
2374 
2375 			// return if attempting to jump while airborn (unless sv_jumpstep)
2376 			if (!sv_jumpstep.integer)
2377 				if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2378 					return;
2379 		}
2380 
2381 		// try moving up and forward to go up a step
2382 		// back to start pos
2383 		VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2384 		VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2385 
2386 		// move up
2387 		VectorClear (upmove);
2388 		upmove[2] = sv_stepheight.value;
2389 		if(!SV_PushEntity(&trace, ent, upmove, true))
2390 		{
2391 			// we got teleported when upstepping... must abort the move
2392 			return;
2393 		}
2394 
2395 		// move forward
2396 		PRVM_serveredictvector(ent, velocity)[2] = 0;
2397 		clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, 0);
2398 		PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2399 		if(clip & 8)
2400 		{
2401 			// we got teleported when upstepping... must abort the move
2402 			// note that z velocity handling may not be what QC expects here, but we cannot help it
2403 			return;
2404 		}
2405 
2406 		SV_CheckVelocity(ent);
2407 		SV_LinkEdict(ent);
2408 		SV_LinkEdict_TouchAreaGrid(ent);
2409 
2410 		// check for stuckness, possibly due to the limited precision of floats
2411 		// in the clipping hulls
2412 		if (clip
2413 		 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2414 		 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2415 		{
2416 			//Con_Printf("wall\n");
2417 			// stepping up didn't make any progress, revert to original move
2418 			VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2419 			VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2420 			//clip = originalmove_clip;
2421 			PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2422 			PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2423 			// now try to unstick if needed
2424 			//clip = SV_TryUnstick (ent, oldvel);
2425 			return;
2426 		}
2427 
2428 		//Con_Printf("step - ");
2429 
2430 		// extra friction based on view angle
2431 		if (clip & 2 && sv_wallfriction.integer)
2432 			SV_WallFriction (ent, stepnormal);
2433 	}
2434 	// don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2435 	else if (!sv_gameplayfix_stepdown.integer || PRVM_serveredictfloat(ent, waterlevel) >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
2436 		return;
2437 
2438 	// move down
2439 	VectorClear (downmove);
2440 	downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2441 	if(!SV_PushEntity (&downtrace, ent, downmove, true))
2442 	{
2443 		// we got teleported when downstepping... must abort the move
2444 		return;
2445 	}
2446 
2447 	if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2448 	{
2449 		// this has been disabled so that you can't jump when you are stepping
2450 		// up while already jumping (also known as the Quake2 double jump bug)
2451 #if 0
2452 		// LordHavoc: disabled this check so you can walk on monsters/players
2453 		//if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2454 		{
2455 			//Con_Printf("onground\n");
2456 			PRVM_serveredictfloat(ent, flags) =	(int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2457 			PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2458 		}
2459 #endif
2460 	}
2461 	else
2462 	{
2463 		//Con_Printf("slope\n");
2464 		// if the push down didn't end up on good ground, use the move without
2465 		// the step up.  This happens near wall / slope combinations, and can
2466 		// cause the player to hop up higher on a slope too steep to climb
2467 		VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2468 		VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2469 		//clip = originalmove_clip;
2470 		PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2471 		PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2472 	}
2473 
2474 	SV_CheckVelocity(ent);
2475 	SV_LinkEdict(ent);
2476 	SV_LinkEdict_TouchAreaGrid(ent);
2477 }
2478 
2479 //============================================================================
2480 
2481 /*
2482 =============
2483 SV_Physics_Follow
2484 
2485 Entities that are "stuck" to another entity
2486 =============
2487 */
SV_Physics_Follow(prvm_edict_t * ent)2488 static void SV_Physics_Follow (prvm_edict_t *ent)
2489 {
2490 	prvm_prog_t *prog = SVVM_prog;
2491 	vec3_t vf, vr, vu, angles, v;
2492 	prvm_edict_t *e;
2493 
2494 	// LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2495 	e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2496 	if (PRVM_serveredictvector(e, angles)[0] == PRVM_serveredictvector(ent, punchangle)[0] && PRVM_serveredictvector(e, angles)[1] == PRVM_serveredictvector(ent, punchangle)[1] && PRVM_serveredictvector(e, angles)[2] == PRVM_serveredictvector(ent, punchangle)[2])
2497 	{
2498 		// quick case for no rotation
2499 		VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2500 	}
2501 	else
2502 	{
2503 		angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2504 		angles[1] =  PRVM_serveredictvector(ent, punchangle)[1];
2505 		angles[2] =  PRVM_serveredictvector(ent, punchangle)[2];
2506 		AngleVectors (angles, vf, vr, vu);
2507 		v[0] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[0] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[0] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[0];
2508 		v[1] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[1] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[1] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[1];
2509 		v[2] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[2] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[2] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[2];
2510 		angles[0] = -PRVM_serveredictvector(e, angles)[0];
2511 		angles[1] =  PRVM_serveredictvector(e, angles)[1];
2512 		angles[2] =  PRVM_serveredictvector(e, angles)[2];
2513 		AngleVectors (angles, vf, vr, vu);
2514 		PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2515 		PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2516 		PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2517 	}
2518 	VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2519 	SV_LinkEdict(ent);
2520 	//SV_LinkEdict_TouchAreaGrid(ent);
2521 }
2522 
2523 /*
2524 ==============================================================================
2525 
2526 TOSS / BOUNCE
2527 
2528 ==============================================================================
2529 */
2530 
2531 /*
2532 =============
2533 SV_CheckWaterTransition
2534 
2535 =============
2536 */
SV_CheckWaterTransition(prvm_edict_t * ent)2537 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2538 {
2539 	vec3_t entorigin;
2540 	prvm_prog_t *prog = SVVM_prog;
2541 	// LordHavoc: bugfixes in this function are keyed to the sv_gameplayfix_bugfixedcheckwatertransition cvar - if this cvar is 0 then all the original bugs should be reenabled for compatibility
2542 	int cont;
2543 	VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2544 	cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2545 	if (!PRVM_serveredictfloat(ent, watertype))
2546 	{
2547 		// just spawned here
2548 		if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2549 		{
2550 			PRVM_serveredictfloat(ent, watertype) = cont;
2551 			PRVM_serveredictfloat(ent, waterlevel) = 1;
2552 			return;
2553 		}
2554 	}
2555 	// DRESK - Support for Entity Contents Transition Event
2556 	// NOTE: Call here BEFORE updating the watertype below,
2557 	// and suppress watersplash sound if a valid function
2558 	// call was made to allow for custom "splash" sounds.
2559 	else if( !SV_CheckContentsTransition(ent, cont) )
2560 	{ // Contents Transition Function Invalid; Potentially Play Water Sound
2561 		// check if the entity crossed into or out of water
2562 		if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2563 			SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2564 	}
2565 
2566 	if (cont <= CONTENTS_WATER)
2567 	{
2568 		PRVM_serveredictfloat(ent, watertype) = cont;
2569 		PRVM_serveredictfloat(ent, waterlevel) = 1;
2570 	}
2571 	else
2572 	{
2573 		PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2574 		PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2575 	}
2576 }
2577 
2578 /*
2579 =============
2580 SV_Physics_Toss
2581 
2582 Toss, bounce, and fly movement.  When onground, do nothing.
2583 =============
2584 */
2585 
SV_Physics_Toss(prvm_edict_t * ent)2586 void SV_Physics_Toss (prvm_edict_t *ent)
2587 {
2588 	prvm_prog_t *prog = SVVM_prog;
2589 	trace_t trace;
2590 	vec3_t move;
2591 	vec_t movetime;
2592 	int bump;
2593 	prvm_edict_t *groundentity;
2594 	float d, ent_gravity;
2595 	float bouncefactor;
2596 	float bouncestop;
2597 
2598 // if onground, return without moving
2599 	if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2600 	{
2601 		groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2602 		if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2603 		{
2604 			// don't stick to ground if onground and moving upward
2605 			PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2606 		}
2607 		else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2608 		{
2609 			// we can trust FL_ONGROUND if groundentity is world because it never moves
2610 			return;
2611 		}
2612 		else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2613 		{
2614 			// if ent was supported by a brush model on previous frame,
2615 			// and groundentity is now freed, set groundentity to 0 (world)
2616 			// which leaves it suspended in the air
2617 			PRVM_serveredictedict(ent, groundentity) = 0;
2618 			if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2619 				return;
2620 		}
2621 		else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2622 		{
2623 			// don't slide if still touching the groundentity
2624 			return;
2625 		}
2626 	}
2627 	ent->priv.server->suspendedinairflag = false;
2628 
2629 	SV_CheckVelocity (ent);
2630 
2631 // add gravity
2632 	if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2633 		PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2634 
2635 // move angles
2636 	VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2637 
2638 	movetime = sv.frametime;
2639 	for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2640 	{
2641 	// move origin
2642 		VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2643 		if(!SV_PushEntity(&trace, ent, move, true))
2644 			return; // teleported
2645 		if (ent->priv.server->free)
2646 			return;
2647 		if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2648 		{
2649 			// try to unstick the entity
2650 			SV_UnstickEntity(ent);
2651 			if(!SV_PushEntity(&trace, ent, move, true))
2652 				return; // teleported
2653 			if (ent->priv.server->free)
2654 				return;
2655 		}
2656 		if (trace.fraction == 1)
2657 			break;
2658 		movetime *= 1 - min(1, trace.fraction);
2659 		switch((int)PRVM_serveredictfloat(ent, movetype))
2660 		{
2661 		case MOVETYPE_BOUNCEMISSILE:
2662 			bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2663 			if (!bouncefactor)
2664 				bouncefactor = 1.0f;
2665 
2666 			ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2667 			PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2668 			if (!sv_gameplayfix_slidemoveprojectiles.integer)
2669 				movetime = 0;
2670 			break;
2671 		case MOVETYPE_BOUNCE:
2672 			bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2673 			if (!bouncefactor)
2674 				bouncefactor = 0.5f;
2675 
2676 			bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2677 			if (!bouncestop)
2678 				bouncestop = 60.0f / 800.0f;
2679 
2680 			ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2681 			ent_gravity = PRVM_serveredictfloat(ent, gravity);
2682 			if (!ent_gravity)
2683 				ent_gravity = 1.0f;
2684 			// LordHavoc: fixed grenades not bouncing when fired down a slope
2685 			if (sv_gameplayfix_grenadebouncedownslopes.integer)
2686 				d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2687 			else
2688 				d = PRVM_serveredictvector(ent, velocity)[2];
2689 			if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2690 			{
2691 				PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2692 				PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2693 				VectorClear(PRVM_serveredictvector(ent, velocity));
2694 				VectorClear(PRVM_serveredictvector(ent, avelocity));
2695 				movetime = 0;
2696 			}
2697 			else
2698 			{
2699 				PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2700 				if (!sv_gameplayfix_slidemoveprojectiles.integer)
2701 					movetime = 0;
2702 			}
2703 			break;
2704 		default:
2705 			ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2706 			if (trace.plane.normal[2] > 0.7)
2707 			{
2708 				PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2709 				PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2710 				if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2711 					ent->priv.server->suspendedinairflag = true;
2712 				VectorClear (PRVM_serveredictvector(ent, velocity));
2713 				VectorClear (PRVM_serveredictvector(ent, avelocity));
2714 				movetime = 0;
2715 			}
2716 			else
2717 			{
2718 				PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2719 				if (!sv_gameplayfix_slidemoveprojectiles.integer)
2720 					movetime = 0;
2721 			}
2722 			break;
2723 		}
2724 	}
2725 
2726 // check for in water
2727 	SV_CheckWaterTransition (ent);
2728 }
2729 
2730 /*
2731 ===============================================================================
2732 
2733 STEPPING MOVEMENT
2734 
2735 ===============================================================================
2736 */
2737 
2738 /*
2739 =============
2740 SV_Physics_Step
2741 
2742 Monsters freefall when they don't have a ground entity, otherwise
2743 all movement is done with discrete steps.
2744 
2745 This is also used for objects that have become still on the ground, but
2746 will fall if the floor is pulled out from under them.
2747 =============
2748 */
SV_Physics_Step(prvm_edict_t * ent)2749 static void SV_Physics_Step (prvm_edict_t *ent)
2750 {
2751 	prvm_prog_t *prog = SVVM_prog;
2752 	int flags = (int)PRVM_serveredictfloat(ent, flags);
2753 
2754 	// DRESK
2755 	// Backup Velocity in the event that movetypesteplandevent is called,
2756 	// to provide a parameter with the entity's velocity at impact.
2757 	vec3_t backupVelocity;
2758 	VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2759 	// don't fall at all if fly/swim
2760 	if (!(flags & (FL_FLY | FL_SWIM)))
2761 	{
2762 		if (flags & FL_ONGROUND)
2763 		{
2764 			// freefall if onground and moving upward
2765 			// freefall if not standing on a world surface (it may be a lift or trap door)
2766 			if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2767 			{
2768 				PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2769 				SV_CheckVelocity(ent);
2770 				SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0);
2771 				SV_LinkEdict(ent);
2772 				SV_LinkEdict_TouchAreaGrid(ent);
2773 				ent->priv.server->waterposition_forceupdate = true;
2774 			}
2775 		}
2776 		else
2777 		{
2778 			// freefall if not onground
2779 			int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2780 
2781 			SV_CheckVelocity(ent);
2782 			SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0);
2783 			SV_LinkEdict(ent);
2784 			SV_LinkEdict_TouchAreaGrid(ent);
2785 
2786 			// just hit ground
2787 			if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2788 			{
2789 				// DRESK - Check for Entity Land Event Function
2790 				if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2791 				{ // Valid Function; Execute
2792 					// Prepare Parameters
2793 					// Assign Velocity at Impact
2794 					PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2795 					PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2796 					PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2797 					// Assign Self
2798 					PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2799 					// Set Time
2800 					PRVM_serverglobalfloat(time) = sv.time;
2801 					// Execute VM Function
2802 					prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2803 				}
2804 				else
2805 				// Check for Engine Landing Sound
2806 				if(sv_sound_land.string)
2807 					SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2808 			}
2809 			ent->priv.server->waterposition_forceupdate = true;
2810 		}
2811 	}
2812 }
2813 
2814 //============================================================================
2815 
SV_Physics_Entity(prvm_edict_t * ent)2816 static void SV_Physics_Entity (prvm_edict_t *ent)
2817 {
2818 	prvm_prog_t *prog = SVVM_prog;
2819 	// don't run think/move on newly spawned projectiles as it messes up
2820 	// movement interpolation and rocket trails, and is inconsistent with
2821 	// respect to entities spawned in the same frame
2822 	// (if an ent spawns a higher numbered ent, it moves in the same frame,
2823 	//  but if it spawns a lower numbered ent, it doesn't - this never moves
2824 	//  ents in the first frame regardless)
2825 	qboolean runmove = ent->priv.server->move;
2826 	ent->priv.server->move = true;
2827 	if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2828 		return;
2829 	switch ((int) PRVM_serveredictfloat(ent, movetype))
2830 	{
2831 	case MOVETYPE_PUSH:
2832 	case MOVETYPE_FAKEPUSH:
2833 		SV_Physics_Pusher (ent);
2834 		break;
2835 	case MOVETYPE_NONE:
2836 		// LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2837 		if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2838 			SV_RunThink (ent);
2839 		break;
2840 	case MOVETYPE_FOLLOW:
2841 		if(SV_RunThink(ent))
2842 			SV_Physics_Follow (ent);
2843 		break;
2844 	case MOVETYPE_NOCLIP:
2845 		if (SV_RunThink(ent))
2846 		{
2847 			SV_CheckWater(ent);
2848 			VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2849 			VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2850 		}
2851 		SV_LinkEdict(ent);
2852 		break;
2853 	case MOVETYPE_STEP:
2854 		SV_Physics_Step (ent);
2855 		// regular thinking
2856 		if (SV_RunThink(ent))
2857 		if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2858 		{
2859 			ent->priv.server->waterposition_forceupdate = false;
2860 			VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2861 			SV_CheckWaterTransition(ent);
2862 		}
2863 		break;
2864 	case MOVETYPE_WALK:
2865 		if (SV_RunThink (ent))
2866 			SV_WalkMove (ent);
2867 		break;
2868 	case MOVETYPE_TOSS:
2869 	case MOVETYPE_BOUNCE:
2870 	case MOVETYPE_BOUNCEMISSILE:
2871 	case MOVETYPE_FLYMISSILE:
2872 	case MOVETYPE_FLY:
2873 	case MOVETYPE_FLY_WORLDONLY:
2874 		// regular thinking
2875 		if (SV_RunThink (ent))
2876 			SV_Physics_Toss (ent);
2877 		break;
2878 	case MOVETYPE_PHYSICS:
2879 		if (SV_RunThink(ent))
2880 		{
2881 			SV_LinkEdict(ent);
2882 			SV_LinkEdict_TouchAreaGrid(ent);
2883 		}
2884 		break;
2885 	default:
2886 		if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2887 			break;
2888 		Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2889 		break;
2890 	}
2891 }
2892 
SV_Physics_ClientEntity_NoThink(prvm_edict_t * ent)2893 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2894 {
2895 	prvm_prog_t *prog = SVVM_prog;
2896 
2897 	// don't run think at all, that is done during server frames
2898 	// instead, call the movetypes directly so they match client input
2899 
2900 	// This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2901 	switch ((int) PRVM_serveredictfloat(ent, movetype))
2902 	{
2903 	case MOVETYPE_PUSH:
2904 	case MOVETYPE_FAKEPUSH:
2905 		// push physics relies heavily on think times and calls, and so cannot be predicted currently
2906 		Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2907 		break;
2908 	case MOVETYPE_NONE:
2909 		break;
2910 	case MOVETYPE_FOLLOW:
2911 		SV_Physics_Follow (ent);
2912 		break;
2913 	case MOVETYPE_NOCLIP:
2914 		VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2915 		VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2916 		break;
2917 	case MOVETYPE_STEP:
2918 		SV_Physics_Step (ent);
2919 		break;
2920 	case MOVETYPE_WALK:
2921 		SV_WalkMove (ent);
2922 		break;
2923 	case MOVETYPE_TOSS:
2924 	case MOVETYPE_BOUNCE:
2925 	case MOVETYPE_BOUNCEMISSILE:
2926 	case MOVETYPE_FLYMISSILE:
2927 		SV_Physics_Toss (ent);
2928 		break;
2929 	case MOVETYPE_FLY:
2930 	case MOVETYPE_FLY_WORLDONLY:
2931 		SV_WalkMove (ent);
2932 		break;
2933 	case MOVETYPE_PHYSICS:
2934 		break;
2935 	default:
2936 		if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2937 			break;
2938 		Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2939 		break;
2940 	}
2941 }
2942 
SV_Physics_ClientMove(void)2943 void SV_Physics_ClientMove(void)
2944 {
2945 	prvm_prog_t *prog = SVVM_prog;
2946 	prvm_edict_t *ent;
2947 	ent = host_client->edict;
2948 
2949 	// call player physics, this needs the proper frametime
2950 	PRVM_serverglobalfloat(frametime) = sv.frametime;
2951 	SV_ClientThink();
2952 
2953 	// call standard client pre-think, with frametime = 0
2954 	PRVM_serverglobalfloat(time) = sv.time;
2955 	PRVM_serverglobalfloat(frametime) = 0;
2956 	PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2957 	prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2958 	PRVM_serverglobalfloat(frametime) = sv.frametime;
2959 
2960 	// make sure the velocity is sane (not a NaN)
2961 	SV_CheckVelocity(ent);
2962 
2963 	// perform movetype behaviour
2964 	// note: will always be MOVETYPE_WALK if disableclientprediction = 0
2965 	SV_Physics_ClientEntity_NoThink (ent);
2966 
2967 	// call standard player post-think, with frametime = 0
2968 	PRVM_serverglobalfloat(time) = sv.time;
2969 	PRVM_serverglobalfloat(frametime) = 0;
2970 	PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2971 	prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2972 	PRVM_serverglobalfloat(frametime) = sv.frametime;
2973 
2974 	if(PRVM_serveredictfloat(ent, fixangle))
2975 	{
2976 		// angle fixing was requested by physics code...
2977 		// so store the current angles for later use
2978 		VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2979 		host_client->fixangle_angles_set = TRUE;
2980 
2981 		// and clear fixangle for the next frame
2982 		PRVM_serveredictfloat(ent, fixangle) = 0;
2983 	}
2984 }
2985 
SV_Physics_ClientEntity_PreThink(prvm_edict_t * ent)2986 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2987 {
2988 	prvm_prog_t *prog = SVVM_prog;
2989 	// don't do physics on disconnected clients, FrikBot relies on this
2990 	if (!host_client->begun)
2991 		return;
2992 
2993 	// make sure the velocity is sane (not a NaN)
2994 	SV_CheckVelocity(ent);
2995 
2996 	// don't run physics here if running asynchronously
2997 	if (host_client->clmovement_inputtimeout <= 0)
2998 	{
2999 		SV_ClientThink();
3000 		//host_client->cmd.time = max(host_client->cmd.time, sv.time);
3001 	}
3002 
3003 	// make sure the velocity is still sane (not a NaN)
3004 	SV_CheckVelocity(ent);
3005 
3006 	// call standard client pre-think
3007 	PRVM_serverglobalfloat(time) = sv.time;
3008 	PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3009 	prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3010 
3011 	// make sure the velocity is still sane (not a NaN)
3012 	SV_CheckVelocity(ent);
3013 }
3014 
SV_Physics_ClientEntity_PostThink(prvm_edict_t * ent)3015 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3016 {
3017 	prvm_prog_t *prog = SVVM_prog;
3018 	// don't do physics on disconnected clients, FrikBot relies on this
3019 	if (!host_client->begun)
3020 		return;
3021 
3022 	// make sure the velocity is sane (not a NaN)
3023 	SV_CheckVelocity(ent);
3024 
3025 	// call standard player post-think
3026 	PRVM_serverglobalfloat(time) = sv.time;
3027 	PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3028 	prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3029 
3030 	// make sure the velocity is still sane (not a NaN)
3031 	SV_CheckVelocity(ent);
3032 
3033 	if(PRVM_serveredictfloat(ent, fixangle))
3034 	{
3035 		// angle fixing was requested by physics code...
3036 		// so store the current angles for later use
3037 		VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3038 		host_client->fixangle_angles_set = TRUE;
3039 
3040 		// and clear fixangle for the next frame
3041 		PRVM_serveredictfloat(ent, fixangle) = 0;
3042 	}
3043 
3044 	// decrement the countdown variable used to decide when to go back to
3045 	// synchronous physics
3046 	if (host_client->clmovement_inputtimeout > sv.frametime)
3047 		host_client->clmovement_inputtimeout -= sv.frametime;
3048 	else
3049 		host_client->clmovement_inputtimeout = 0;
3050 }
3051 
SV_Physics_ClientEntity(prvm_edict_t * ent)3052 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3053 {
3054 	prvm_prog_t *prog = SVVM_prog;
3055 	// don't do physics on disconnected clients, FrikBot relies on this
3056 	if (!host_client->begun)
3057 	{
3058 		memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3059 		return;
3060 	}
3061 
3062 	// make sure the velocity is sane (not a NaN)
3063 	SV_CheckVelocity(ent);
3064 
3065 	switch ((int) PRVM_serveredictfloat(ent, movetype))
3066 	{
3067 	case MOVETYPE_PUSH:
3068 	case MOVETYPE_FAKEPUSH:
3069 		SV_Physics_Pusher (ent);
3070 		break;
3071 	case MOVETYPE_NONE:
3072 		// LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3073 		if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3074 			SV_RunThink (ent);
3075 		break;
3076 	case MOVETYPE_FOLLOW:
3077 		SV_RunThink (ent);
3078 		if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3079 			SV_Physics_Follow (ent);
3080 		break;
3081 	case MOVETYPE_NOCLIP:
3082 		SV_RunThink(ent);
3083 		if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3084 		{
3085 			SV_CheckWater(ent);
3086 			VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3087 			VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3088 		}
3089 		break;
3090 	case MOVETYPE_STEP:
3091 		if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3092 			SV_Physics_Step (ent);
3093 		if (SV_RunThink(ent))
3094 		if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3095 		{
3096 			ent->priv.server->waterposition_forceupdate = false;
3097 			VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3098 			SV_CheckWaterTransition(ent);
3099 		}
3100 		break;
3101 	case MOVETYPE_WALK:
3102 		SV_RunThink (ent);
3103 		// don't run physics here if running asynchronously
3104 		if (host_client->clmovement_inputtimeout <= 0)
3105 			SV_WalkMove (ent);
3106 		break;
3107 	case MOVETYPE_TOSS:
3108 	case MOVETYPE_BOUNCE:
3109 	case MOVETYPE_BOUNCEMISSILE:
3110 	case MOVETYPE_FLYMISSILE:
3111 		// regular thinking
3112 		SV_RunThink (ent);
3113 		if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3114 			SV_Physics_Toss (ent);
3115 		break;
3116 	case MOVETYPE_FLY:
3117 	case MOVETYPE_FLY_WORLDONLY:
3118 		SV_RunThink (ent);
3119 		if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3120 			SV_WalkMove (ent);
3121 		break;
3122 	case MOVETYPE_PHYSICS:
3123 		SV_RunThink (ent);
3124 		break;
3125 	default:
3126 		if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3127 			break;
3128 		Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3129 		break;
3130 	}
3131 
3132 	SV_CheckVelocity (ent);
3133 
3134 	SV_LinkEdict(ent);
3135 	SV_LinkEdict_TouchAreaGrid(ent);
3136 
3137 	SV_CheckVelocity (ent);
3138 }
3139 
3140 /*
3141 ================
3142 SV_Physics
3143 
3144 ================
3145 */
SV_Physics(void)3146 void SV_Physics (void)
3147 {
3148 	prvm_prog_t *prog = SVVM_prog;
3149 	int i;
3150 	prvm_edict_t *ent;
3151 
3152 // let the progs know that a new frame has started
3153 	PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3154 	PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3155 	PRVM_serverglobalfloat(time) = sv.time;
3156 	PRVM_serverglobalfloat(frametime) = sv.frametime;
3157 	prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3158 
3159 	// run physics engine
3160 	World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3161 
3162 //
3163 // treat each object in turn
3164 //
3165 
3166 	// if force_retouch, relink all the entities
3167 	if (PRVM_serverglobalfloat(force_retouch) > 0)
3168 		for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3169 			if (!ent->priv.server->free)
3170 				SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3171 
3172 	if (sv_gameplayfix_consistentplayerprethink.integer)
3173 	{
3174 		// run physics on the client entities in 3 stages
3175 		for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3176 			if (!ent->priv.server->free)
3177 				SV_Physics_ClientEntity_PreThink(ent);
3178 
3179 		for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3180 			if (!ent->priv.server->free)
3181 				SV_Physics_ClientEntity(ent);
3182 
3183 		for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3184 			if (!ent->priv.server->free)
3185 				SV_Physics_ClientEntity_PostThink(ent);
3186 	}
3187 	else
3188 	{
3189 		// run physics on the client entities
3190 		for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3191 		{
3192 			if (!ent->priv.server->free)
3193 			{
3194 				SV_Physics_ClientEntity_PreThink(ent);
3195 				SV_Physics_ClientEntity(ent);
3196 				SV_Physics_ClientEntity_PostThink(ent);
3197 			}
3198 		}
3199 	}
3200 
3201 	// run physics on all the non-client entities
3202 	if (!sv_freezenonclients.integer)
3203 	{
3204 		for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3205 			if (!ent->priv.server->free)
3206 				SV_Physics_Entity(ent);
3207 		// make a second pass to see if any ents spawned this frame and make
3208 		// sure they run their move/think
3209 		if (sv_gameplayfix_delayprojectiles.integer < 0)
3210 			for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3211 				if (!ent->priv.server->move && !ent->priv.server->free)
3212 					SV_Physics_Entity(ent);
3213 	}
3214 
3215 	if (PRVM_serverglobalfloat(force_retouch) > 0)
3216 		PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3217 
3218 	// LordHavoc: endframe support
3219 	if (PRVM_serverfunction(EndFrame))
3220 	{
3221 		PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3222 		PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3223 		PRVM_serverglobalfloat(time) = sv.time;
3224 		prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3225 	}
3226 
3227 	// decrement prog->num_edicts if the highest number entities died
3228 	for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3229 
3230 	if (!sv_freezenonclients.integer)
3231 		sv.time += sv.frametime;
3232 }
3233