]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_collision.c
bee473b2a2515ee0db5a3f84b89209fb434fe0cd
[xonotic/darkplaces.git] / cl_collision.c
1
2 #include "quakedef.h"
3 #include "cl_collision.h"
4
5 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
6 float CL_SelectTraceLine(const vec3_t start, const vec3_t pEnd, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent)
7 #else
8 float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent)
9 #endif
10 {
11         float maxfrac, maxrealfrac;
12         int n;
13         entity_render_t *ent;
14         vec_t tracemins[3], tracemaxs[3];
15         trace_t trace;
16         vec_t tempnormal[3], starttransformed[3], endtransformed[3];
17 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
18         vec3_t end;
19         vec_t len = 0;
20
21         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
22         {
23                 // TRICK: make the trace 1 qu longer!
24                 VectorSubtract(pEnd, start, end);
25                 len = VectorNormalizeLength(end);
26                 VectorMA(pEnd, collision_endposnudge.value, end, end);
27         }
28         else
29                 VectorCopy(pEnd, end);
30 #endif
31
32         memset (&trace, 0 , sizeof(trace_t));
33         trace.fraction = 1;
34         trace.realfraction = 1;
35         VectorCopy (end, trace.endpos);
36
37         if (hitent)
38                 *hitent = 0;
39         if (cl.worldmodel && cl.worldmodel->TraceLine)
40                 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, start, end, SUPERCONTENTS_SOLID);
41
42         if (normal)
43                 VectorCopy(trace.plane.normal, normal);
44         maxfrac = trace.fraction;
45         maxrealfrac = trace.realfraction;
46
47         tracemins[0] = min(start[0], end[0]);
48         tracemaxs[0] = max(start[0], end[0]);
49         tracemins[1] = min(start[1], end[1]);
50         tracemaxs[1] = max(start[1], end[1]);
51         tracemins[2] = min(start[2], end[2]);
52         tracemaxs[2] = max(start[2], end[2]);
53
54         // look for embedded bmodels
55         for (n = 0;n < cl.num_entities;n++)
56         {
57                 if (!cl.entities_active[n])
58                         continue;
59                 ent = &cl.entities[n].render;
60                 if (!BoxesOverlap(ent->mins, ent->maxs, tracemins, tracemaxs))
61                         continue;
62                 if (!ent->model || !ent->model->TraceLine)
63                         continue;
64                 if ((ent->flags & RENDER_EXTERIORMODEL) && !chase_active.integer)
65                         continue;
66                 // if transparent and not selectable, skip entity
67                 if (!(cl.entities[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST))))
68                         continue;
69                 if (ent == ignoreent)
70                         continue;
71                 Matrix4x4_Transform(&ent->inversematrix, start, starttransformed);
72                 Matrix4x4_Transform(&ent->inversematrix, end, endtransformed);
73                 Collision_ClipTrace_Box(&trace, ent->model->normalmins, ent->model->normalmaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, SUPERCONTENTS_SOLID, SUPERCONTENTS_SOLID, 0, NULL);
74 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
75                 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
76                         Collision_ShortenTrace(&trace, len / (len + collision_endposnudge.value), pEnd);
77 #endif
78                 if (maxrealfrac < trace.realfraction)
79                         continue;
80
81                 ent->model->TraceLine(ent->model, ent->frameblend, ent->skeleton, &trace, starttransformed, endtransformed, SUPERCONTENTS_SOLID);
82
83                 if (maxrealfrac > trace.realfraction)
84                 {
85                         if (hitent)
86                                 *hitent = n;
87                         maxfrac = trace.fraction;
88                         maxrealfrac = trace.realfraction;
89                         if (normal)
90                         {
91                                 VectorCopy(trace.plane.normal, tempnormal);
92                                 Matrix4x4_Transform3x3(&ent->matrix, tempnormal, normal);
93                         }
94                 }
95         }
96         maxfrac = bound(0, maxfrac, 1);
97         maxrealfrac = bound(0, maxrealfrac, 1);
98         //if (maxfrac < 0 || maxfrac > 1) Con_Printf("fraction out of bounds %f %s:%d\n", maxfrac, __FILE__, __LINE__);
99         if (impact)
100                 VectorLerp(start, maxfrac, end, impact);
101         return maxfrac;
102 }
103
104 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius)
105 {
106         // FIXME: check multiple brush models
107         if (cl.worldmodel && cl.worldmodel->brush.FindNonSolidLocation)
108                 cl.worldmodel->brush.FindNonSolidLocation(cl.worldmodel, in, out, radius);
109 }
110
111 dp_model_t *CL_GetModelByIndex(int modelindex)
112 {
113         if(!modelindex)
114                 return NULL;
115         if (modelindex < 0)
116         {
117                 modelindex = -(modelindex+1);
118                 if (modelindex < MAX_MODELS)
119                         return cl.csqc_model_precache[modelindex];
120         }
121         else
122         {
123                 if(modelindex < MAX_MODELS)
124                         return cl.model_precache[modelindex];
125         }
126         return NULL;
127 }
128
129 dp_model_t *CL_GetModelFromEdict(prvm_edict_t *ed)
130 {
131         prvm_prog_t *prog = CLVM_prog;
132         if (!ed || ed->priv.server->free)
133                 return NULL;
134         return CL_GetModelByIndex((int)PRVM_clientedictfloat(ed, modelindex));
135 }
136
137 void CL_LinkEdict(prvm_edict_t *ent)
138 {
139         prvm_prog_t *prog = CLVM_prog;
140         vec3_t mins, maxs;
141
142         if (ent == prog->edicts)
143                 return;         // don't add the world
144
145         if (ent->priv.server->free)
146                 return;
147
148         // set the abs box
149
150         if (PRVM_clientedictfloat(ent, solid) == SOLID_BSP)
151         {
152                 dp_model_t *model = CL_GetModelByIndex( (int)PRVM_clientedictfloat(ent, modelindex) );
153                 if (model == NULL)
154                 {
155                         Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
156
157                         model = CL_GetModelByIndex( 0 );
158                 }
159
160                 if( model != NULL )
161                 {
162                         if (!model->TraceBox)
163                                 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
164
165                         if (PRVM_clientedictvector(ent, angles)[0] || PRVM_clientedictvector(ent, angles)[2] || PRVM_clientedictvector(ent, avelocity)[0] || PRVM_clientedictvector(ent, avelocity)[2])
166                         {
167                                 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmins, mins);
168                                 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmaxs, maxs);
169                         }
170                         else if (PRVM_clientedictvector(ent, angles)[1] || PRVM_clientedictvector(ent, avelocity)[1])
171                         {
172                                 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmins, mins);
173                                 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmaxs, maxs);
174                         }
175                         else
176                         {
177                                 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmins, mins);
178                                 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmaxs, maxs);
179                         }
180                 }
181                 else
182                 {
183                         // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
184                         VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
185                         VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
186                 }
187         }
188         else
189         {
190                 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
191                 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
192         }
193
194         VectorCopy(mins, PRVM_clientedictvector(ent, absmin));
195         VectorCopy(maxs, PRVM_clientedictvector(ent, absmax));
196
197         World_LinkEdict(&cl.world, ent, mins, maxs);
198 }
199
200 int CL_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
201 {
202         prvm_prog_t *prog = CLVM_prog;
203         if (passedict)
204         {
205                 int dphitcontentsmask = (int)PRVM_clientedictfloat(passedict, dphitcontentsmask);
206                 if (dphitcontentsmask)
207                         return dphitcontentsmask;
208                 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_SLIDEBOX)
209                 {
210                         if ((int)PRVM_clientedictfloat(passedict, flags) & FL_MONSTER)
211                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
212                         else
213                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
214                 }
215                 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_CORPSE)
216                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
217                 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_TRIGGER)
218                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
219                 else
220                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
221         }
222         else
223                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
224 }
225
226 /*
227 ==================
228 CL_Move
229 ==================
230 */
231 trace_t CL_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
232 {
233         prvm_prog_t *prog = CLVM_prog;
234         int i, bodysupercontents;
235         int passedictprog;
236         prvm_edict_t *traceowner, *touch;
237         trace_t trace;
238         // temporary storage because prvm_vec_t may need conversion
239         vec3_t touchmins, touchmaxs;
240         // bounding box of entire move area
241         vec3_t clipboxmins, clipboxmaxs;
242         // size when clipping against monsters
243         vec3_t clipmins2, clipmaxs2;
244         // start and end origin of move
245         vec3_t clipstart;
246         // trace results
247         trace_t cliptrace;
248         // matrices to transform into/out of other entity's space
249         matrix4x4_t matrix, imatrix;
250         // model of other entity
251         dp_model_t *model;
252         // list of entities to test for collisions
253         int numtouchedicts;
254         static prvm_edict_t *touchedicts[MAX_EDICTS];
255
256         if (hitnetworkentity)
257                 *hitnetworkentity = 0;
258
259         VectorCopy(start, clipstart);
260         VectorClear(clipmins2);
261         VectorClear(clipmaxs2);
262 #if COLLISIONPARANOID >= 3
263         Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
264 #endif
265
266         // clip to world
267         Collision_ClipPointToWorld(&cliptrace, cl.worldmodel, clipstart, hitsupercontentsmask);
268         cliptrace.bmodelstartsolid = cliptrace.startsolid;
269         if (cliptrace.startsolid || cliptrace.fraction < 1)
270                 cliptrace.ent = prog ? prog->edicts : NULL;
271         if (type == MOVE_WORLDONLY)
272                 goto finished;
273
274         if (type == MOVE_MISSILE)
275         {
276                 // LordHavoc: modified this, was = -15, now -= 15
277                 for (i = 0;i < 3;i++)
278                 {
279                         clipmins2[i] -= 15;
280                         clipmaxs2[i] += 15;
281                 }
282         }
283
284         // create the bounding box of the entire move
285         for (i = 0;i < 3;i++)
286         {
287                 clipboxmins[i] = clipstart[i] - 1;
288                 clipboxmaxs[i] = clipstart[i] + 1;
289         }
290
291         // debug override to test against everything
292         if (sv_debugmove.integer)
293         {
294                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
295                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
296         }
297
298         // if the passedict is world, make it NULL (to avoid two checks each time)
299         // this checks prog because this function is often called without a CSQC
300         // VM context
301         if (prog == NULL || passedict == prog->edicts)
302                 passedict = NULL;
303         // precalculate prog value for passedict for comparisons
304         passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
305         // precalculate passedict's owner edict pointer for comparisons
306         traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
307
308         // collide against network entities
309         if (hitnetworkbrushmodels)
310         {
311                 for (i = 0;i < cl.num_brushmodel_entities;i++)
312                 {
313                         entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
314                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
315                                 continue;
316                         Collision_ClipPointToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, hitsupercontentsmask);
317                         if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
318                                 *hitnetworkentity = cl.brushmodel_entities[i];
319                         Collision_CombineTraces(&cliptrace, &trace, NULL, true);
320                 }
321         }
322
323         // collide against player entities
324         if (hitnetworkplayers)
325         {
326                 vec3_t origin, entmins, entmaxs;
327                 matrix4x4_t entmatrix, entinversematrix;
328
329                 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
330                 {
331                         // don't hit network players, if we are a nonsolid player
332                         if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
333                                 goto skipnetworkplayers;
334                 }
335
336                 for (i = 1;i <= cl.maxclients;i++)
337                 {
338                         entity_render_t *ent = &cl.entities[i].render;
339
340                         // don't hit ourselves
341                         if (i == cl.playerentity)
342                                 continue;
343
344                         // don't hit players that don't exist
345                         if (!cl.scores[i-1].name[0])
346                                 continue;
347
348                         if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
349                         {
350                                 // don't hit spectators or nonsolid players
351                                 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
352                                         continue;
353                         }
354
355                         Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
356                         VectorAdd(origin, cl.playerstandmins, entmins);
357                         VectorAdd(origin, cl.playerstandmaxs, entmaxs);
358                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
359                                 continue;
360                         Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
361                         Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
362                         Collision_ClipPointToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask);
363                         if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
364                                 *hitnetworkentity = i;
365                         Collision_CombineTraces(&cliptrace, &trace, NULL, false);
366                 }
367
368 skipnetworkplayers:
369                 ;
370         }
371
372         // clip to entities
373         // because this uses World_EntitiestoBox, we know all entity boxes overlap
374         // the clip region, so we can skip culling checks in the loop below
375         // note: if prog is NULL then there won't be any linked entities
376         numtouchedicts = 0;
377         if (hitcsqcentities && prog != NULL)
378         {
379                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
380                 if (numtouchedicts > MAX_EDICTS)
381                 {
382                         // this never happens
383                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
384                         numtouchedicts = MAX_EDICTS;
385                 }
386         }
387         for (i = 0;i < numtouchedicts;i++)
388         {
389                 touch = touchedicts[i];
390
391                 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
392                         continue;
393                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
394                         continue;
395
396                 if (passedict)
397                 {
398                         // don't clip against self
399                         if (passedict == touch)
400                                 continue;
401                         // don't clip owned entities against owner
402                         if (traceowner == touch)
403                                 continue;
404                         // don't clip owner against owned entities
405                         if (passedictprog == PRVM_clientedictedict(touch, owner))
406                                 continue;
407                         // don't clip points against points (they can't collide)
408                         if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
409                                 continue;
410                 }
411
412                 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
413
414                 // might interact, so do an exact clip
415                 model = NULL;
416                 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
417                         model = CL_GetModelFromEdict(touch);
418                 if (model)
419                         Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
420                 else
421                         Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
422                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
423                 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
424                 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
425                 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
426                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
427                 else
428                         Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
429
430                 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
431                         *hitnetworkentity = 0;
432                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
433         }
434
435 finished:
436         return cliptrace;
437 }
438
439 /*
440 ==================
441 CL_TraceLine
442 ==================
443 */
444 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
445 trace_t CL_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities, qboolean hitsurfaces)
446 #else
447 trace_t CL_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities, qboolean hitsurfaces)
448 #endif
449 {
450         prvm_prog_t *prog = CLVM_prog;
451         int i, bodysupercontents;
452         int passedictprog;
453         prvm_edict_t *traceowner, *touch;
454         trace_t trace;
455         // temporary storage because prvm_vec_t may need conversion
456         vec3_t touchmins, touchmaxs;
457         // bounding box of entire move area
458         vec3_t clipboxmins, clipboxmaxs;
459         // size when clipping against monsters
460         vec3_t clipmins2, clipmaxs2;
461         // start and end origin of move
462         vec3_t clipstart, clipend;
463         // trace results
464         trace_t cliptrace;
465         // matrices to transform into/out of other entity's space
466         matrix4x4_t matrix, imatrix;
467         // model of other entity
468         dp_model_t *model;
469         // list of entities to test for collisions
470         int numtouchedicts;
471         static prvm_edict_t *touchedicts[MAX_EDICTS];
472 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
473         vec3_t end;
474         vec_t len = 0;
475
476         if (VectorCompare(start, pEnd))
477                 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
478
479         if(collision_endposnudge.value > 0)
480         {
481                 // TRICK: make the trace 1 qu longer!
482                 VectorSubtract(pEnd, start, end);
483                 len = VectorNormalizeLength(end);
484                 VectorMA(pEnd, collision_endposnudge.value, end, end);
485         }
486         else
487                 VectorCopy(pEnd, end);
488 #else
489         if (VectorCompare(start, end))
490                 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
491 #endif
492
493         if (hitnetworkentity)
494                 *hitnetworkentity = 0;
495
496         VectorCopy(start, clipstart);
497         VectorCopy(end, clipend);
498         VectorClear(clipmins2);
499         VectorClear(clipmaxs2);
500 #if COLLISIONPARANOID >= 3
501         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
502 #endif
503
504         // clip to world
505         Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, hitsurfaces);
506         cliptrace.bmodelstartsolid = cliptrace.startsolid;
507         if (cliptrace.startsolid || cliptrace.fraction < 1)
508                 cliptrace.ent = prog ? prog->edicts : NULL;
509         if (type == MOVE_WORLDONLY)
510                 goto finished;
511
512         if (type == MOVE_MISSILE)
513         {
514                 // LordHavoc: modified this, was = -15, now -= 15
515                 for (i = 0;i < 3;i++)
516                 {
517                         clipmins2[i] -= 15;
518                         clipmaxs2[i] += 15;
519                 }
520         }
521
522         // create the bounding box of the entire move
523         for (i = 0;i < 3;i++)
524         {
525                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
526                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
527         }
528
529         // debug override to test against everything
530         if (sv_debugmove.integer)
531         {
532                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
533                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
534         }
535
536         // if the passedict is world, make it NULL (to avoid two checks each time)
537         // this checks prog because this function is often called without a CSQC
538         // VM context
539         if (prog == NULL || passedict == prog->edicts)
540                 passedict = NULL;
541         // precalculate prog value for passedict for comparisons
542         passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
543         // precalculate passedict's owner edict pointer for comparisons
544         traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
545
546         // collide against network entities
547         if (hitnetworkbrushmodels)
548         {
549                 for (i = 0;i < cl.num_brushmodel_entities;i++)
550                 {
551                         entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
552                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
553                                 continue;
554                         Collision_ClipLineToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, hitsurfaces);
555                         if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
556                                 *hitnetworkentity = cl.brushmodel_entities[i];
557                         Collision_CombineTraces(&cliptrace, &trace, NULL, true);
558                 }
559         }
560
561         // collide against player entities
562         if (hitnetworkplayers)
563         {
564                 vec3_t origin, entmins, entmaxs;
565                 matrix4x4_t entmatrix, entinversematrix;
566
567                 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
568                 {
569                         // don't hit network players, if we are a nonsolid player
570                         if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
571                                 goto skipnetworkplayers;
572                 }
573
574                 for (i = 1;i <= cl.maxclients;i++)
575                 {
576                         entity_render_t *ent = &cl.entities[i].render;
577
578                         // don't hit ourselves
579                         if (i == cl.playerentity)
580                                 continue;
581
582                         // don't hit players that don't exist
583                         if (!cl.scores[i-1].name[0])
584                                 continue;
585
586                         if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
587                         {
588                                 // don't hit spectators or nonsolid players
589                                 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
590                                         continue;
591                         }
592
593                         Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
594                         VectorAdd(origin, cl.playerstandmins, entmins);
595                         VectorAdd(origin, cl.playerstandmaxs, entmaxs);
596                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
597                                 continue;
598                         Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
599                         Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
600                         Collision_ClipLineToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask, hitsurfaces);
601                         if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
602                                 *hitnetworkentity = i;
603                         Collision_CombineTraces(&cliptrace, &trace, NULL, false);
604                 }
605
606 skipnetworkplayers:
607                 ;
608         }
609
610         // clip to entities
611         // because this uses World_EntitiestoBox, we know all entity boxes overlap
612         // the clip region, so we can skip culling checks in the loop below
613         // note: if prog is NULL then there won't be any linked entities
614         numtouchedicts = 0;
615         if (hitcsqcentities && prog != NULL)
616         {
617                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
618                 if (numtouchedicts > MAX_EDICTS)
619                 {
620                         // this never happens
621                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
622                         numtouchedicts = MAX_EDICTS;
623                 }
624         }
625         for (i = 0;i < numtouchedicts;i++)
626         {
627                 touch = touchedicts[i];
628
629                 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
630                         continue;
631                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
632                         continue;
633
634                 if (passedict)
635                 {
636                         // don't clip against self
637                         if (passedict == touch)
638                                 continue;
639                         // don't clip owned entities against owner
640                         if (traceowner == touch)
641                                 continue;
642                         // don't clip owner against owned entities
643                         if (passedictprog == PRVM_clientedictedict(touch, owner))
644                                 continue;
645                         // don't clip points against points (they can't collide)
646                         if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
647                                 continue;
648                 }
649
650                 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
651
652                 // might interact, so do an exact clip
653                 model = NULL;
654                 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
655                         model = CL_GetModelFromEdict(touch);
656                 if (model)
657                         Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
658                 else
659                         Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
660                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
661                 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
662                 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
663                 if (type == MOVE_MISSILE && (int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
664                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
665                 else
666                         Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, hitsurfaces);
667
668                 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
669                         *hitnetworkentity = 0;
670                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
671         }
672
673 finished:
674 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
675         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
676                 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
677 #endif
678         return cliptrace;
679 }
680
681 /*
682 ==================
683 CL_Move
684 ==================
685 */
686 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
687 trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
688 #else
689 trace_t CL_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, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
690 #endif
691 {
692         prvm_prog_t *prog = CLVM_prog;
693         vec3_t hullmins, hullmaxs;
694         int i, bodysupercontents;
695         int passedictprog;
696         qboolean pointtrace;
697         prvm_edict_t *traceowner, *touch;
698         trace_t trace;
699         // temporary storage because prvm_vec_t may need conversion
700         vec3_t touchmins, touchmaxs;
701         // bounding box of entire move area
702         vec3_t clipboxmins, clipboxmaxs;
703         // size of the moving object
704         vec3_t clipmins, clipmaxs;
705         // size when clipping against monsters
706         vec3_t clipmins2, clipmaxs2;
707         // start and end origin of move
708         vec3_t clipstart, clipend;
709         // trace results
710         trace_t cliptrace;
711         // matrices to transform into/out of other entity's space
712         matrix4x4_t matrix, imatrix;
713         // model of other entity
714         dp_model_t *model;
715         // list of entities to test for collisions
716         int numtouchedicts;
717         static prvm_edict_t *touchedicts[MAX_EDICTS];
718 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
719         vec3_t end;
720         vec_t len = 0;
721
722         if (VectorCompare(mins, maxs))
723         {
724                 vec3_t shiftstart, shiftend;
725                 VectorAdd(start, mins, shiftstart);
726                 VectorAdd(pEnd, mins, shiftend);
727                 if (VectorCompare(start, pEnd))
728                         trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
729                 else
730                         trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
731                 VectorSubtract(trace.endpos, mins, trace.endpos);
732                 return trace;
733         }
734
735         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
736         {
737                 // TRICK: make the trace 1 qu longer!
738                 VectorSubtract(pEnd, start, end);
739                 len = VectorNormalizeLength(end);
740                 VectorMA(pEnd, collision_endposnudge.value, end, end);
741         }
742         else
743                 VectorCopy(pEnd, end);
744 #else
745         if (VectorCompare(mins, maxs))
746         {
747                 vec3_t shiftstart, shiftend;
748                 VectorAdd(start, mins, shiftstart);
749                 VectorAdd(end, mins, shiftend);
750                 if (VectorCompare(start, end))
751                         trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
752                 else
753                         trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
754                 VectorSubtract(trace.endpos, mins, trace.endpos);
755                 return trace;
756         }
757 #endif
758
759         if (hitnetworkentity)
760                 *hitnetworkentity = 0;
761
762         VectorCopy(start, clipstart);
763         VectorCopy(end, clipend);
764         VectorCopy(mins, clipmins);
765         VectorCopy(maxs, clipmaxs);
766         VectorCopy(mins, clipmins2);
767         VectorCopy(maxs, clipmaxs2);
768 #if COLLISIONPARANOID >= 3
769         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
770 #endif
771
772         // clip to world
773         Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
774         cliptrace.bmodelstartsolid = cliptrace.startsolid;
775         if (cliptrace.startsolid || cliptrace.fraction < 1)
776                 cliptrace.ent = prog ? prog->edicts : NULL;
777         if (type == MOVE_WORLDONLY)
778                 goto finished;
779
780         if (type == MOVE_MISSILE)
781         {
782                 // LordHavoc: modified this, was = -15, now -= 15
783                 for (i = 0;i < 3;i++)
784                 {
785                         clipmins2[i] -= 15;
786                         clipmaxs2[i] += 15;
787                 }
788         }
789
790         // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
791         if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
792                 cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
793         else
794         {
795                 VectorCopy(clipmins, hullmins);
796                 VectorCopy(clipmaxs, hullmaxs);
797         }
798
799         // create the bounding box of the entire move
800         for (i = 0;i < 3;i++)
801         {
802                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
803                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
804         }
805
806         // debug override to test against everything
807         if (sv_debugmove.integer)
808         {
809                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
810                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
811         }
812
813         // if the passedict is world, make it NULL (to avoid two checks each time)
814         // this checks prog because this function is often called without a CSQC
815         // VM context
816         if (prog == NULL || passedict == prog->edicts)
817                 passedict = NULL;
818         // precalculate prog value for passedict for comparisons
819         passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
820         // figure out whether this is a point trace for comparisons
821         pointtrace = VectorCompare(clipmins, clipmaxs);
822         // precalculate passedict's owner edict pointer for comparisons
823         traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
824
825         // collide against network entities
826         if (hitnetworkbrushmodels)
827         {
828                 for (i = 0;i < cl.num_brushmodel_entities;i++)
829                 {
830                         entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
831                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
832                                 continue;
833                         Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask);
834                         if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
835                                 *hitnetworkentity = cl.brushmodel_entities[i];
836                         Collision_CombineTraces(&cliptrace, &trace, NULL, true);
837                 }
838         }
839
840         // collide against player entities
841         if (hitnetworkplayers)
842         {
843                 vec3_t origin, entmins, entmaxs;
844                 matrix4x4_t entmatrix, entinversematrix;
845
846                 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
847                 {
848                         // don't hit network players, if we are a nonsolid player
849                         if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
850                                 goto skipnetworkplayers;
851                 }
852
853                 for (i = 1;i <= cl.maxclients;i++)
854                 {
855                         entity_render_t *ent = &cl.entities[i].render;
856
857                         // don't hit ourselves
858                         if (i == cl.playerentity)
859                                 continue;
860
861                         // don't hit players that don't exist
862                         if (!cl.scores[i-1].name[0])
863                                 continue;
864
865                         if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
866                         {
867                                 // don't hit spectators or nonsolid players
868                                 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
869                                         continue;
870                         }
871
872                         Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
873                         VectorAdd(origin, cl.playerstandmins, entmins);
874                         VectorAdd(origin, cl.playerstandmaxs, entmaxs);
875                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
876                                 continue;
877                         Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
878                         Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
879                         Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask);
880                         if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
881                                 *hitnetworkentity = i;
882                         Collision_CombineTraces(&cliptrace, &trace, NULL, false);
883                 }
884
885 skipnetworkplayers:
886                 ;
887         }
888
889         // clip to entities
890         // because this uses World_EntitiestoBox, we know all entity boxes overlap
891         // the clip region, so we can skip culling checks in the loop below
892         // note: if prog is NULL then there won't be any linked entities
893         numtouchedicts = 0;
894         if (hitcsqcentities && prog != NULL)
895         {
896                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
897                 if (numtouchedicts > MAX_EDICTS)
898                 {
899                         // this never happens
900                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
901                         numtouchedicts = MAX_EDICTS;
902                 }
903         }
904         for (i = 0;i < numtouchedicts;i++)
905         {
906                 touch = touchedicts[i];
907
908                 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
909                         continue;
910                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
911                         continue;
912
913                 if (passedict)
914                 {
915                         // don't clip against self
916                         if (passedict == touch)
917                                 continue;
918                         // don't clip owned entities against owner
919                         if (traceowner == touch)
920                                 continue;
921                         // don't clip owner against owned entities
922                         if (passedictprog == PRVM_clientedictedict(touch, owner))
923                                 continue;
924                         // don't clip points against points (they can't collide)
925                         if (pointtrace && VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
926                                 continue;
927                 }
928
929                 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
930
931                 // might interact, so do an exact clip
932                 model = NULL;
933                 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
934                         model = CL_GetModelFromEdict(touch);
935                 if (model)
936                         Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
937                 else
938                         Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
939                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
940                 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
941                 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
942                 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
943                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
944                 else
945                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
946
947                 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
948                         *hitnetworkentity = 0;
949                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
950         }
951
952 finished:
953 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
954         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
955                 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
956 #endif
957         return cliptrace;
958 }
959
960 /*
961 ==================
962 CL_Cache_TraceLine
963 ==================
964 */
965 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
966 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t pEnd, int type, int hitsupercontentsmask)
967 #else
968 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int type, int hitsupercontentsmask)
969 #endif
970 {
971         prvm_prog_t *prog = CLVM_prog;
972         int i;
973         prvm_edict_t *touch;
974         trace_t trace;
975         // bounding box of entire move area
976         vec3_t clipboxmins, clipboxmaxs;
977         // start and end origin of move
978         vec3_t clipstart, clipend;
979         // trace results
980         trace_t cliptrace;
981         // matrices to transform into/out of other entity's space
982         matrix4x4_t matrix, imatrix;
983         // model of other entity
984         dp_model_t *model;
985         // list of entities to test for collisions
986         int numtouchedicts;
987         static prvm_edict_t *touchedicts[MAX_EDICTS];
988 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
989         vec3_t end;
990         vec_t len = 0;
991
992         if(collision_endposnudge.value > 0 && !VectorCompare(start, pEnd))
993         {
994                 // TRICK: make the trace 1 qu longer!
995                 VectorSubtract(pEnd, start, end);
996                 len = VectorNormalizeLength(end);
997                 VectorMA(pEnd, collision_endposnudge.value, end, end);
998         }
999         else
1000                 VectorCopy(pEnd, end);
1001 #endif
1002
1003         VectorCopy(start, clipstart);
1004         VectorCopy(end, clipend);
1005 #if COLLISIONPARANOID >= 3
1006         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
1007 #endif
1008
1009         // clip to world
1010         Collision_Cache_ClipLineToWorldSurfaces(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask);
1011         cliptrace.bmodelstartsolid = cliptrace.startsolid;
1012         if (cliptrace.startsolid || cliptrace.fraction < 1)
1013                 cliptrace.ent = prog ? prog->edicts : NULL;
1014         if (type == MOVE_WORLDONLY)
1015                 goto finished;
1016
1017         // create the bounding box of the entire move
1018         for (i = 0;i < 3;i++)
1019         {
1020                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) - 1;
1021                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + 1;
1022         }
1023
1024         // if the passedict is world, make it NULL (to avoid two checks each time)
1025         // this checks prog because this function is often called without a CSQC
1026         // VM context
1027
1028         // collide against network entities
1029         for (i = 0;i < cl.num_brushmodel_entities;i++)
1030         {
1031                 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
1032                 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
1033                         continue;
1034                 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, ent->model, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask);
1035                 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
1036         }
1037
1038         // clip to entities
1039         // because this uses World_EntitiestoBox, we know all entity boxes overlap
1040         // the clip region, so we can skip culling checks in the loop below
1041         // note: if prog is NULL then there won't be any linked entities
1042         numtouchedicts = 0;
1043         if (prog != NULL)
1044         {
1045                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
1046                 if (numtouchedicts > MAX_EDICTS)
1047                 {
1048                         // this never happens
1049                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
1050                         numtouchedicts = MAX_EDICTS;
1051                 }
1052         }
1053         for (i = 0;i < numtouchedicts;i++)
1054         {
1055                 touch = touchedicts[i];
1056                 // might interact, so do an exact clip
1057                 // only hit entity models, not collision shapes
1058                 model = CL_GetModelFromEdict(touch);
1059                 if (!model)
1060                         continue;
1061                 // animated models are too slow to collide against and can't be cached
1062                 if (touch->priv.server->frameblend || touch->priv.server->skeleton.relativetransforms)
1063                         continue;
1064                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
1065                         continue;
1066                 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
1067                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
1068                 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, model, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
1069                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
1070         }
1071
1072 finished:
1073 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
1074         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
1075                 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
1076 #endif
1077         return cliptrace;
1078 }
1079