3 #include "cl_collision.h"
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)
8 float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent)
11 float maxfrac, maxrealfrac;
14 float tracemins[3], tracemaxs[3];
16 float tempnormal[3], starttransformed[3], endtransformed[3];
17 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
21 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
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);
29 VectorCopy(pEnd, end);
32 memset (&trace, 0 , sizeof(trace_t));
34 trace.realfraction = 1;
35 VectorCopy (end, trace.endpos);
39 if (cl.worldmodel && cl.worldmodel->TraceLine)
40 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, start, end, SUPERCONTENTS_SOLID);
43 VectorCopy(trace.plane.normal, normal);
44 maxfrac = trace.fraction;
45 maxrealfrac = trace.realfraction;
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]);
54 // look for embedded bmodels
55 for (n = 0;n < cl.num_entities;n++)
57 if (!cl.entities_active[n])
59 ent = &cl.entities[n].render;
60 if (!BoxesOverlap(ent->mins, ent->maxs, tracemins, tracemaxs))
62 if (!ent->model || !ent->model->TraceLine)
64 if ((ent->flags & RENDER_EXTERIORMODEL) && !chase_active.integer)
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))))
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);
78 if (maxrealfrac < trace.realfraction)
81 ent->model->TraceLine(ent->model, ent->frameblend, ent->skeleton, &trace, starttransformed, endtransformed, SUPERCONTENTS_SOLID);
83 if (maxrealfrac > trace.realfraction)
87 maxfrac = trace.fraction;
88 maxrealfrac = trace.realfraction;
91 VectorCopy(trace.plane.normal, tempnormal);
92 Matrix4x4_Transform3x3(&ent->matrix, tempnormal, normal);
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__);
100 VectorLerp(start, maxfrac, end, impact);
104 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius)
106 // FIXME: check multiple brush models
107 if (cl.worldmodel && cl.worldmodel->brush.FindNonSolidLocation)
108 cl.worldmodel->brush.FindNonSolidLocation(cl.worldmodel, in, out, radius);
111 dp_model_t *CL_GetModelByIndex(int modelindex)
117 modelindex = -(modelindex+1);
118 if (modelindex < MAX_MODELS)
119 return cl.csqc_model_precache[modelindex];
123 if(modelindex < MAX_MODELS)
124 return cl.model_precache[modelindex];
129 dp_model_t *CL_GetModelFromEdict(prvm_edict_t *ed)
131 prvm_prog_t *prog = CLVM_prog;
132 if (!ed || ed->priv.server->free)
134 return CL_GetModelByIndex((int)PRVM_clientedictfloat(ed, modelindex));
137 void CL_LinkEdict(prvm_edict_t *ent)
139 prvm_prog_t *prog = CLVM_prog;
142 if (ent == prog->edicts)
143 return; // don't add the world
145 if (ent->priv.server->free)
150 if (PRVM_clientedictfloat(ent, solid) == SOLID_BSP)
152 dp_model_t *model = CL_GetModelByIndex( (int)PRVM_clientedictfloat(ent, modelindex) );
155 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
157 model = CL_GetModelByIndex( 0 );
162 if (!model->TraceBox)
163 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
165 if (PRVM_clientedictvector(ent, angles)[0] || PRVM_clientedictvector(ent, angles)[2] || PRVM_clientedictvector(ent, avelocity)[0] || PRVM_clientedictvector(ent, avelocity)[2])
167 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmins, mins);
168 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmaxs, maxs);
170 else if (PRVM_clientedictvector(ent, angles)[1] || PRVM_clientedictvector(ent, avelocity)[1])
172 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmins, mins);
173 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmaxs, maxs);
177 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmins, mins);
178 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmaxs, maxs);
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);
190 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
191 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
194 VectorCopy(mins, PRVM_clientedictvector(ent, absmin));
195 VectorCopy(maxs, PRVM_clientedictvector(ent, absmax));
197 World_LinkEdict(&cl.world, ent, PRVM_clientedictvector(ent, absmin), PRVM_clientedictvector(ent, absmax));
200 int CL_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
202 prvm_prog_t *prog = CLVM_prog;
205 int dphitcontentsmask = (int)PRVM_clientedictfloat(passedict, dphitcontentsmask);
206 if (dphitcontentsmask)
207 return dphitcontentsmask;
208 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_SLIDEBOX)
210 if ((int)PRVM_clientedictfloat(passedict, flags) & FL_MONSTER)
211 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
213 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
215 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_CORPSE)
216 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
217 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_TRIGGER)
218 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
220 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
223 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
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)
233 prvm_prog_t *prog = CLVM_prog;
234 int i, bodysupercontents;
236 prvm_edict_t *traceowner, *touch;
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
246 // matrices to transform into/out of other entity's space
247 matrix4x4_t matrix, imatrix;
248 // model of other entity
250 // list of entities to test for collisions
252 static prvm_edict_t *touchedicts[MAX_EDICTS];
254 if (hitnetworkentity)
255 *hitnetworkentity = 0;
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]);
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)
272 if (type == MOVE_MISSILE)
274 // LordHavoc: modified this, was = -15, now -= 15
275 for (i = 0;i < 3;i++)
282 // create the bounding box of the entire move
283 for (i = 0;i < 3;i++)
285 clipboxmins[i] = clipstart[i] - 1;
286 clipboxmaxs[i] = clipstart[i] + 1;
289 // debug override to test against everything
290 if (sv_debugmove.integer)
292 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
293 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
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
299 if (prog == NULL || passedict == prog->edicts)
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;
306 // collide against network entities
307 if (hitnetworkbrushmodels)
309 for (i = 0;i < cl.num_brushmodel_entities;i++)
311 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
312 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
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);
321 // collide against player entities
322 if (hitnetworkplayers)
324 vec3_t origin, entmins, entmaxs;
325 matrix4x4_t entmatrix, entinversematrix;
327 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
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;
334 for (i = 1;i <= cl.maxclients;i++)
336 entity_render_t *ent = &cl.entities[i].render;
338 // don't hit ourselves
339 if (i == cl.playerentity)
342 // don't hit players that don't exist
343 if (!cl.scores[i-1].name[0])
346 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
348 // don't hit spectators or nonsolid players
349 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
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))
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);
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
375 if (hitcsqcentities && prog != NULL)
377 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
378 if (numtouchedicts > MAX_EDICTS)
380 // this never happens
381 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
382 numtouchedicts = MAX_EDICTS;
385 for (i = 0;i < numtouchedicts;i++)
387 touch = touchedicts[i];
389 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
391 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
396 // don't clip against self
397 if (passedict == touch)
399 // don't clip owned entities against owner
400 if (traceowner == touch)
402 // don't clip owner against owned entities
403 if (passedictprog == PRVM_clientedictedict(touch, owner))
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)))
410 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
412 // might interact, so do an exact clip
414 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
415 model = CL_GetModelFromEdict(touch);
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);
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);
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);
426 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
427 *hitnetworkentity = 0;
428 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
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)
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)
446 prvm_prog_t *prog = CLVM_prog;
447 int i, bodysupercontents;
449 prvm_edict_t *traceowner, *touch;
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;
459 // matrices to transform into/out of other entity's space
460 matrix4x4_t matrix, imatrix;
461 // model of other entity
463 // list of entities to test for collisions
465 static prvm_edict_t *touchedicts[MAX_EDICTS];
466 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
470 if (VectorCompare(start, pEnd))
471 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
473 if(collision_endposnudge.value > 0)
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);
481 VectorCopy(pEnd, end);
483 if (VectorCompare(start, end))
484 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
487 if (hitnetworkentity)
488 *hitnetworkentity = 0;
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]);
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)
506 if (type == MOVE_MISSILE)
508 // LordHavoc: modified this, was = -15, now -= 15
509 for (i = 0;i < 3;i++)
516 // create the bounding box of the entire move
517 for (i = 0;i < 3;i++)
519 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
520 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
523 // debug override to test against everything
524 if (sv_debugmove.integer)
526 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
527 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
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
533 if (prog == NULL || passedict == prog->edicts)
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;
540 // collide against network entities
541 if (hitnetworkbrushmodels)
543 for (i = 0;i < cl.num_brushmodel_entities;i++)
545 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
546 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
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);
555 // collide against player entities
556 if (hitnetworkplayers)
558 vec3_t origin, entmins, entmaxs;
559 matrix4x4_t entmatrix, entinversematrix;
561 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
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;
568 for (i = 1;i <= cl.maxclients;i++)
570 entity_render_t *ent = &cl.entities[i].render;
572 // don't hit ourselves
573 if (i == cl.playerentity)
576 // don't hit players that don't exist
577 if (!cl.scores[i-1].name[0])
580 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
582 // don't hit spectators or nonsolid players
583 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
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))
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);
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
609 if (hitcsqcentities && prog != NULL)
611 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
612 if (numtouchedicts > MAX_EDICTS)
614 // this never happens
615 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
616 numtouchedicts = MAX_EDICTS;
619 for (i = 0;i < numtouchedicts;i++)
621 touch = touchedicts[i];
623 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
625 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
630 // don't clip against self
631 if (passedict == touch)
633 // don't clip owned entities against owner
634 if (traceowner == touch)
636 // don't clip owner against owned entities
637 if (passedictprog == PRVM_clientedictedict(touch, owner))
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)))
644 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
646 // might interact, so do an exact clip
648 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
649 model = CL_GetModelFromEdict(touch);
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);
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);
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);
660 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
661 *hitnetworkentity = 0;
662 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
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);
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)
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)
684 prvm_prog_t *prog = CLVM_prog;
685 vec3_t hullmins, hullmaxs;
686 int i, bodysupercontents;
689 prvm_edict_t *traceowner, *touch;
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;
701 // matrices to transform into/out of other entity's space
702 matrix4x4_t matrix, imatrix;
703 // model of other entity
705 // list of entities to test for collisions
707 static prvm_edict_t *touchedicts[MAX_EDICTS];
708 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
712 if (VectorCompare(mins, maxs))
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);
720 trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
721 VectorSubtract(trace.endpos, mins, trace.endpos);
725 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
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);
733 VectorCopy(pEnd, end);
735 if (VectorCompare(mins, maxs))
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);
743 trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
744 VectorSubtract(trace.endpos, mins, trace.endpos);
749 if (hitnetworkentity)
750 *hitnetworkentity = 0;
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]);
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)
770 if (type == MOVE_MISSILE)
772 // LordHavoc: modified this, was = -15, now -= 15
773 for (i = 0;i < 3;i++)
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);
785 VectorCopy(clipmins, hullmins);
786 VectorCopy(clipmaxs, hullmaxs);
789 // create the bounding box of the entire move
790 for (i = 0;i < 3;i++)
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;
796 // debug override to test against everything
797 if (sv_debugmove.integer)
799 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
800 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
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
806 if (prog == NULL || passedict == prog->edicts)
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;
815 // collide against network entities
816 if (hitnetworkbrushmodels)
818 for (i = 0;i < cl.num_brushmodel_entities;i++)
820 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
821 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
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);
830 // collide against player entities
831 if (hitnetworkplayers)
833 vec3_t origin, entmins, entmaxs;
834 matrix4x4_t entmatrix, entinversematrix;
836 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
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;
843 for (i = 1;i <= cl.maxclients;i++)
845 entity_render_t *ent = &cl.entities[i].render;
847 // don't hit ourselves
848 if (i == cl.playerentity)
851 // don't hit players that don't exist
852 if (!cl.scores[i-1].name[0])
855 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
857 // don't hit spectators or nonsolid players
858 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
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))
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);
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
884 if (hitcsqcentities && prog != NULL)
886 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
887 if (numtouchedicts > MAX_EDICTS)
889 // this never happens
890 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
891 numtouchedicts = MAX_EDICTS;
894 for (i = 0;i < numtouchedicts;i++)
896 touch = touchedicts[i];
898 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
900 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
905 // don't clip against self
906 if (passedict == touch)
908 // don't clip owned entities against owner
909 if (traceowner == touch)
911 // don't clip owner against owned entities
912 if (passedictprog == PRVM_clientedictedict(touch, owner))
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)))
919 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
921 // might interact, so do an exact clip
923 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
924 model = CL_GetModelFromEdict(touch);
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);
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);
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);
935 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
936 *hitnetworkentity = 0;
937 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
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);
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)
956 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int type, int hitsupercontentsmask)
959 prvm_prog_t *prog = CLVM_prog;
963 // bounding box of entire move area
964 vec3_t clipboxmins, clipboxmaxs;
965 // start and end origin of move
966 vec3_t clipstart, clipend;
969 // matrices to transform into/out of other entity's space
970 matrix4x4_t matrix, imatrix;
971 // model of other entity
973 // list of entities to test for collisions
975 static prvm_edict_t *touchedicts[MAX_EDICTS];
976 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
980 if(collision_endposnudge.value > 0 && !VectorCompare(start, pEnd))
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);
988 VectorCopy(pEnd, end);
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]);
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)
1005 // create the bounding box of the entire move
1006 for (i = 0;i < 3;i++)
1008 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) - 1;
1009 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + 1;
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
1016 // collide against network entities
1017 for (i = 0;i < cl.num_brushmodel_entities;i++)
1019 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
1020 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
1022 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, ent->model, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask);
1023 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
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
1033 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
1034 if (numtouchedicts > MAX_EDICTS)
1036 // this never happens
1037 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
1038 numtouchedicts = MAX_EDICTS;
1041 for (i = 0;i < numtouchedicts;i++)
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);
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)
1052 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
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);
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);