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, 0);
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, 0, SUPERCONTENTS_SOLID, 0, NULL);
54 if (maxfrac < trace.fraction)
57 ent->model->TraceLine(ent->model, ent->frameblend, ent->skeleton, &trace, starttransformed, endtransformed, SUPERCONTENTS_SOLID, 0);
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, int skipsupercontentsmask, 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, skipsupercontentsmask);
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, skipsupercontentsmask);
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.entities_active[i])
322 if (!cl.scores[i-1].name[0])
325 if(IS_OLDNEXUIZ_DERIVED(gamemode))
327 // don't hit spectators or nonsolid players
328 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
332 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
333 VectorAdd(origin, cl.playerstandmins, entmins);
334 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
335 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
337 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
338 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
339 Collision_ClipPointToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask, skipsupercontentsmask);
340 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
341 *hitnetworkentity = i;
342 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
350 // because this uses World_EntitiestoBox, we know all entity boxes overlap
351 // the clip region, so we can skip culling checks in the loop below
352 // note: if prog is NULL then there won't be any linked entities
354 if (hitcsqcentities && prog != NULL)
356 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
357 if (numtouchedicts > MAX_EDICTS)
359 // this never happens
360 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
361 numtouchedicts = MAX_EDICTS;
364 for (i = 0;i < numtouchedicts;i++)
366 touch = touchedicts[i];
368 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
370 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
375 // don't clip against self
376 if (passedict == touch)
378 // don't clip owned entities against owner
379 if (traceowner == touch)
381 // don't clip owner against owned entities
382 if (passedictprog == PRVM_clientedictedict(touch, owner))
384 // don't clip points against points (they can't collide)
385 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
389 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
391 // might interact, so do an exact clip
393 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
394 model = CL_GetModelFromEdict(touch);
396 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
398 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
399 Matrix4x4_Invert_Simple(&imatrix, &matrix);
400 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
401 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
402 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
403 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, skipsupercontentsmask, 0.0f);
405 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask);
407 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
408 *hitnetworkentity = 0;
409 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
421 trace_t CL_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities, qboolean hitsurfaces)
423 prvm_prog_t *prog = CLVM_prog;
424 int i, bodysupercontents;
426 prvm_edict_t *traceowner, *touch;
428 // temporary storage because prvm_vec_t may need conversion
429 vec3_t touchmins, touchmaxs;
430 // bounding box of entire move area
431 vec3_t clipboxmins, clipboxmaxs;
432 // size when clipping against monsters
433 vec3_t clipmins2, clipmaxs2;
434 // start and end origin of move
435 vec3_t clipstart, clipend;
438 // matrices to transform into/out of other entity's space
439 matrix4x4_t matrix, imatrix;
440 // model of other entity
442 // list of entities to test for collisions
444 static prvm_edict_t *touchedicts[MAX_EDICTS];
445 if (VectorCompare(start, end))
446 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
448 if (hitnetworkentity)
449 *hitnetworkentity = 0;
451 VectorCopy(start, clipstart);
452 VectorCopy(end, clipend);
453 VectorClear(clipmins2);
454 VectorClear(clipmaxs2);
455 #if COLLISIONPARANOID >= 3
456 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
460 Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, extend, hitsurfaces);
461 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
462 if (cliptrace.startsolid || cliptrace.fraction < 1)
463 cliptrace.ent = prog ? prog->edicts : NULL;
464 if (type == MOVE_WORLDONLY)
467 if (type == MOVE_MISSILE)
469 // LordHavoc: modified this, was = -15, now -= 15
470 for (i = 0;i < 3;i++)
477 // create the bounding box of the entire move
478 for (i = 0;i < 3;i++)
480 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
481 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
484 // debug override to test against everything
485 if (sv_debugmove.integer)
487 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
488 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
491 // if the passedict is world, make it NULL (to avoid two checks each time)
492 // this checks prog because this function is often called without a CSQC
494 if (prog == NULL || passedict == prog->edicts)
496 // precalculate prog value for passedict for comparisons
497 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
498 // precalculate passedict's owner edict pointer for comparisons
499 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
501 // collide against network entities
502 if (hitnetworkbrushmodels)
504 for (i = 0;i < cl.num_brushmodel_entities;i++)
506 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
507 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
509 Collision_ClipLineToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask, extend, hitsurfaces);
510 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
511 *hitnetworkentity = cl.brushmodel_entities[i];
512 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
516 // collide against player entities
517 if (hitnetworkplayers)
519 vec3_t origin, entmins, entmaxs;
520 matrix4x4_t entmatrix, entinversematrix;
522 if(IS_OLDNEXUIZ_DERIVED(gamemode))
524 // don't hit network players, if we are a nonsolid player
525 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
526 goto skipnetworkplayers;
529 for (i = 1;i <= cl.maxclients;i++)
531 entity_render_t *ent = &cl.entities[i].render;
533 // don't hit ourselves
534 if (i == cl.playerentity)
537 // don't hit players that don't exist
538 if (!cl.entities_active[i])
540 if (!cl.scores[i-1].name[0])
543 if(IS_OLDNEXUIZ_DERIVED(gamemode))
545 // don't hit spectators or nonsolid players
546 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
550 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
551 VectorAdd(origin, cl.playerstandmins, entmins);
552 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
553 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
555 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
556 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
557 Collision_ClipLineToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask, extend, hitsurfaces);
558 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
559 *hitnetworkentity = i;
560 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
568 // because this uses World_EntitiestoBox, we know all entity boxes overlap
569 // the clip region, so we can skip culling checks in the loop below
570 // note: if prog is NULL then there won't be any linked entities
572 if (hitcsqcentities && prog != NULL)
574 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
575 if (numtouchedicts > MAX_EDICTS)
577 // this never happens
578 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
579 numtouchedicts = MAX_EDICTS;
582 for (i = 0;i < numtouchedicts;i++)
584 touch = touchedicts[i];
586 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
588 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
593 // don't clip against self
594 if (passedict == touch)
596 // don't clip owned entities against owner
597 if (traceowner == touch)
599 // don't clip owner against owned entities
600 if (passedictprog == PRVM_clientedictedict(touch, owner))
602 // don't clip points against points (they can't collide)
603 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
607 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
609 // might interact, so do an exact clip
611 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
612 model = CL_GetModelFromEdict(touch);
614 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
616 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
617 Matrix4x4_Invert_Simple(&imatrix, &matrix);
618 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
619 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
620 if (type == MOVE_MISSILE && (int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
621 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
623 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, extend, hitsurfaces);
625 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
626 *hitnetworkentity = 0;
627 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
639 trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
641 prvm_prog_t *prog = CLVM_prog;
642 vec3_t hullmins, hullmaxs;
643 int i, bodysupercontents;
646 prvm_edict_t *traceowner, *touch;
648 // temporary storage because prvm_vec_t may need conversion
649 vec3_t touchmins, touchmaxs;
650 // bounding box of entire move area
651 vec3_t clipboxmins, clipboxmaxs;
652 // size of the moving object
653 vec3_t clipmins, clipmaxs;
654 // size when clipping against monsters
655 vec3_t clipmins2, clipmaxs2;
656 // start and end origin of move
657 vec3_t clipstart, clipend;
660 // matrices to transform into/out of other entity's space
661 matrix4x4_t matrix, imatrix;
662 // model of other entity
664 // list of entities to test for collisions
666 static prvm_edict_t *touchedicts[MAX_EDICTS];
667 if (VectorCompare(mins, maxs))
669 vec3_t shiftstart, shiftend;
670 VectorAdd(start, mins, shiftstart);
671 VectorAdd(end, mins, shiftend);
672 if (VectorCompare(start, end))
673 trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
675 trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, extend, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
676 VectorSubtract(trace.endpos, mins, trace.endpos);
680 if (hitnetworkentity)
681 *hitnetworkentity = 0;
683 VectorCopy(start, clipstart);
684 VectorCopy(end, clipend);
685 VectorCopy(mins, clipmins);
686 VectorCopy(maxs, clipmaxs);
687 VectorCopy(mins, clipmins2);
688 VectorCopy(maxs, clipmaxs2);
689 #if COLLISIONPARANOID >= 3
690 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
694 Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
695 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
696 if (cliptrace.startsolid || cliptrace.fraction < 1)
697 cliptrace.ent = prog ? prog->edicts : NULL;
698 if (type == MOVE_WORLDONLY)
701 if (type == MOVE_MISSILE)
703 // LordHavoc: modified this, was = -15, now -= 15
704 for (i = 0;i < 3;i++)
711 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
712 if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
713 cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
716 VectorCopy(clipmins, hullmins);
717 VectorCopy(clipmaxs, hullmaxs);
720 // create the bounding box of the entire move
721 for (i = 0;i < 3;i++)
723 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
724 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
727 // debug override to test against everything
728 if (sv_debugmove.integer)
730 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
731 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
734 // if the passedict is world, make it NULL (to avoid two checks each time)
735 // this checks prog because this function is often called without a CSQC
737 if (prog == NULL || passedict == prog->edicts)
739 // precalculate prog value for passedict for comparisons
740 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
741 // figure out whether this is a point trace for comparisons
742 pointtrace = VectorCompare(clipmins, clipmaxs);
743 // precalculate passedict's owner edict pointer for comparisons
744 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
746 // collide against network entities
747 if (hitnetworkbrushmodels)
749 for (i = 0;i < cl.num_brushmodel_entities;i++)
751 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
752 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
754 Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask, skipsupercontentsmask, extend);
755 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
756 *hitnetworkentity = cl.brushmodel_entities[i];
757 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
761 // collide against player entities
762 if (hitnetworkplayers)
764 vec3_t origin, entmins, entmaxs;
765 matrix4x4_t entmatrix, entinversematrix;
767 if(IS_OLDNEXUIZ_DERIVED(gamemode))
769 // don't hit network players, if we are a nonsolid player
770 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
771 goto skipnetworkplayers;
774 for (i = 1;i <= cl.maxclients;i++)
776 entity_render_t *ent = &cl.entities[i].render;
778 // don't hit ourselves
779 if (i == cl.playerentity)
782 // don't hit players that don't exist
783 if (!cl.entities_active[i])
785 if (!cl.scores[i-1].name[0])
788 if(IS_OLDNEXUIZ_DERIVED(gamemode))
790 // don't hit spectators or nonsolid players
791 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
795 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
796 VectorAdd(origin, cl.playerstandmins, entmins);
797 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
798 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
800 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
801 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
802 Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask, skipsupercontentsmask, extend);
803 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
804 *hitnetworkentity = i;
805 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
813 // because this uses World_EntitiestoBox, we know all entity boxes overlap
814 // the clip region, so we can skip culling checks in the loop below
815 // note: if prog is NULL then there won't be any linked entities
817 if (hitcsqcentities && prog != NULL)
819 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
820 if (numtouchedicts > MAX_EDICTS)
822 // this never happens
823 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
824 numtouchedicts = MAX_EDICTS;
827 for (i = 0;i < numtouchedicts;i++)
829 touch = touchedicts[i];
831 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
833 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
838 // don't clip against self
839 if (passedict == touch)
841 // don't clip owned entities against owner
842 if (traceowner == touch)
844 // don't clip owner against owned entities
845 if (passedictprog == PRVM_clientedictedict(touch, owner))
847 // don't clip points against points (they can't collide)
848 if (pointtrace && VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
852 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
854 // might interact, so do an exact clip
856 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
857 model = CL_GetModelFromEdict(touch);
859 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
861 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
862 Matrix4x4_Invert_Simple(&imatrix, &matrix);
863 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
864 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
865 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
866 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
868 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
870 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
871 *hitnetworkentity = 0;
872 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
884 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int type, int hitsupercontentsmask, int skipsupercontentsmask)
886 prvm_prog_t *prog = CLVM_prog;
890 // bounding box of entire move area
891 vec3_t clipboxmins, clipboxmaxs;
892 // start and end origin of move
893 vec3_t clipstart, clipend;
896 // matrices to transform into/out of other entity's space
897 matrix4x4_t matrix, imatrix;
898 // model of other entity
900 // list of entities to test for collisions
902 static prvm_edict_t *touchedicts[MAX_EDICTS];
904 VectorCopy(start, clipstart);
905 VectorCopy(end, clipend);
906 #if COLLISIONPARANOID >= 3
907 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
911 Collision_Cache_ClipLineToWorldSurfaces(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask);
912 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
913 if (cliptrace.startsolid || cliptrace.fraction < 1)
914 cliptrace.ent = prog ? prog->edicts : NULL;
915 if (type == MOVE_WORLDONLY)
918 // create the bounding box of the entire move
919 for (i = 0;i < 3;i++)
921 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) - 1;
922 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + 1;
925 // if the passedict is world, make it NULL (to avoid two checks each time)
926 // this checks prog because this function is often called without a CSQC
929 // collide against network entities
930 for (i = 0;i < cl.num_brushmodel_entities;i++)
932 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
933 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
935 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, ent->model, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask);
936 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
940 // because this uses World_EntitiestoBox, we know all entity boxes overlap
941 // the clip region, so we can skip culling checks in the loop below
942 // note: if prog is NULL then there won't be any linked entities
946 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
947 if (numtouchedicts > MAX_EDICTS)
949 // this never happens
950 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
951 numtouchedicts = MAX_EDICTS;
954 for (i = 0;i < numtouchedicts;i++)
956 touch = touchedicts[i];
957 // might interact, so do an exact clip
958 // only hit entity models, not collision shapes
959 model = CL_GetModelFromEdict(touch);
962 // animated models are too slow to collide against and can't be cached
963 if (touch->priv.server->frameblend || touch->priv.server->skeleton.relativetransforms)
965 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
967 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2], PRVM_clientedictvector(touch, angles)[0], PRVM_clientedictvector(touch, angles)[1], PRVM_clientedictvector(touch, angles)[2], 1);
968 Matrix4x4_Invert_Simple(&imatrix, &matrix);
969 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, model, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask);
970 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);