3 #include "cl_collision.h"
5 float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent)
7 float maxfrac, maxrealfrac;
10 vec_t tracemins[3], tracemaxs[3];
12 vec_t tempnormal[3], starttransformed[3], endtransformed[3];
14 memset (&trace, 0 , sizeof(trace_t));
16 trace.realfraction = 1;
17 VectorCopy (end, trace.endpos);
21 if (cl.worldmodel && cl.worldmodel->TraceLine)
22 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, start, end, SUPERCONTENTS_SOLID);
25 VectorCopy(trace.plane.normal, normal);
26 maxfrac = trace.fraction;
27 maxrealfrac = trace.realfraction;
29 tracemins[0] = min(start[0], end[0]);
30 tracemaxs[0] = max(start[0], end[0]);
31 tracemins[1] = min(start[1], end[1]);
32 tracemaxs[1] = max(start[1], end[1]);
33 tracemins[2] = min(start[2], end[2]);
34 tracemaxs[2] = max(start[2], end[2]);
36 // look for embedded bmodels
37 for (n = 0;n < cl.num_entities;n++)
39 if (!cl.entities_active[n])
41 ent = &cl.entities[n].render;
42 if (!BoxesOverlap(ent->mins, ent->maxs, tracemins, tracemaxs))
44 if (!ent->model || !ent->model->TraceLine)
46 if ((ent->flags & RENDER_EXTERIORMODEL) && !chase_active.integer)
48 // if transparent and not selectable, skip entity
49 if (!(cl.entities[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST))))
53 Matrix4x4_Transform(&ent->inversematrix, start, starttransformed);
54 Matrix4x4_Transform(&ent->inversematrix, end, endtransformed);
55 Collision_ClipTrace_Box(&trace, ent->model->normalmins, ent->model->normalmaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, SUPERCONTENTS_SOLID, SUPERCONTENTS_SOLID, 0, NULL);
56 if (maxrealfrac < trace.realfraction)
59 ent->model->TraceLine(ent->model, ent->frameblend, ent->skeleton, &trace, starttransformed, endtransformed, SUPERCONTENTS_SOLID);
61 if (maxrealfrac > trace.realfraction)
65 maxfrac = trace.fraction;
66 maxrealfrac = trace.realfraction;
69 VectorCopy(trace.plane.normal, tempnormal);
70 Matrix4x4_Transform3x3(&ent->matrix, tempnormal, normal);
74 maxfrac = bound(0, maxfrac, 1);
75 //maxrealfrac = bound(0, maxrealfrac, 1);
76 //if (maxfrac < 0 || maxfrac > 1) Con_Printf("fraction out of bounds %f %s:%d\n", maxfrac, __FILE__, __LINE__);
78 VectorLerp(start, maxfrac, end, impact);
82 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius)
84 // FIXME: check multiple brush models
85 if (cl.worldmodel && cl.worldmodel->brush.FindNonSolidLocation)
86 cl.worldmodel->brush.FindNonSolidLocation(cl.worldmodel, in, out, radius);
89 dp_model_t *CL_GetModelByIndex(int modelindex)
95 modelindex = -(modelindex+1);
96 if (modelindex < MAX_MODELS)
97 return cl.csqc_model_precache[modelindex];
101 if(modelindex < MAX_MODELS)
102 return cl.model_precache[modelindex];
107 dp_model_t *CL_GetModelFromEdict(prvm_edict_t *ed)
109 prvm_prog_t *prog = CLVM_prog;
110 if (!ed || ed->priv.server->free)
112 return CL_GetModelByIndex((int)PRVM_clientedictfloat(ed, modelindex));
115 void CL_LinkEdict(prvm_edict_t *ent)
117 prvm_prog_t *prog = CLVM_prog;
120 if (ent == prog->edicts)
121 return; // don't add the world
123 if (ent->priv.server->free)
128 if (PRVM_clientedictfloat(ent, solid) == SOLID_BSP)
130 dp_model_t *model = CL_GetModelByIndex( (int)PRVM_clientedictfloat(ent, modelindex) );
133 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
135 model = CL_GetModelByIndex( 0 );
140 if (!model->TraceBox)
141 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
143 if (PRVM_clientedictvector(ent, angles)[0] || PRVM_clientedictvector(ent, angles)[2] || PRVM_clientedictvector(ent, avelocity)[0] || PRVM_clientedictvector(ent, avelocity)[2])
145 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmins, mins);
146 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmaxs, maxs);
148 else if (PRVM_clientedictvector(ent, angles)[1] || PRVM_clientedictvector(ent, avelocity)[1])
150 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmins, mins);
151 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmaxs, maxs);
155 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmins, mins);
156 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmaxs, maxs);
161 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
162 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
163 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
168 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
169 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
172 VectorCopy(mins, PRVM_clientedictvector(ent, absmin));
173 VectorCopy(maxs, PRVM_clientedictvector(ent, absmax));
175 World_LinkEdict(&cl.world, ent, mins, maxs);
178 int CL_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
180 prvm_prog_t *prog = CLVM_prog;
183 int dphitcontentsmask = (int)PRVM_clientedictfloat(passedict, dphitcontentsmask);
184 if (dphitcontentsmask)
185 return dphitcontentsmask;
186 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_SLIDEBOX)
188 if ((int)PRVM_clientedictfloat(passedict, flags) & FL_MONSTER)
189 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
191 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
193 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_CORPSE)
194 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
195 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_TRIGGER)
196 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
198 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
201 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
209 trace_t CL_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
211 prvm_prog_t *prog = CLVM_prog;
212 int i, bodysupercontents;
214 prvm_edict_t *traceowner, *touch;
216 // temporary storage because prvm_vec_t may need conversion
217 vec3_t touchmins, touchmaxs;
218 // bounding box of entire move area
219 vec3_t clipboxmins, clipboxmaxs;
220 // size when clipping against monsters
221 vec3_t clipmins2, clipmaxs2;
222 // start and end origin of move
226 // matrices to transform into/out of other entity's space
227 matrix4x4_t matrix, imatrix;
228 // model of other entity
230 // list of entities to test for collisions
232 static prvm_edict_t *touchedicts[MAX_EDICTS];
234 if (hitnetworkentity)
235 *hitnetworkentity = 0;
237 VectorCopy(start, clipstart);
238 VectorClear(clipmins2);
239 VectorClear(clipmaxs2);
240 #if COLLISIONPARANOID >= 3
241 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
245 Collision_ClipPointToWorld(&cliptrace, cl.worldmodel, clipstart, hitsupercontentsmask);
246 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
247 if (cliptrace.startsolid || cliptrace.fraction < 1)
248 cliptrace.ent = prog ? prog->edicts : NULL;
249 if (type == MOVE_WORLDONLY)
252 if (type == MOVE_MISSILE)
254 // LordHavoc: modified this, was = -15, now -= 15
255 for (i = 0;i < 3;i++)
262 // create the bounding box of the entire move
263 for (i = 0;i < 3;i++)
265 clipboxmins[i] = clipstart[i] - 1;
266 clipboxmaxs[i] = clipstart[i] + 1;
269 // debug override to test against everything
270 if (sv_debugmove.integer)
272 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
273 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
276 // if the passedict is world, make it NULL (to avoid two checks each time)
277 // this checks prog because this function is often called without a CSQC
279 if (prog == NULL || passedict == prog->edicts)
281 // precalculate prog value for passedict for comparisons
282 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
283 // precalculate passedict's owner edict pointer for comparisons
284 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
286 // collide against network entities
287 if (hitnetworkbrushmodels)
289 for (i = 0;i < cl.num_brushmodel_entities;i++)
291 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
292 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
294 Collision_ClipPointToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, hitsupercontentsmask);
295 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
296 *hitnetworkentity = cl.brushmodel_entities[i];
297 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
301 // collide against player entities
302 if (hitnetworkplayers)
304 vec3_t origin, entmins, entmaxs;
305 matrix4x4_t entmatrix, entinversematrix;
307 if(IS_OLDNEXUIZ_DERIVED(gamemode))
309 // don't hit network players, if we are a nonsolid player
310 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
311 goto skipnetworkplayers;
314 for (i = 1;i <= cl.maxclients;i++)
316 entity_render_t *ent = &cl.entities[i].render;
318 // don't hit ourselves
319 if (i == cl.playerentity)
322 // don't hit players that don't exist
323 if (!cl.scores[i-1].name[0])
326 if(IS_OLDNEXUIZ_DERIVED(gamemode))
328 // don't hit spectators or nonsolid players
329 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
333 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
334 VectorAdd(origin, cl.playerstandmins, entmins);
335 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
336 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
338 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
339 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
340 Collision_ClipPointToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask);
341 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
342 *hitnetworkentity = i;
343 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
351 // because this uses World_EntitiestoBox, we know all entity boxes overlap
352 // the clip region, so we can skip culling checks in the loop below
353 // note: if prog is NULL then there won't be any linked entities
355 if (hitcsqcentities && prog != NULL)
357 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
358 if (numtouchedicts > MAX_EDICTS)
360 // this never happens
361 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
362 numtouchedicts = MAX_EDICTS;
365 for (i = 0;i < numtouchedicts;i++)
367 touch = touchedicts[i];
369 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
371 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
376 // don't clip against self
377 if (passedict == touch)
379 // don't clip owned entities against owner
380 if (traceowner == touch)
382 // don't clip owner against owned entities
383 if (passedictprog == PRVM_clientedictedict(touch, owner))
385 // don't clip points against points (they can't collide)
386 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
390 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
392 // might interact, so do an exact clip
394 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
395 model = CL_GetModelFromEdict(touch);
397 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);
399 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
400 Matrix4x4_Invert_Simple(&imatrix, &matrix);
401 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
402 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
403 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
404 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, 0.0f);
406 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
408 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
409 *hitnetworkentity = 0;
410 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
422 trace_t CL_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, float extend, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities, qboolean hitsurfaces)
424 prvm_prog_t *prog = CLVM_prog;
425 int i, bodysupercontents;
427 prvm_edict_t *traceowner, *touch;
429 // temporary storage because prvm_vec_t may need conversion
430 vec3_t touchmins, touchmaxs;
431 // bounding box of entire move area
432 vec3_t clipboxmins, clipboxmaxs;
433 // size when clipping against monsters
434 vec3_t clipmins2, clipmaxs2;
435 // start and end origin of move
436 vec3_t clipstart, clipend;
439 // matrices to transform into/out of other entity's space
440 matrix4x4_t matrix, imatrix;
441 // model of other entity
443 // list of entities to test for collisions
445 static prvm_edict_t *touchedicts[MAX_EDICTS];
446 if (VectorCompare(start, end))
447 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
449 if (hitnetworkentity)
450 *hitnetworkentity = 0;
452 VectorCopy(start, clipstart);
453 VectorCopy(end, clipend);
454 VectorClear(clipmins2);
455 VectorClear(clipmaxs2);
456 #if COLLISIONPARANOID >= 3
457 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
461 Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, extend, hitsurfaces);
462 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
463 if (cliptrace.startsolid || cliptrace.fraction < 1)
464 cliptrace.ent = prog ? prog->edicts : NULL;
465 if (type == MOVE_WORLDONLY)
468 if (type == MOVE_MISSILE)
470 // LordHavoc: modified this, was = -15, now -= 15
471 for (i = 0;i < 3;i++)
478 // create the bounding box of the entire move
479 for (i = 0;i < 3;i++)
481 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
482 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
485 // debug override to test against everything
486 if (sv_debugmove.integer)
488 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
489 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
492 // if the passedict is world, make it NULL (to avoid two checks each time)
493 // this checks prog because this function is often called without a CSQC
495 if (prog == NULL || passedict == prog->edicts)
497 // precalculate prog value for passedict for comparisons
498 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
499 // precalculate passedict's owner edict pointer for comparisons
500 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
502 // collide against network entities
503 if (hitnetworkbrushmodels)
505 for (i = 0;i < cl.num_brushmodel_entities;i++)
507 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
508 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
510 Collision_ClipLineToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, extend, hitsurfaces);
511 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
512 *hitnetworkentity = cl.brushmodel_entities[i];
513 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
517 // collide against player entities
518 if (hitnetworkplayers)
520 vec3_t origin, entmins, entmaxs;
521 matrix4x4_t entmatrix, entinversematrix;
523 if(IS_OLDNEXUIZ_DERIVED(gamemode))
525 // don't hit network players, if we are a nonsolid player
526 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
527 goto skipnetworkplayers;
530 for (i = 1;i <= cl.maxclients;i++)
532 entity_render_t *ent = &cl.entities[i].render;
534 // don't hit ourselves
535 if (i == cl.playerentity)
538 // don't hit players that don't exist
539 if (!cl.scores[i-1].name[0])
542 if(IS_OLDNEXUIZ_DERIVED(gamemode))
544 // don't hit spectators or nonsolid players
545 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
549 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
550 VectorAdd(origin, cl.playerstandmins, entmins);
551 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
552 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
554 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
555 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
556 Collision_ClipLineToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask, extend, hitsurfaces);
557 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
558 *hitnetworkentity = i;
559 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
567 // because this uses World_EntitiestoBox, we know all entity boxes overlap
568 // the clip region, so we can skip culling checks in the loop below
569 // note: if prog is NULL then there won't be any linked entities
571 if (hitcsqcentities && prog != NULL)
573 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
574 if (numtouchedicts > MAX_EDICTS)
576 // this never happens
577 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
578 numtouchedicts = MAX_EDICTS;
581 for (i = 0;i < numtouchedicts;i++)
583 touch = touchedicts[i];
585 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
587 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
592 // don't clip against self
593 if (passedict == touch)
595 // don't clip owned entities against owner
596 if (traceowner == touch)
598 // don't clip owner against owned entities
599 if (passedictprog == PRVM_clientedictedict(touch, owner))
601 // don't clip points against points (they can't collide)
602 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
606 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
608 // might interact, so do an exact clip
610 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
611 model = CL_GetModelFromEdict(touch);
613 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
615 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
616 Matrix4x4_Invert_Simple(&imatrix, &matrix);
617 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
618 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
619 if (type == MOVE_MISSILE && (int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
620 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, extend);
622 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, extend, hitsurfaces);
624 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
625 *hitnetworkentity = 0;
626 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
638 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, float extend, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
640 prvm_prog_t *prog = CLVM_prog;
641 vec3_t hullmins, hullmaxs;
642 int i, bodysupercontents;
645 prvm_edict_t *traceowner, *touch;
647 // temporary storage because prvm_vec_t may need conversion
648 vec3_t touchmins, touchmaxs;
649 // bounding box of entire move area
650 vec3_t clipboxmins, clipboxmaxs;
651 // size of the moving object
652 vec3_t clipmins, clipmaxs;
653 // size when clipping against monsters
654 vec3_t clipmins2, clipmaxs2;
655 // start and end origin of move
656 vec3_t clipstart, clipend;
659 // matrices to transform into/out of other entity's space
660 matrix4x4_t matrix, imatrix;
661 // model of other entity
663 // list of entities to test for collisions
665 static prvm_edict_t *touchedicts[MAX_EDICTS];
666 if (VectorCompare(mins, maxs))
668 vec3_t shiftstart, shiftend;
669 VectorAdd(start, mins, shiftstart);
670 VectorAdd(end, mins, shiftend);
671 if (VectorCompare(start, end))
672 trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
674 trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, extend, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
675 VectorSubtract(trace.endpos, mins, trace.endpos);
679 if (hitnetworkentity)
680 *hitnetworkentity = 0;
682 VectorCopy(start, clipstart);
683 VectorCopy(end, clipend);
684 VectorCopy(mins, clipmins);
685 VectorCopy(maxs, clipmaxs);
686 VectorCopy(mins, clipmins2);
687 VectorCopy(maxs, clipmaxs2);
688 #if COLLISIONPARANOID >= 3
689 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
693 Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, extend);
694 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
695 if (cliptrace.startsolid || cliptrace.fraction < 1)
696 cliptrace.ent = prog ? prog->edicts : NULL;
697 if (type == MOVE_WORLDONLY)
700 if (type == MOVE_MISSILE)
702 // LordHavoc: modified this, was = -15, now -= 15
703 for (i = 0;i < 3;i++)
710 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
711 if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
712 cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
715 VectorCopy(clipmins, hullmins);
716 VectorCopy(clipmaxs, hullmaxs);
719 // create the bounding box of the entire move
720 for (i = 0;i < 3;i++)
722 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
723 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
726 // debug override to test against everything
727 if (sv_debugmove.integer)
729 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
730 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
733 // if the passedict is world, make it NULL (to avoid two checks each time)
734 // this checks prog because this function is often called without a CSQC
736 if (prog == NULL || passedict == prog->edicts)
738 // precalculate prog value for passedict for comparisons
739 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
740 // figure out whether this is a point trace for comparisons
741 pointtrace = VectorCompare(clipmins, clipmaxs);
742 // precalculate passedict's owner edict pointer for comparisons
743 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
745 // collide against network entities
746 if (hitnetworkbrushmodels)
748 for (i = 0;i < cl.num_brushmodel_entities;i++)
750 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
751 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
753 Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask, extend);
754 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
755 *hitnetworkentity = cl.brushmodel_entities[i];
756 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
760 // collide against player entities
761 if (hitnetworkplayers)
763 vec3_t origin, entmins, entmaxs;
764 matrix4x4_t entmatrix, entinversematrix;
766 if(IS_OLDNEXUIZ_DERIVED(gamemode))
768 // don't hit network players, if we are a nonsolid player
769 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
770 goto skipnetworkplayers;
773 for (i = 1;i <= cl.maxclients;i++)
775 entity_render_t *ent = &cl.entities[i].render;
777 // don't hit ourselves
778 if (i == cl.playerentity)
781 // don't hit players that don't exist
782 if (!cl.scores[i-1].name[0])
785 if(IS_OLDNEXUIZ_DERIVED(gamemode))
787 // don't hit spectators or nonsolid players
788 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
792 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
793 VectorAdd(origin, cl.playerstandmins, entmins);
794 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
795 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
797 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
798 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
799 Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask, extend);
800 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
801 *hitnetworkentity = i;
802 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
810 // because this uses World_EntitiestoBox, we know all entity boxes overlap
811 // the clip region, so we can skip culling checks in the loop below
812 // note: if prog is NULL then there won't be any linked entities
814 if (hitcsqcentities && prog != NULL)
816 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
817 if (numtouchedicts > MAX_EDICTS)
819 // this never happens
820 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
821 numtouchedicts = MAX_EDICTS;
824 for (i = 0;i < numtouchedicts;i++)
826 touch = touchedicts[i];
828 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
830 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
835 // don't clip against self
836 if (passedict == touch)
838 // don't clip owned entities against owner
839 if (traceowner == touch)
841 // don't clip owner against owned entities
842 if (passedictprog == PRVM_clientedictedict(touch, owner))
844 // don't clip points against points (they can't collide)
845 if (pointtrace && VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
849 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
851 // might interact, so do an exact clip
853 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
854 model = CL_GetModelFromEdict(touch);
856 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);
858 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
859 Matrix4x4_Invert_Simple(&imatrix, &matrix);
860 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
861 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
862 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
863 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, extend);
865 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, extend);
867 if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
868 *hitnetworkentity = 0;
869 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
881 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int type, int hitsupercontentsmask)
883 prvm_prog_t *prog = CLVM_prog;
887 // bounding box of entire move area
888 vec3_t clipboxmins, clipboxmaxs;
889 // start and end origin of move
890 vec3_t clipstart, clipend;
893 // matrices to transform into/out of other entity's space
894 matrix4x4_t matrix, imatrix;
895 // model of other entity
897 // list of entities to test for collisions
899 static prvm_edict_t *touchedicts[MAX_EDICTS];
901 VectorCopy(start, clipstart);
902 VectorCopy(end, clipend);
903 #if COLLISIONPARANOID >= 3
904 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
908 Collision_Cache_ClipLineToWorldSurfaces(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask);
909 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
910 if (cliptrace.startsolid || cliptrace.fraction < 1)
911 cliptrace.ent = prog ? prog->edicts : NULL;
912 if (type == MOVE_WORLDONLY)
915 // create the bounding box of the entire move
916 for (i = 0;i < 3;i++)
918 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) - 1;
919 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + 1;
922 // if the passedict is world, make it NULL (to avoid two checks each time)
923 // this checks prog because this function is often called without a CSQC
926 // collide against network entities
927 for (i = 0;i < cl.num_brushmodel_entities;i++)
929 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
930 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
932 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, ent->model, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask);
933 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
937 // because this uses World_EntitiestoBox, we know all entity boxes overlap
938 // the clip region, so we can skip culling checks in the loop below
939 // note: if prog is NULL then there won't be any linked entities
943 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
944 if (numtouchedicts > MAX_EDICTS)
946 // this never happens
947 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
948 numtouchedicts = MAX_EDICTS;
951 for (i = 0;i < numtouchedicts;i++)
953 touch = touchedicts[i];
954 // might interact, so do an exact clip
955 // only hit entity models, not collision shapes
956 model = CL_GetModelFromEdict(touch);
959 // animated models are too slow to collide against and can't be cached
960 if (touch->priv.server->frameblend || touch->priv.server->skeleton.relativetransforms)
962 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
964 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);
965 Matrix4x4_Invert_Simple(&imatrix, &matrix);
966 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, model, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
967 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);