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