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)
10 vec_t tracemins[3], tracemaxs[3];
12 vec_t tempnormal[3], starttransformed[3], endtransformed[3];
14 memset (&trace, 0 , sizeof(trace_t));
16 VectorCopy (end, trace.endpos);
20 if (cl.worldmodel && cl.worldmodel->TraceLine)
21 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, start, end, SUPERCONTENTS_SOLID);
24 VectorCopy(trace.plane.normal, normal);
25 maxfrac = trace.fraction;
27 tracemins[0] = min(start[0], end[0]);
28 tracemaxs[0] = max(start[0], end[0]);
29 tracemins[1] = min(start[1], end[1]);
30 tracemaxs[1] = max(start[1], end[1]);
31 tracemins[2] = min(start[2], end[2]);
32 tracemaxs[2] = max(start[2], end[2]);
34 // look for embedded bmodels
35 for (n = 0;n < cl.num_entities;n++)
37 if (!cl.entities_active[n])
39 ent = &cl.entities[n].render;
40 if (!BoxesOverlap(ent->mins, ent->maxs, tracemins, tracemaxs))
42 if (!ent->model || !ent->model->TraceLine)
44 if ((ent->flags & RENDER_EXTERIORMODEL) && !chase_active.integer)
46 // if transparent and not selectable, skip entity
47 if (!(cl.entities[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST))))
51 Matrix4x4_Transform(&ent->inversematrix, start, starttransformed);
52 Matrix4x4_Transform(&ent->inversematrix, end, endtransformed);
53 Collision_ClipTrace_Box(&trace, ent->model->normalmins, ent->model->normalmaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, SUPERCONTENTS_SOLID, SUPERCONTENTS_SOLID, 0, NULL);
54 if (maxfrac < trace.fraction)
57 ent->model->TraceLine(ent->model, ent->frameblend, ent->skeleton, &trace, starttransformed, endtransformed, SUPERCONTENTS_SOLID);
59 if (maxfrac > trace.fraction)
63 maxfrac = trace.fraction;
66 VectorCopy(trace.plane.normal, tempnormal);
67 Matrix4x4_Transform3x3(&ent->matrix, tempnormal, normal);
71 maxfrac = bound(0, maxfrac, 1);
72 //maxrealfrac = bound(0, maxrealfrac, 1);
73 //if (maxfrac < 0 || maxfrac > 1) Con_Printf("fraction out of bounds %f %s:%d\n", maxfrac, __FILE__, __LINE__);
75 VectorLerp(start, maxfrac, end, impact);
79 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius)
81 // FIXME: check multiple brush models
82 if (cl.worldmodel && cl.worldmodel->brush.FindNonSolidLocation)
83 cl.worldmodel->brush.FindNonSolidLocation(cl.worldmodel, in, out, radius);
86 dp_model_t *CL_GetModelByIndex(int modelindex)
92 modelindex = -(modelindex+1);
93 if (modelindex < MAX_MODELS)
94 return cl.csqc_model_precache[modelindex];
98 if(modelindex < MAX_MODELS)
99 return cl.model_precache[modelindex];
104 dp_model_t *CL_GetModelFromEdict(prvm_edict_t *ed)
106 prvm_prog_t *prog = CLVM_prog;
107 if (!ed || ed->priv.server->free)
109 return CL_GetModelByIndex((int)PRVM_clientedictfloat(ed, modelindex));
112 void CL_LinkEdict(prvm_edict_t *ent)
114 prvm_prog_t *prog = CLVM_prog;
117 if (ent == prog->edicts)
118 return; // don't add the world
120 if (ent->priv.server->free)
125 if (PRVM_clientedictfloat(ent, solid) == SOLID_BSP)
127 dp_model_t *model = CL_GetModelByIndex( (int)PRVM_clientedictfloat(ent, modelindex) );
130 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
132 model = CL_GetModelByIndex( 0 );
137 if (!model->TraceBox)
138 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
140 if (PRVM_clientedictvector(ent, angles)[0] || PRVM_clientedictvector(ent, angles)[2] || PRVM_clientedictvector(ent, avelocity)[0] || PRVM_clientedictvector(ent, avelocity)[2])
142 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmins, mins);
143 VectorAdd(PRVM_clientedictvector(ent, origin), model->rotatedmaxs, maxs);
145 else if (PRVM_clientedictvector(ent, angles)[1] || PRVM_clientedictvector(ent, avelocity)[1])
147 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmins, mins);
148 VectorAdd(PRVM_clientedictvector(ent, origin), model->yawmaxs, maxs);
152 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmins, mins);
153 VectorAdd(PRVM_clientedictvector(ent, origin), model->normalmaxs, maxs);
158 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
159 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
160 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
165 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
166 VectorAdd(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
169 VectorCopy(mins, PRVM_clientedictvector(ent, absmin));
170 VectorCopy(maxs, PRVM_clientedictvector(ent, absmax));
172 World_LinkEdict(&cl.world, ent, mins, maxs);
175 int CL_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
177 prvm_prog_t *prog = CLVM_prog;
180 int dphitcontentsmask = (int)PRVM_clientedictfloat(passedict, dphitcontentsmask);
181 if (dphitcontentsmask)
182 return dphitcontentsmask;
183 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_SLIDEBOX)
185 if ((int)PRVM_clientedictfloat(passedict, flags) & FL_MONSTER)
186 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
188 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
190 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_CORPSE)
191 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
192 else if (PRVM_clientedictfloat(passedict, solid) == SOLID_TRIGGER)
193 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
195 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
198 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
206 trace_t CL_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
208 prvm_prog_t *prog = CLVM_prog;
209 int i, bodysupercontents;
211 prvm_edict_t *traceowner, *touch;
213 // temporary storage because prvm_vec_t may need conversion
214 vec3_t touchmins, touchmaxs;
215 // bounding box of entire move area
216 vec3_t clipboxmins, clipboxmaxs;
217 // size when clipping against monsters
218 vec3_t clipmins2, clipmaxs2;
219 // start and end origin of move
223 // matrices to transform into/out of other entity's space
224 matrix4x4_t matrix, imatrix;
225 // model of other entity
227 // list of entities to test for collisions
229 static prvm_edict_t *touchedicts[MAX_EDICTS];
231 if (hitnetworkentity)
232 *hitnetworkentity = 0;
234 VectorCopy(start, clipstart);
235 VectorClear(clipmins2);
236 VectorClear(clipmaxs2);
237 #if COLLISIONPARANOID >= 3
238 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
242 Collision_ClipPointToWorld(&cliptrace, cl.worldmodel, clipstart, hitsupercontentsmask);
243 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
244 if (cliptrace.startsolid || cliptrace.fraction < 1)
245 cliptrace.ent = prog ? prog->edicts : NULL;
246 if (type == MOVE_WORLDONLY)
249 if (type == MOVE_MISSILE)
251 // LordHavoc: modified this, was = -15, now -= 15
252 for (i = 0;i < 3;i++)
259 // create the bounding box of the entire move
260 for (i = 0;i < 3;i++)
262 clipboxmins[i] = clipstart[i] - 1;
263 clipboxmaxs[i] = clipstart[i] + 1;
266 // debug override to test against everything
267 if (sv_debugmove.integer)
269 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
270 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
273 // if the passedict is world, make it NULL (to avoid two checks each time)
274 // this checks prog because this function is often called without a CSQC
276 if (prog == NULL || passedict == prog->edicts)
278 // precalculate prog value for passedict for comparisons
279 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
280 // precalculate passedict's owner edict pointer for comparisons
281 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
283 // collide against network entities
284 if (hitnetworkbrushmodels)
286 for (i = 0;i < cl.num_brushmodel_entities;i++)
288 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
289 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
291 Collision_ClipPointToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, hitsupercontentsmask);
292 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
293 *hitnetworkentity = cl.brushmodel_entities[i];
294 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
298 // collide against player entities
299 if (hitnetworkplayers)
301 vec3_t origin, entmins, entmaxs;
302 matrix4x4_t entmatrix, entinversematrix;
304 if(IS_OLDNEXUIZ_DERIVED(gamemode))
306 // don't hit network players, if we are a nonsolid player
307 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
308 goto skipnetworkplayers;
311 for (i = 1;i <= cl.maxclients;i++)
313 entity_render_t *ent = &cl.entities[i].render;
315 // don't hit ourselves
316 if (i == cl.playerentity)
319 // don't hit players that don't exist
320 if (!cl.scores[i-1].name[0])
323 if(IS_OLDNEXUIZ_DERIVED(gamemode))
325 // don't hit spectators or nonsolid players
326 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
330 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
331 VectorAdd(origin, cl.playerstandmins, entmins);
332 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
333 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
335 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
336 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
337 Collision_ClipPointToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask);
338 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
339 *hitnetworkentity = i;
340 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
348 // because this uses World_EntitiestoBox, we know all entity boxes overlap
349 // the clip region, so we can skip culling checks in the loop below
350 // note: if prog is NULL then there won't be any linked entities
352 if (hitcsqcentities && prog != NULL)
354 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
355 if (numtouchedicts > MAX_EDICTS)
357 // this never happens
358 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
359 numtouchedicts = MAX_EDICTS;
362 for (i = 0;i < numtouchedicts;i++)
364 touch = touchedicts[i];
366 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
368 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
373 // don't clip against self
374 if (passedict == touch)
376 // don't clip owned entities against owner
377 if (traceowner == touch)
379 // don't clip owner against owned entities
380 if (passedictprog == PRVM_clientedictedict(touch, owner))
382 // don't clip points against points (they can't collide)
383 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
387 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
389 // might interact, so do an exact clip
391 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
392 model = CL_GetModelFromEdict(touch);
394 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);
396 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
397 Matrix4x4_Invert_Simple(&imatrix, &matrix);
398 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
399 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
400 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
401 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, 0.0f);
403 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
405 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
406 *hitnetworkentity = 0;
407 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
419 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)
421 prvm_prog_t *prog = CLVM_prog;
422 int i, bodysupercontents;
424 prvm_edict_t *traceowner, *touch;
426 // temporary storage because prvm_vec_t may need conversion
427 vec3_t touchmins, touchmaxs;
428 // bounding box of entire move area
429 vec3_t clipboxmins, clipboxmaxs;
430 // size when clipping against monsters
431 vec3_t clipmins2, clipmaxs2;
432 // start and end origin of move
433 vec3_t clipstart, clipend;
436 // matrices to transform into/out of other entity's space
437 matrix4x4_t matrix, imatrix;
438 // model of other entity
440 // list of entities to test for collisions
442 static prvm_edict_t *touchedicts[MAX_EDICTS];
443 if (VectorCompare(start, end))
444 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
446 if (hitnetworkentity)
447 *hitnetworkentity = 0;
449 VectorCopy(start, clipstart);
450 VectorCopy(end, clipend);
451 VectorClear(clipmins2);
452 VectorClear(clipmaxs2);
453 #if COLLISIONPARANOID >= 3
454 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
458 Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, extend, hitsurfaces);
459 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
460 if (cliptrace.startsolid || cliptrace.fraction < 1)
461 cliptrace.ent = prog ? prog->edicts : NULL;
462 if (type == MOVE_WORLDONLY)
465 if (type == MOVE_MISSILE)
467 // LordHavoc: modified this, was = -15, now -= 15
468 for (i = 0;i < 3;i++)
475 // create the bounding box of the entire move
476 for (i = 0;i < 3;i++)
478 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
479 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
482 // debug override to test against everything
483 if (sv_debugmove.integer)
485 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
486 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
489 // if the passedict is world, make it NULL (to avoid two checks each time)
490 // this checks prog because this function is often called without a CSQC
492 if (prog == NULL || passedict == prog->edicts)
494 // precalculate prog value for passedict for comparisons
495 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
496 // precalculate passedict's owner edict pointer for comparisons
497 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
499 // collide against network entities
500 if (hitnetworkbrushmodels)
502 for (i = 0;i < cl.num_brushmodel_entities;i++)
504 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
505 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
507 Collision_ClipLineToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, extend, hitsurfaces);
508 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
509 *hitnetworkentity = cl.brushmodel_entities[i];
510 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
514 // collide against player entities
515 if (hitnetworkplayers)
517 vec3_t origin, entmins, entmaxs;
518 matrix4x4_t entmatrix, entinversematrix;
520 if(IS_OLDNEXUIZ_DERIVED(gamemode))
522 // don't hit network players, if we are a nonsolid player
523 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
524 goto skipnetworkplayers;
527 for (i = 1;i <= cl.maxclients;i++)
529 entity_render_t *ent = &cl.entities[i].render;
531 // don't hit ourselves
532 if (i == cl.playerentity)
535 // don't hit players that don't exist
536 if (!cl.scores[i-1].name[0])
539 if(IS_OLDNEXUIZ_DERIVED(gamemode))
541 // don't hit spectators or nonsolid players
542 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
546 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
547 VectorAdd(origin, cl.playerstandmins, entmins);
548 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
549 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
551 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
552 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
553 Collision_ClipLineToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask, extend, hitsurfaces);
554 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
555 *hitnetworkentity = i;
556 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
564 // because this uses World_EntitiestoBox, we know all entity boxes overlap
565 // the clip region, so we can skip culling checks in the loop below
566 // note: if prog is NULL then there won't be any linked entities
568 if (hitcsqcentities && prog != NULL)
570 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
571 if (numtouchedicts > MAX_EDICTS)
573 // this never happens
574 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
575 numtouchedicts = MAX_EDICTS;
578 for (i = 0;i < numtouchedicts;i++)
580 touch = touchedicts[i];
582 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
584 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
589 // don't clip against self
590 if (passedict == touch)
592 // don't clip owned entities against owner
593 if (traceowner == touch)
595 // don't clip owner against owned entities
596 if (passedictprog == PRVM_clientedictedict(touch, owner))
598 // don't clip points against points (they can't collide)
599 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
603 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
605 // might interact, so do an exact clip
607 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
608 model = CL_GetModelFromEdict(touch);
610 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);
612 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
613 Matrix4x4_Invert_Simple(&imatrix, &matrix);
614 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
615 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
616 if (type == MOVE_MISSILE && (int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
617 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, extend);
619 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, extend, hitsurfaces);
621 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
622 *hitnetworkentity = 0;
623 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
635 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)
637 prvm_prog_t *prog = CLVM_prog;
638 vec3_t hullmins, hullmaxs;
639 int i, bodysupercontents;
642 prvm_edict_t *traceowner, *touch;
644 // temporary storage because prvm_vec_t may need conversion
645 vec3_t touchmins, touchmaxs;
646 // bounding box of entire move area
647 vec3_t clipboxmins, clipboxmaxs;
648 // size of the moving object
649 vec3_t clipmins, clipmaxs;
650 // size when clipping against monsters
651 vec3_t clipmins2, clipmaxs2;
652 // start and end origin of move
653 vec3_t clipstart, clipend;
656 // matrices to transform into/out of other entity's space
657 matrix4x4_t matrix, imatrix;
658 // model of other entity
660 // list of entities to test for collisions
662 static prvm_edict_t *touchedicts[MAX_EDICTS];
663 if (VectorCompare(mins, maxs))
665 vec3_t shiftstart, shiftend;
666 VectorAdd(start, mins, shiftstart);
667 VectorAdd(end, mins, shiftend);
668 if (VectorCompare(start, end))
669 trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
671 trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, extend, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
672 VectorSubtract(trace.endpos, mins, trace.endpos);
676 if (hitnetworkentity)
677 *hitnetworkentity = 0;
679 VectorCopy(start, clipstart);
680 VectorCopy(end, clipend);
681 VectorCopy(mins, clipmins);
682 VectorCopy(maxs, clipmaxs);
683 VectorCopy(mins, clipmins2);
684 VectorCopy(maxs, clipmaxs2);
685 #if COLLISIONPARANOID >= 3
686 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
690 Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, extend);
691 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
692 if (cliptrace.startsolid || cliptrace.fraction < 1)
693 cliptrace.ent = prog ? prog->edicts : NULL;
694 if (type == MOVE_WORLDONLY)
697 if (type == MOVE_MISSILE)
699 // LordHavoc: modified this, was = -15, now -= 15
700 for (i = 0;i < 3;i++)
707 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
708 if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
709 cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
712 VectorCopy(clipmins, hullmins);
713 VectorCopy(clipmaxs, hullmaxs);
716 // create the bounding box of the entire move
717 for (i = 0;i < 3;i++)
719 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
720 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
723 // debug override to test against everything
724 if (sv_debugmove.integer)
726 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
727 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
730 // if the passedict is world, make it NULL (to avoid two checks each time)
731 // this checks prog because this function is often called without a CSQC
733 if (prog == NULL || passedict == prog->edicts)
735 // precalculate prog value for passedict for comparisons
736 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
737 // figure out whether this is a point trace for comparisons
738 pointtrace = VectorCompare(clipmins, clipmaxs);
739 // precalculate passedict's owner edict pointer for comparisons
740 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
742 // collide against network entities
743 if (hitnetworkbrushmodels)
745 for (i = 0;i < cl.num_brushmodel_entities;i++)
747 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
748 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
750 Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask, extend);
751 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
752 *hitnetworkentity = cl.brushmodel_entities[i];
753 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
757 // collide against player entities
758 if (hitnetworkplayers)
760 vec3_t origin, entmins, entmaxs;
761 matrix4x4_t entmatrix, entinversematrix;
763 if(IS_OLDNEXUIZ_DERIVED(gamemode))
765 // don't hit network players, if we are a nonsolid player
766 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
767 goto skipnetworkplayers;
770 for (i = 1;i <= cl.maxclients;i++)
772 entity_render_t *ent = &cl.entities[i].render;
774 // don't hit ourselves
775 if (i == cl.playerentity)
778 // don't hit players that don't exist
779 if (!cl.scores[i-1].name[0])
782 if(IS_OLDNEXUIZ_DERIVED(gamemode))
784 // don't hit spectators or nonsolid players
785 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
789 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
790 VectorAdd(origin, cl.playerstandmins, entmins);
791 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
792 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
794 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
795 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
796 Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask, extend);
797 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
798 *hitnetworkentity = i;
799 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
807 // because this uses World_EntitiestoBox, we know all entity boxes overlap
808 // the clip region, so we can skip culling checks in the loop below
809 // note: if prog is NULL then there won't be any linked entities
811 if (hitcsqcentities && prog != NULL)
813 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
814 if (numtouchedicts > MAX_EDICTS)
816 // this never happens
817 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
818 numtouchedicts = MAX_EDICTS;
821 for (i = 0;i < numtouchedicts;i++)
823 touch = touchedicts[i];
825 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
827 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
832 // don't clip against self
833 if (passedict == touch)
835 // don't clip owned entities against owner
836 if (traceowner == touch)
838 // don't clip owner against owned entities
839 if (passedictprog == PRVM_clientedictedict(touch, owner))
841 // don't clip points against points (they can't collide)
842 if (pointtrace && VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
846 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
848 // might interact, so do an exact clip
850 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
851 model = CL_GetModelFromEdict(touch);
853 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);
855 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
856 Matrix4x4_Invert_Simple(&imatrix, &matrix);
857 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
858 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
859 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
860 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, extend);
862 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, extend);
864 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
865 *hitnetworkentity = 0;
866 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
878 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int type, int hitsupercontentsmask)
880 prvm_prog_t *prog = CLVM_prog;
884 // bounding box of entire move area
885 vec3_t clipboxmins, clipboxmaxs;
886 // start and end origin of move
887 vec3_t clipstart, clipend;
890 // matrices to transform into/out of other entity's space
891 matrix4x4_t matrix, imatrix;
892 // model of other entity
894 // list of entities to test for collisions
896 static prvm_edict_t *touchedicts[MAX_EDICTS];
898 VectorCopy(start, clipstart);
899 VectorCopy(end, clipend);
900 #if COLLISIONPARANOID >= 3
901 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
905 Collision_Cache_ClipLineToWorldSurfaces(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask);
906 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
907 if (cliptrace.startsolid || cliptrace.fraction < 1)
908 cliptrace.ent = prog ? prog->edicts : NULL;
909 if (type == MOVE_WORLDONLY)
912 // create the bounding box of the entire move
913 for (i = 0;i < 3;i++)
915 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) - 1;
916 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + 1;
919 // if the passedict is world, make it NULL (to avoid two checks each time)
920 // this checks prog because this function is often called without a CSQC
923 // collide against network entities
924 for (i = 0;i < cl.num_brushmodel_entities;i++)
926 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
927 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
929 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, ent->model, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask);
930 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
934 // because this uses World_EntitiestoBox, we know all entity boxes overlap
935 // the clip region, so we can skip culling checks in the loop below
936 // note: if prog is NULL then there won't be any linked entities
940 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
941 if (numtouchedicts > MAX_EDICTS)
943 // this never happens
944 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
945 numtouchedicts = MAX_EDICTS;
948 for (i = 0;i < numtouchedicts;i++)
950 touch = touchedicts[i];
951 // might interact, so do an exact clip
952 // only hit entity models, not collision shapes
953 model = CL_GetModelFromEdict(touch);
956 // animated models are too slow to collide against and can't be cached
957 if (touch->priv.server->frameblend || touch->priv.server->skeleton.relativetransforms)
959 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
961 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);
962 Matrix4x4_Invert_Simple(&imatrix, &matrix);
963 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, model, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
964 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);