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, 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, 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, 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, int skipmaterialflagsmask, 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];
232 if (hitnetworkentity)
233 *hitnetworkentity = 0;
235 VectorCopy(start, clipstart);
236 VectorClear(clipmins2);
237 VectorClear(clipmaxs2);
238 #if COLLISIONPARANOID >= 3
239 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
243 Collision_ClipPointToWorld(&cliptrace, cl.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
244 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
245 if (cliptrace.startsolid || cliptrace.fraction < 1)
246 cliptrace.ent = prog ? prog->edicts : NULL;
247 if (type == MOVE_WORLDONLY)
250 if (type == MOVE_MISSILE)
252 // LadyHavoc: modified this, was = -15, now -= 15
253 for (i = 0;i < 3;i++)
260 // create the bounding box of the entire move
261 for (i = 0;i < 3;i++)
263 clipboxmins[i] = clipstart[i] - 1;
264 clipboxmaxs[i] = clipstart[i] + 1;
267 // debug override to test against everything
268 if (sv_debugmove.integer)
270 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
271 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
274 // if the passedict is world, make it NULL (to avoid two checks each time)
275 // this checks prog because this function is often called without a CSQC
277 if (prog == NULL || passedict == prog->edicts)
279 // precalculate prog value for passedict for comparisons
280 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
281 // precalculate passedict's owner edict pointer for comparisons
282 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
284 clipgroup = passedict ? (int)PRVM_clientedictfloat(passedict, clipgroup) : 0;
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, skipsupercontentsmask, skipmaterialflagsmask);
295 if (cliptrace.fraction > trace.fraction && 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.entities_active[i])
325 if (!cl.scores[i-1].name[0])
328 if(IS_OLDNEXUIZ_DERIVED(gamemode))
330 // don't hit spectators or nonsolid players
331 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
335 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
336 VectorAdd(origin, cl.playerstandmins, entmins);
337 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
338 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
340 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
341 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
342 Collision_ClipPointToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
343 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
344 *hitnetworkentity = i;
345 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
353 // because this uses World_EntitiestoBox, we know all entity boxes overlap
354 // the clip region, so we can skip culling checks in the loop below
355 // note: if prog is NULL then there won't be any linked entities
357 if (hitcsqcentities && prog != NULL)
359 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
360 if (numtouchedicts > MAX_EDICTS)
362 // this never happens
363 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
364 numtouchedicts = MAX_EDICTS;
367 for (i = 0;i < numtouchedicts;i++)
369 touch = touchedicts[i];
371 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
373 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
378 // don't clip against self
379 if (passedict == touch)
381 // don't clip owned entities against owner
382 if (traceowner == touch)
384 // don't clip owner against owned entities
385 if (passedictprog == PRVM_clientedictedict(touch, owner))
387 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
388 if (clipgroup && clipgroup == (int)PRVM_clientedictfloat(touch, clipgroup))
390 // don't clip points against points (they can't collide)
391 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
395 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
397 // might interact, so do an exact clip
399 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
400 model = CL_GetModelFromEdict(touch);
402 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);
404 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
405 Matrix4x4_Invert_Simple(&imatrix, &matrix);
406 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
407 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
408 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
409 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0.0f);
411 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
413 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
414 *hitnetworkentity = 0;
415 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
427 trace_t CL_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities, qboolean hitsurfaces)
429 prvm_prog_t *prog = CLVM_prog;
430 int i, bodysupercontents;
432 prvm_edict_t *traceowner, *touch;
434 // temporary storage because prvm_vec_t may need conversion
435 vec3_t touchmins, touchmaxs;
436 // bounding box of entire move area
437 vec3_t clipboxmins, clipboxmaxs;
438 // size when clipping against monsters
439 vec3_t clipmins2, clipmaxs2;
440 // start and end origin of move
441 vec3_t clipstart, clipend;
444 // matrices to transform into/out of other entity's space
445 matrix4x4_t matrix, imatrix;
446 // model of other entity
448 // list of entities to test for collisions
450 static prvm_edict_t *touchedicts[MAX_EDICTS];
452 if (VectorCompare(start, end))
453 return CL_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
455 if (hitnetworkentity)
456 *hitnetworkentity = 0;
458 VectorCopy(start, clipstart);
459 VectorCopy(end, clipend);
460 VectorClear(clipmins2);
461 VectorClear(clipmaxs2);
462 #if COLLISIONPARANOID >= 3
463 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
467 Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitsurfaces);
468 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
469 if (cliptrace.startsolid || cliptrace.fraction < 1)
470 cliptrace.ent = prog ? prog->edicts : NULL;
471 if (type == MOVE_WORLDONLY)
474 if (type == MOVE_MISSILE)
476 // LadyHavoc: modified this, was = -15, now -= 15
477 for (i = 0;i < 3;i++)
484 // create the bounding box of the entire move
485 for (i = 0;i < 3;i++)
487 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
488 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
491 // debug override to test against everything
492 if (sv_debugmove.integer)
494 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
495 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
498 // if the passedict is world, make it NULL (to avoid two checks each time)
499 // this checks prog because this function is often called without a CSQC
501 if (prog == NULL || passedict == prog->edicts)
503 // precalculate prog value for passedict for comparisons
504 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
505 // precalculate passedict's owner edict pointer for comparisons
506 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
508 clipgroup = passedict ? (int)PRVM_clientedictfloat(passedict, clipgroup) : 0;
510 // collide against network entities
511 if (hitnetworkbrushmodels)
513 for (i = 0;i < cl.num_brushmodel_entities;i++)
515 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
516 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
518 Collision_ClipLineToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitsurfaces);
519 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
520 *hitnetworkentity = cl.brushmodel_entities[i];
521 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
525 // collide against player entities
526 if (hitnetworkplayers)
528 vec3_t origin, entmins, entmaxs;
529 matrix4x4_t entmatrix, entinversematrix;
531 if(IS_OLDNEXUIZ_DERIVED(gamemode))
533 // don't hit network players, if we are a nonsolid player
534 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
535 goto skipnetworkplayers;
538 for (i = 1;i <= cl.maxclients;i++)
540 entity_render_t *ent = &cl.entities[i].render;
542 // don't hit ourselves
543 if (i == cl.playerentity)
546 // don't hit players that don't exist
547 if (!cl.entities_active[i])
549 if (!cl.scores[i-1].name[0])
552 if(IS_OLDNEXUIZ_DERIVED(gamemode))
554 // don't hit spectators or nonsolid players
555 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
559 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
560 VectorAdd(origin, cl.playerstandmins, entmins);
561 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
562 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
564 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
565 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
566 Collision_ClipLineToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitsurfaces);
567 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
568 *hitnetworkentity = i;
569 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
577 // because this uses World_EntitiestoBox, we know all entity boxes overlap
578 // the clip region, so we can skip culling checks in the loop below
579 // note: if prog is NULL then there won't be any linked entities
581 if (hitcsqcentities && prog != NULL)
583 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
584 if (numtouchedicts > MAX_EDICTS)
586 // this never happens
587 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
588 numtouchedicts = MAX_EDICTS;
591 for (i = 0;i < numtouchedicts;i++)
593 touch = touchedicts[i];
595 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
597 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
602 // don't clip against self
603 if (passedict == touch)
605 // don't clip owned entities against owner
606 if (traceowner == touch)
608 // don't clip owner against owned entities
609 if (passedictprog == PRVM_clientedictedict(touch, owner))
611 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
612 if (clipgroup && clipgroup == (int)PRVM_clientedictfloat(touch, clipgroup))
614 // don't clip points against points (they can't collide)
615 if (VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
619 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
621 // might interact, so do an exact clip
623 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
624 model = CL_GetModelFromEdict(touch);
626 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);
628 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
629 Matrix4x4_Invert_Simple(&imatrix, &matrix);
630 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
631 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
632 if (type == MOVE_MISSILE && (int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
633 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
635 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitsurfaces);
637 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
638 *hitnetworkentity = 0;
639 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
646 trace_t CL_TraceLine_FromViewOrigin(int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities, qboolean hitsurfaces)
648 vec3_t org, temp, dest;
650 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, org);
651 VectorSet(temp, 65536, 0, 0);
652 Matrix4x4_Transform(&r_refdef.view.matrix, temp, dest);
654 return CL_TraceLine(org, dest, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, hitsurfaces);
663 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, int skipmaterialflagsmask, float extend, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
665 prvm_prog_t *prog = CLVM_prog;
666 vec3_t hullmins, hullmaxs;
667 int i, bodysupercontents;
670 prvm_edict_t *traceowner, *touch;
672 // temporary storage because prvm_vec_t may need conversion
673 vec3_t touchmins, touchmaxs;
674 // bounding box of entire move area
675 vec3_t clipboxmins, clipboxmaxs;
676 // size of the moving object
677 vec3_t clipmins, clipmaxs;
678 // size when clipping against monsters
679 vec3_t clipmins2, clipmaxs2;
680 // start and end origin of move
681 vec3_t clipstart, clipend;
684 // matrices to transform into/out of other entity's space
685 matrix4x4_t matrix, imatrix;
686 // model of other entity
688 // list of entities to test for collisions
690 static prvm_edict_t *touchedicts[MAX_EDICTS];
692 if (VectorCompare(mins, maxs))
694 vec3_t shiftstart, shiftend;
695 VectorAdd(start, mins, shiftstart);
696 VectorAdd(end, mins, shiftend);
697 if (VectorCompare(start, end))
698 trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
700 trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
701 VectorSubtract(trace.endpos, mins, trace.endpos);
705 if (hitnetworkentity)
706 *hitnetworkentity = 0;
708 VectorCopy(start, clipstart);
709 VectorCopy(end, clipend);
710 VectorCopy(mins, clipmins);
711 VectorCopy(maxs, clipmaxs);
712 VectorCopy(mins, clipmins2);
713 VectorCopy(maxs, clipmaxs2);
714 #if COLLISIONPARANOID >= 3
715 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
719 Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
720 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
721 if (cliptrace.startsolid || cliptrace.fraction < 1)
722 cliptrace.ent = prog ? prog->edicts : NULL;
723 if (type == MOVE_WORLDONLY)
726 if (type == MOVE_MISSILE)
728 // LadyHavoc: modified this, was = -15, now -= 15
729 for (i = 0;i < 3;i++)
736 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
737 if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
738 cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
741 VectorCopy(clipmins, hullmins);
742 VectorCopy(clipmaxs, hullmaxs);
745 // create the bounding box of the entire move
746 for (i = 0;i < 3;i++)
748 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
749 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
752 // debug override to test against everything
753 if (sv_debugmove.integer)
755 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
756 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
759 // if the passedict is world, make it NULL (to avoid two checks each time)
760 // this checks prog because this function is often called without a CSQC
762 if (prog == NULL || passedict == prog->edicts)
764 // precalculate prog value for passedict for comparisons
765 passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
766 // figure out whether this is a point trace for comparisons
767 pointtrace = VectorCompare(clipmins, clipmaxs);
768 // precalculate passedict's owner edict pointer for comparisons
769 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_clientedictedict(passedict, owner)) : NULL;
771 clipgroup = passedict ? (int)PRVM_clientedictfloat(passedict, clipgroup) : 0;
773 // collide against network entities
774 if (hitnetworkbrushmodels)
776 for (i = 0;i < cl.num_brushmodel_entities;i++)
778 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
779 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
781 Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
782 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
783 *hitnetworkentity = cl.brushmodel_entities[i];
784 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
788 // collide against player entities
789 if (hitnetworkplayers)
791 vec3_t origin, entmins, entmaxs;
792 matrix4x4_t entmatrix, entinversematrix;
794 if(IS_OLDNEXUIZ_DERIVED(gamemode))
796 // don't hit network players, if we are a nonsolid player
797 if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
798 goto skipnetworkplayers;
801 for (i = 1;i <= cl.maxclients;i++)
803 entity_render_t *ent = &cl.entities[i].render;
805 // don't hit ourselves
806 if (i == cl.playerentity)
809 // don't hit players that don't exist
810 if (!cl.entities_active[i])
812 if (!cl.scores[i-1].name[0])
815 if(IS_OLDNEXUIZ_DERIVED(gamemode))
817 // don't hit spectators or nonsolid players
818 if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
822 Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
823 VectorAdd(origin, cl.playerstandmins, entmins);
824 VectorAdd(origin, cl.playerstandmaxs, entmaxs);
825 if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
827 Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
828 Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
829 Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
830 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
831 *hitnetworkentity = i;
832 Collision_CombineTraces(&cliptrace, &trace, NULL, false);
840 // because this uses World_EntitiestoBox, we know all entity boxes overlap
841 // the clip region, so we can skip culling checks in the loop below
842 // note: if prog is NULL then there won't be any linked entities
844 if (hitcsqcentities && prog != NULL)
846 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
847 if (numtouchedicts > MAX_EDICTS)
849 // this never happens
850 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
851 numtouchedicts = MAX_EDICTS;
854 for (i = 0;i < numtouchedicts;i++)
856 touch = touchedicts[i];
858 if (PRVM_clientedictfloat(touch, solid) < SOLID_BBOX)
860 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
865 // don't clip against self
866 if (passedict == touch)
868 // don't clip owned entities against owner
869 if (traceowner == touch)
871 // don't clip owner against owned entities
872 if (passedictprog == PRVM_clientedictedict(touch, owner))
874 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
875 if (clipgroup && clipgroup == (int)PRVM_clientedictfloat(touch, clipgroup))
877 // don't clip points against points (they can't collide)
878 if (pointtrace && VectorCompare(PRVM_clientedictvector(touch, mins), PRVM_clientedictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)))
882 bodysupercontents = PRVM_clientedictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
884 // might interact, so do an exact clip
886 if ((int) PRVM_clientedictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
887 model = CL_GetModelFromEdict(touch);
889 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);
891 Matrix4x4_CreateTranslate(&matrix, PRVM_clientedictvector(touch, origin)[0], PRVM_clientedictvector(touch, origin)[1], PRVM_clientedictvector(touch, origin)[2]);
892 Matrix4x4_Invert_Simple(&imatrix, &matrix);
893 VectorCopy(PRVM_clientedictvector(touch, mins), touchmins);
894 VectorCopy(PRVM_clientedictvector(touch, maxs), touchmaxs);
895 if ((int)PRVM_clientedictfloat(touch, flags) & FL_MONSTER)
896 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
898 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
900 if (cliptrace.fraction > trace.fraction && hitnetworkentity)
901 *hitnetworkentity = 0;
902 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);
914 trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int type, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
916 prvm_prog_t *prog = CLVM_prog;
920 // bounding box of entire move area
921 vec3_t clipboxmins, clipboxmaxs;
922 // start and end origin of move
923 vec3_t clipstart, clipend;
926 // matrices to transform into/out of other entity's space
927 matrix4x4_t matrix, imatrix;
928 // model of other entity
930 // list of entities to test for collisions
932 static prvm_edict_t *touchedicts[MAX_EDICTS];
934 VectorCopy(start, clipstart);
935 VectorCopy(end, clipend);
936 #if COLLISIONPARANOID >= 3
937 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
941 Collision_Cache_ClipLineToWorldSurfaces(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
942 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
943 if (cliptrace.startsolid || cliptrace.fraction < 1)
944 cliptrace.ent = prog ? prog->edicts : NULL;
945 if (type == MOVE_WORLDONLY)
948 // create the bounding box of the entire move
949 for (i = 0;i < 3;i++)
951 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) - 1;
952 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + 1;
955 // if the passedict is world, make it NULL (to avoid two checks each time)
956 // this checks prog because this function is often called without a CSQC
959 // collide against network entities
960 for (i = 0;i < cl.num_brushmodel_entities;i++)
962 entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
963 if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
965 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, ent->model, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
966 Collision_CombineTraces(&cliptrace, &trace, NULL, true);
970 // because this uses World_EntitiestoBox, we know all entity boxes overlap
971 // the clip region, so we can skip culling checks in the loop below
972 // note: if prog is NULL then there won't be any linked entities
976 numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
977 if (numtouchedicts > MAX_EDICTS)
979 // this never happens
980 Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
981 numtouchedicts = MAX_EDICTS;
984 for (i = 0;i < numtouchedicts;i++)
986 touch = touchedicts[i];
987 // might interact, so do an exact clip
988 // only hit entity models, not collision shapes
989 model = CL_GetModelFromEdict(touch);
992 // animated models are not suitable for caching
993 if ((touch->priv.server->frameblend && (touch->priv.server->frameblend[0].lerp != 1.0 || touch->priv.server->frameblend[0].subframe != 0)) || touch->priv.server->skeleton.relativetransforms)
995 if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
997 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);
998 Matrix4x4_Invert_Simple(&imatrix, &matrix);
999 Collision_Cache_ClipLineToGenericEntitySurfaces(&trace, model, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1000 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_clientedictfloat(touch, solid) == SOLID_BSP);