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