/** * @brief Test if point is "visible" from team. * @param[in] team A team to test. * @param[in] point A point to check. * @return true if point is "visible" */ static bool G_TeamPointVis (int team, const vec3_t point) { Edict *from = nullptr; vec3_t eye; /* test if point is visible from team */ while ((from = G_EdictsGetNextLivingActorOfTeam(from, team))) { if (G_FrustumVis(from, point)) { /* get viewers eye height */ G_ActorGetEyeVector(from, eye); /* line of sight */ if (!G_TestLine(eye, point)) { const float distance = VectorDist(from->origin, point); bool blocked = false; /* check visibility in the smoke */ if (distance >= UNIT_SIZE) { Edict *e = nullptr; while ((e = G_EdictsGetNextInUse(e))) { if (G_IsSmoke(e) && RayIntersectAABB(eye, point, e->absmin, e->absmax)) { blocked = true; break; } } } if (!blocked) return true; } } } /* not visible */ return false; }
/** * @brief tests for smoke interference * @param[in] from The point to check visibility from * @param[in] check The edict to check visibility to * @return true if @c check is invisible from @c from (smoke is in the way), false otherwise. */ bool G_SmokeVis (const vec3_t from, const Edict* check) { const float distance = VectorDist(check->origin, from); /* units that are very close are visible in the smoke */ if (distance > UNIT_SIZE * 1.5f) { Edict* e = nullptr; while ((e = G_EdictsGetNextInUse(e))) { if (G_IsSmoke(e)) { if (RayIntersectAABB(from, check->absBox.mins, e->absBox) || RayIntersectAABB(from, check->absBox.maxs, e->absBox)) { return true; } } } } return false; }
/** * @brief calculate how much check is "visible" from @c from * @param[in] from The world coordinate to check from * @param[in] ent The source edict of the check * @param[in] check The edict to check how good (or if at all) it is visible * @param[in] full Perform a full check in different directions. If this is * @c false the actor is fully visible if one vis check returned @c true. With * @c true this function can also return a value != 0.0 and != 1.0. Try to only * use @c true if you really need the full check. Full checks are of course * more expensive. * @return a value between 0.0 and 1.0 which reflects the visibility from 0 * to 100 percent * @note This call isn't cheap - try to do this only if you really need the * visibility check or the statement whether one particular actor see another * particular actor. * @sa CL_ActorVis */ float G_ActorVis (const vec3_t from, const edict_t *ent, const edict_t *check, bool full) { vec3_t test, dir; float delta; int i, n; const float distance = VectorDist(check->origin, ent->origin); /* units that are very close are visible in the smoke */ if (distance > UNIT_SIZE * 1.5f) { vec3_t eyeEnt; edict_t *e = NULL; G_ActorGetEyeVector(ent, eyeEnt); while ((e = G_EdictsGetNext(e))) { if (G_IsSmoke(e)) { if (RayIntersectAABB(eyeEnt, check->absmin, e->absmin, e->absmax) || RayIntersectAABB(eyeEnt, check->absmax, e->absmin, e->absmax)) { return ACTOR_VIS_0; } } } } /* start on eye height */ VectorCopy(check->origin, test); if (G_IsDead(check)) { test[2] += PLAYER_DEAD; delta = 0; } else if (G_IsCrouched(check)) { test[2] += PLAYER_CROUCH - 2; delta = (PLAYER_CROUCH - PLAYER_MIN) / 2 - 2; } else { test[2] += PLAYER_STAND; delta = (PLAYER_STAND - PLAYER_MIN) / 2 - 2; } /* side shifting -> better checks */ dir[0] = from[1] - check->origin[1]; dir[1] = check->origin[0] - from[0]; dir[2] = 0; VectorNormalizeFast(dir); VectorMA(test, -7, dir, test); /* do 3 tests */ n = 0; for (i = 0; i < 3; i++) { if (!G_LineVis(from, test)) { if (full) n++; else return ACTOR_VIS_100; } /* look further down or stop */ if (!delta) { if (n > 0) return ACTOR_VIS_100; else return ACTOR_VIS_0; } VectorMA(test, 7, dir, test); test[2] -= delta; } /* return factor */ switch (n) { case 0: return ACTOR_VIS_0; case 1: return ACTOR_VIS_10; case 2: return ACTOR_VIS_50; default: return ACTOR_VIS_100; } }