]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_collision.c
Assume that GL_ARB_texture_compression is supported.
[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
231         if (hitnetworkentity)
232                 *hitnetworkentity = 0;
233
234         VectorCopy(start, clipstart);
235         VectorClear(clipmins2);
236         VectorClear(clipmaxs2);
237 #if COLLISIONPARANOID >= 3
238         Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
239 #endif
240
241         // clip to world
242         Collision_ClipPointToWorld(&cliptrace, cl.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
243         cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
244         if (cliptrace.startsolid || cliptrace.fraction < 1)
245                 cliptrace.ent = prog ? prog->edicts : NULL;
246         if (type == MOVE_WORLDONLY)
247                 goto finished;
248
249         if (type == MOVE_MISSILE)
250         {
251                 // LordHavoc: modified this, was = -15, now -= 15
252                 for (i = 0;i < 3;i++)
253                 {
254                         clipmins2[i] -= 15;
255                         clipmaxs2[i] += 15;
256                 }
257         }
258
259         // create the bounding box of the entire move
260         for (i = 0;i < 3;i++)
261         {
262                 clipboxmins[i] = clipstart[i] - 1;
263                 clipboxmaxs[i] = clipstart[i] + 1;
264         }
265
266         // debug override to test against everything
267         if (sv_debugmove.integer)
268         {
269                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
270                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
271         }
272
273         // if the passedict is world, make it NULL (to avoid two checks each time)
274         // this checks prog because this function is often called without a CSQC
275         // VM context
276         if (prog == NULL || passedict == prog->edicts)
277                 passedict = NULL;
278         // precalculate prog value for passedict for comparisons
279         passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
280         // precalculate passedict's owner edict pointer for comparisons
281         traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
282
283         // collide against network entities
284         if (hitnetworkbrushmodels)
285         {
286                 for (i = 0;i < cl.num_brushmodel_entities;i++)
287                 {
288                         entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
289                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
290                                 continue;
291                         Collision_ClipPointToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
292                         if (cliptrace.fraction > trace.fraction && hitnetworkentity)
293                                 *hitnetworkentity = cl.brushmodel_entities[i];
294                         Collision_CombineTraces(&cliptrace, &trace, NULL, true);
295                 }
296         }
297
298         // collide against player entities
299         if (hitnetworkplayers)
300         {
301                 vec3_t origin, entmins, entmaxs;
302                 matrix4x4_t entmatrix, entinversematrix;
303
304                 if(IS_OLDNEXUIZ_DERIVED(gamemode))
305                 {
306                         // don't hit network players, if we are a nonsolid player
307                         if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
308                                 goto skipnetworkplayers;
309                 }
310
311                 for (i = 1;i <= cl.maxclients;i++)
312                 {
313                         entity_render_t *ent = &cl.entities[i].render;
314
315                         // don't hit ourselves
316                         if (i == cl.playerentity)
317                                 continue;
318
319                         // don't hit players that don't exist
320                         if (!cl.entities_active[i])
321                                 continue;
322                         if (!cl.scores[i-1].name[0])
323                                 continue;
324
325                         if(IS_OLDNEXUIZ_DERIVED(gamemode))
326                         {
327                                 // don't hit spectators or nonsolid players
328                                 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
329                                         continue;
330                         }
331
332                         Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
333                         VectorAdd(origin, cl.playerstandmins, entmins);
334                         VectorAdd(origin, cl.playerstandmaxs, entmaxs);
335                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
336                                 continue;
337                         Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
338                         Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
339                         Collision_ClipPointToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
340                         if (cliptrace.fraction > trace.fraction && hitnetworkentity)
341                                 *hitnetworkentity = i;
342                         Collision_CombineTraces(&cliptrace, &trace, NULL, false);
343                 }
344
345 skipnetworkplayers:
346                 ;
347         }
348
349         // clip to entities
350         // because this uses World_EntitiestoBox, we know all entity boxes overlap
351         // the clip region, so we can skip culling checks in the loop below
352         // note: if prog is NULL then there won't be any linked entities
353         numtouchedicts = 0;
354         if (hitcsqcentities && prog != NULL)
355         {
356                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
357                 if (numtouchedicts > MAX_EDICTS)
358                 {
359                         // this never happens
360                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
361                         numtouchedicts = MAX_EDICTS;
362                 }
363         }
364         for (i = 0;i < numtouchedicts;i++)
365         {
366                 touch = touchedicts[i];
367
368                 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
369                         continue;
370                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
371                         continue;
372
373                 if (passedict)
374                 {
375                         // don't clip against self
376                         if (passedict == touch)
377                                 continue;
378                         // don't clip owned entities against owner
379                         if (traceowner == touch)
380                                 continue;
381                         // don't clip owner against owned entities
382                         if (passedictprog == PRVM_clientedictedict(touch, owner))
383                                 continue;
384                         // don't clip points against points (they can't collide)
385                         if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
386                                 continue;
387                 }
388
389                 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
390
391                 // might interact, so do an exact clip
392                 model = NULL;
393                 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
394                         model = CL_GetModelFromEdict(touch);
395                 if (model)
396                         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);
397                 else
398                         Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
399                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
400                 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
401                 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
402                 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
403                         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);
404                 else
405                         Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
406
407                 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
408                         *hitnetworkentity = 0;
409                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
410         }
411
412 finished:
413         return cliptrace;
414 }
415
416 /*
417 ==================
418 CL_TraceLine
419 ==================
420 */
421 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)
422 {
423         prvm_prog_t *prog = CLVM_prog;
424         int i, bodysupercontents;
425         int passedictprog;
426         prvm_edict_t *traceowner, *touch;
427         trace_t trace;
428         // temporary storage because prvm_vec_t may need conversion
429         vec3_t touchmins, touchmaxs;
430         // bounding box of entire move area
431         vec3_t clipboxmins, clipboxmaxs;
432         // size when clipping against monsters
433         vec3_t clipmins2, clipmaxs2;
434         // start and end origin of move
435         vec3_t clipstart, clipend;
436         // trace results
437         trace_t cliptrace;
438         // matrices to transform into/out of other entity's space
439         matrix4x4_t matrix, imatrix;
440         // model of other entity
441         dp_model_t *model;
442         // list of entities to test for collisions
443         int numtouchedicts;
444         static prvm_edict_t *touchedicts[MAX_EDICTS];
445         if (VectorCompare(start, end))
446                 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
447
448         if (hitnetworkentity)
449                 *hitnetworkentity = 0;
450
451         VectorCopy(start, clipstart);
452         VectorCopy(end, clipend);
453         VectorClear(clipmins2);
454         VectorClear(clipmaxs2);
455 #if COLLISIONPARANOID >= 3
456         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
457 #endif
458
459         // clip to world
460         Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitsurfaces);
461         cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
462         if (cliptrace.startsolid || cliptrace.fraction < 1)
463                 cliptrace.ent = prog ? prog->edicts : NULL;
464         if (type == MOVE_WORLDONLY)
465                 goto finished;
466
467         if (type == MOVE_MISSILE)
468         {
469                 // LordHavoc: modified this, was = -15, now -= 15
470                 for (i = 0;i < 3;i++)
471                 {
472                         clipmins2[i] -= 15;
473                         clipmaxs2[i] += 15;
474                 }
475         }
476
477         // create the bounding box of the entire move
478         for (i = 0;i < 3;i++)
479         {
480                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
481                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
482         }
483
484         // debug override to test against everything
485         if (sv_debugmove.integer)
486         {
487                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
488                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
489         }
490
491         // if the passedict is world, make it NULL (to avoid two checks each time)
492         // this checks prog because this function is often called without a CSQC
493         // VM context
494         if (prog == NULL || passedict == prog->edicts)
495                 passedict = NULL;
496         // precalculate prog value for passedict for comparisons
497         passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
498         // precalculate passedict's owner edict pointer for comparisons
499         traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
500
501         // collide against network entities
502         if (hitnetworkbrushmodels)
503         {
504                 for (i = 0;i < cl.num_brushmodel_entities;i++)
505                 {
506                         entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
507                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
508                                 continue;
509                         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);
510                         if (cliptrace.fraction > trace.fraction && hitnetworkentity)
511                                 *hitnetworkentity = cl.brushmodel_entities[i];
512                         Collision_CombineTraces(&cliptrace, &trace, NULL, true);
513                 }
514         }
515
516         // collide against player entities
517         if (hitnetworkplayers)
518         {
519                 vec3_t origin, entmins, entmaxs;
520                 matrix4x4_t entmatrix, entinversematrix;
521
522                 if(IS_OLDNEXUIZ_DERIVED(gamemode))
523                 {
524                         // don't hit network players, if we are a nonsolid player
525                         if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
526                                 goto skipnetworkplayers;
527                 }
528
529                 for (i = 1;i <= cl.maxclients;i++)
530                 {
531                         entity_render_t *ent = &cl.entities[i].render;
532
533                         // don't hit ourselves
534                         if (i == cl.playerentity)
535                                 continue;
536
537                         // don't hit players that don't exist
538                         if (!cl.entities_active[i])
539                                 continue;
540                         if (!cl.scores[i-1].name[0])
541                                 continue;
542
543                         if(IS_OLDNEXUIZ_DERIVED(gamemode))
544                         {
545                                 // don't hit spectators or nonsolid players
546                                 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
547                                         continue;
548                         }
549
550                         Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
551                         VectorAdd(origin, cl.playerstandmins, entmins);
552                         VectorAdd(origin, cl.playerstandmaxs, entmaxs);
553                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
554                                 continue;
555                         Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
556                         Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
557                         Collision_ClipLineToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitsurfaces);
558                         if (cliptrace.fraction > trace.fraction && hitnetworkentity)
559                                 *hitnetworkentity = i;
560                         Collision_CombineTraces(&cliptrace, &trace, NULL, false);
561                 }
562
563 skipnetworkplayers:
564                 ;
565         }
566
567         // clip to entities
568         // because this uses World_EntitiestoBox, we know all entity boxes overlap
569         // the clip region, so we can skip culling checks in the loop below
570         // note: if prog is NULL then there won't be any linked entities
571         numtouchedicts = 0;
572         if (hitcsqcentities && prog != NULL)
573         {
574                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
575                 if (numtouchedicts > MAX_EDICTS)
576                 {
577                         // this never happens
578                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
579                         numtouchedicts = MAX_EDICTS;
580                 }
581         }
582         for (i = 0;i < numtouchedicts;i++)
583         {
584                 touch = touchedicts[i];
585
586                 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
587                         continue;
588                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
589                         continue;
590
591                 if (passedict)
592                 {
593                         // don't clip against self
594                         if (passedict == touch)
595                                 continue;
596                         // don't clip owned entities against owner
597                         if (traceowner == touch)
598                                 continue;
599                         // don't clip owner against owned entities
600                         if (passedictprog == PRVM_clientedictedict(touch, owner))
601                                 continue;
602                         // don't clip points against points (they can't collide)
603                         if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
604                                 continue;
605                 }
606
607                 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
608
609                 // might interact, so do an exact clip
610                 model = NULL;
611                 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
612                         model = CL_GetModelFromEdict(touch);
613                 if (model)
614                         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);
615                 else
616                         Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
617                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
618                 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
619                 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
620                 if (type == MOVE_MISSILE && (int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
621                         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);
622                 else
623                         Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitsurfaces);
624
625                 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
626                         *hitnetworkentity = 0;
627                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
628         }
629
630 finished:
631         return cliptrace;
632 }
633
634 /*
635 ==================
636 CL_Move
637 ==================
638 */
639 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)
640 {
641         prvm_prog_t *prog = CLVM_prog;
642         vec3_t hullmins, hullmaxs;
643         int i, bodysupercontents;
644         int passedictprog;
645         qboolean pointtrace;
646         prvm_edict_t *traceowner, *touch;
647         trace_t trace;
648         // temporary storage because prvm_vec_t may need conversion
649         vec3_t touchmins, touchmaxs;
650         // bounding box of entire move area
651         vec3_t clipboxmins, clipboxmaxs;
652         // size of the moving object
653         vec3_t clipmins, clipmaxs;
654         // size when clipping against monsters
655         vec3_t clipmins2, clipmaxs2;
656         // start and end origin of move
657         vec3_t clipstart, clipend;
658         // trace results
659         trace_t cliptrace;
660         // matrices to transform into/out of other entity's space
661         matrix4x4_t matrix, imatrix;
662         // model of other entity
663         dp_model_t *model;
664         // list of entities to test for collisions
665         int numtouchedicts;
666         static prvm_edict_t *touchedicts[MAX_EDICTS];
667         if (VectorCompare(mins, maxs))
668         {
669                 vec3_t shiftstart, shiftend;
670                 VectorAdd(start, mins, shiftstart);
671                 VectorAdd(end, mins, shiftend);
672                 if (VectorCompare(start, end))
673                         trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
674                 else
675                         trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
676                 VectorSubtract(trace.endpos, mins, trace.endpos);
677                 return trace;
678         }
679
680         if (hitnetworkentity)
681                 *hitnetworkentity = 0;
682
683         VectorCopy(start, clipstart);
684         VectorCopy(end, clipend);
685         VectorCopy(mins, clipmins);
686         VectorCopy(maxs, clipmaxs);
687         VectorCopy(mins, clipmins2);
688         VectorCopy(maxs, clipmaxs2);
689 #if COLLISIONPARANOID >= 3
690         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
691 #endif
692
693         // clip to world
694         Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
695         cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
696         if (cliptrace.startsolid || cliptrace.fraction < 1)
697                 cliptrace.ent = prog ? prog->edicts : NULL;
698         if (type == MOVE_WORLDONLY)
699                 goto finished;
700
701         if (type == MOVE_MISSILE)
702         {
703                 // LordHavoc: modified this, was = -15, now -= 15
704                 for (i = 0;i < 3;i++)
705                 {
706                         clipmins2[i] -= 15;
707                         clipmaxs2[i] += 15;
708                 }
709         }
710
711         // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
712         if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
713                 cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
714         else
715         {
716                 VectorCopy(clipmins, hullmins);
717                 VectorCopy(clipmaxs, hullmaxs);
718         }
719
720         // create the bounding box of the entire move
721         for (i = 0;i < 3;i++)
722         {
723                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
724                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
725         }
726
727         // debug override to test against everything
728         if (sv_debugmove.integer)
729         {
730                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
731                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
732         }
733
734         // if the passedict is world, make it NULL (to avoid two checks each time)
735         // this checks prog because this function is often called without a CSQC
736         // VM context
737         if (prog == NULL || passedict == prog->edicts)
738                 passedict = NULL;
739         // precalculate prog value for passedict for comparisons
740         passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
741         // figure out whether this is a point trace for comparisons
742         pointtrace = VectorCompare(clipmins, clipmaxs);
743         // precalculate passedict's owner edict pointer for comparisons
744         traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
745
746         // collide against network entities
747         if (hitnetworkbrushmodels)
748         {
749                 for (i = 0;i < cl.num_brushmodel_entities;i++)
750                 {
751                         entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
752                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
753                                 continue;
754                         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);
755                         if (cliptrace.fraction > trace.fraction && hitnetworkentity)
756                                 *hitnetworkentity = cl.brushmodel_entities[i];
757                         Collision_CombineTraces(&cliptrace, &trace, NULL, true);
758                 }
759         }
760
761         // collide against player entities
762         if (hitnetworkplayers)
763         {
764                 vec3_t origin, entmins, entmaxs;
765                 matrix4x4_t entmatrix, entinversematrix;
766
767                 if(IS_OLDNEXUIZ_DERIVED(gamemode))
768                 {
769                         // don't hit network players, if we are a nonsolid player
770                         if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
771                                 goto skipnetworkplayers;
772                 }
773
774                 for (i = 1;i <= cl.maxclients;i++)
775                 {
776                         entity_render_t *ent = &cl.entities[i].render;
777
778                         // don't hit ourselves
779                         if (i == cl.playerentity)
780                                 continue;
781
782                         // don't hit players that don't exist
783                         if (!cl.entities_active[i])
784                                 continue;
785                         if (!cl.scores[i-1].name[0])
786                                 continue;
787
788                         if(IS_OLDNEXUIZ_DERIVED(gamemode))
789                         {
790                                 // don't hit spectators or nonsolid players
791                                 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
792                                         continue;
793                         }
794
795                         Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
796                         VectorAdd(origin, cl.playerstandmins, entmins);
797                         VectorAdd(origin, cl.playerstandmaxs, entmaxs);
798                         if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
799                                 continue;
800                         Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
801                         Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
802                         Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
803                         if (cliptrace.fraction > trace.fraction && hitnetworkentity)
804                                 *hitnetworkentity = i;
805                         Collision_CombineTraces(&cliptrace, &trace, NULL, false);
806                 }
807
808 skipnetworkplayers:
809                 ;
810         }
811
812         // clip to entities
813         // because this uses World_EntitiestoBox, we know all entity boxes overlap
814         // the clip region, so we can skip culling checks in the loop below
815         // note: if prog is NULL then there won't be any linked entities
816         numtouchedicts = 0;
817         if (hitcsqcentities && prog != NULL)
818         {
819                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
820                 if (numtouchedicts > MAX_EDICTS)
821                 {
822                         // this never happens
823                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
824                         numtouchedicts = MAX_EDICTS;
825                 }
826         }
827         for (i = 0;i < numtouchedicts;i++)
828         {
829                 touch = touchedicts[i];
830
831                 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
832                         continue;
833                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
834                         continue;
835
836                 if (passedict)
837                 {
838                         // don't clip against self
839                         if (passedict == touch)
840                                 continue;
841                         // don't clip owned entities against owner
842                         if (traceowner == touch)
843                                 continue;
844                         // don't clip owner against owned entities
845                         if (passedictprog == PRVM_clientedictedict(touch, owner))
846                                 continue;
847                         // don't clip points against points (they can't collide)
848                         if (pointtrace && VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
849                                 continue;
850                 }
851
852                 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
853
854                 // might interact, so do an exact clip
855                 model = NULL;
856                 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
857                         model = CL_GetModelFromEdict(touch);
858                 if (model)
859                         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);
860                 else
861                         Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
862                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
863                 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
864                 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
865                 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
866                         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);
867                 else
868                         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);
869
870                 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
871                         *hitnetworkentity = 0;
872                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
873         }
874
875 finished:
876         return cliptrace;
877 }
878
879 /*
880 ==================
881 CL_Cache_TraceLine
882 ==================
883 */
884 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int type, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
885 {
886         prvm_prog_t *prog = CLVM_prog;
887         int i;
888         prvm_edict_t *touch;
889         trace_t trace;
890         // bounding box of entire move area
891         vec3_t clipboxmins, clipboxmaxs;
892         // start and end origin of move
893         vec3_t clipstart, clipend;
894         // trace results
895         trace_t cliptrace;
896         // matrices to transform into/out of other entity's space
897         matrix4x4_t matrix, imatrix;
898         // model of other entity
899         dp_model_t *model;
900         // list of entities to test for collisions
901         int numtouchedicts;
902         static prvm_edict_t *touchedicts[MAX_EDICTS];
903
904         VectorCopy(start, clipstart);
905         VectorCopy(end, clipend);
906 #if COLLISIONPARANOID >= 3
907         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
908 #endif
909
910         // clip to world
911         Collision_Cache_ClipLineToWorldSurfaces(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
912         cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
913         if (cliptrace.startsolid || cliptrace.fraction < 1)
914                 cliptrace.ent = prog ? prog->edicts : NULL;
915         if (type == MOVE_WORLDONLY)
916                 goto finished;
917
918         // create the bounding box of the entire move
919         for (i = 0;i < 3;i++)
920         {
921                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) - 1;
922                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + 1;
923         }
924
925         // if the passedict is world, make it NULL (to avoid two checks each time)
926         // this checks prog because this function is often called without a CSQC
927         // VM context
928
929         // collide against network entities
930         for (i = 0;i < cl.num_brushmodel_entities;i++)
931         {
932                 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
933                 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
934                         continue;
935                 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, ent->model, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
936                 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
937         }
938
939         // clip to entities
940         // because this uses World_EntitiestoBox, we know all entity boxes overlap
941         // the clip region, so we can skip culling checks in the loop below
942         // note: if prog is NULL then there won't be any linked entities
943         numtouchedicts = 0;
944         if (prog != NULL)
945         {
946                 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
947                 if (numtouchedicts > MAX_EDICTS)
948                 {
949                         // this never happens
950                         Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
951                         numtouchedicts = MAX_EDICTS;
952                 }
953         }
954         for (i = 0;i < numtouchedicts;i++)
955         {
956                 touch = touchedicts[i];
957                 // might interact, so do an exact clip
958                 // only hit entity models, not collision shapes
959                 model = CL_GetModelFromEdict(touch);
960                 if (!model)
961                         continue;
962                 // animated models are not suitable for caching
963                 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)
964                         continue;
965                 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
966                         continue;
967                 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);
968                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
969                 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, model, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
970                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
971         }
972
973 finished:
974         return cliptrace;
975 }
976