Example #1
0
/**
 * @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;
}
Example #2
0
/**
 * @brief test if check is visible by from
 * @param[in] team Living team members are always visible. If this is a negative
 * number we inverse the team rules (see comments included). In combination with VT_NOFRUSTUM
 * we can check whether there is any edict (that is no in our team) that can see @c check
 * @param[in] from is from team @c team and must be a living actor
 * @param[in] check The edict we want to get the visibility for
 * @param[in] flags @c VT_NOFRUSTUM, ...
 */
bool G_Vis (const int team, const Edict* from, const Edict* check, const vischeckflags_t flags)
{
	vec3_t eye;

	/* if any of them isn't in use, then they're not visible */
	if (!from->inuse || !check->inuse)
		return false;

	/* only actors and 2x2 units can see anything */
	if (!G_IsLivingActor(from) && !G_IsActiveCamera(from))
		return false;

	/* living team members are always visible */
	if (team >= 0 && check->getTeam() == team && !G_IsDead(check))
		return true;

	/* standard team rules */
	if (team >= 0 && from->getTeam() != team)
		return false;

	/* inverse team rules */
	if (team < 0 && check->getTeam() == -team)
		return false;

	/* check for same pos */
	if (VectorCompare(from->pos, check->pos))
		return true;

	if (!G_IsVisibleOnBattlefield(check))
		return false;

	/* view distance check */
	const int spotDist = G_VisCheckDist(from);
	if (VectorDistSqr(from->origin, check->origin) > spotDist * spotDist)
		return false;

	/* view frustum check */
	if (!(flags & VT_NOFRUSTUM) && !G_FrustumVis(from, check->origin))
		return false;

	/* get viewers eye height */
	G_ActorGetEyeVector(from, eye);

	/* line trace check */
	switch (check->type) {
	case ET_ACTOR:
	case ET_ACTOR2x2:
		return G_ActorVis(eye, from, check, false) > ACTOR_VIS_0;
	case ET_ITEM:
	case ET_CAMERA:
	case ET_PARTICLE:
		return !G_LineVis(eye, check->origin);
	default:
		return false;
	}
}
Example #3
0
/**
 * @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;
	}
}
Example #4
0
/**
 * @brief Deals splash damage to a target and its surroundings.
 * @param[in] ent The shooting actor
 * @param[in] fd The fire definition that defines what type of damage is dealt and how big the splash radius is.
 * @param[in] impact The impact vector where the grenade is exploding
 * @param[in,out] mock pseudo shooting - only for calculating mock values - nullptr for real shots
 * @param[in] tr The trace where the grenade hits something (or not)
 */
static void G_SplashDamage (Actor* ent, const fireDef_t* fd, vec3_t impact, shot_mock_t* mock, const trace_t* tr)
{
	assert(fd->splrad > 0.0f);

	const bool shock = (fd->obj->dmgtype == gi.csi->damShock);

	Edict* check = nullptr;
	while ((check = G_EdictsGetNextInUse(check))) {
		/* If we use a blinding weapon we skip the target if it's looking
		 * away from the impact location. */
		if (shock && !G_FrustumVis(check, impact))
			continue;

		const bool isActor = G_IsLivingActor(check);
		vec3_t center;
		if (G_IsBrushModel(check) && G_IsBreakable(check))
			check->absBox.getCenter(center);
		else if (isActor || G_IsBreakable(check))
			VectorCopy(check->origin, center);
		else
			continue;

		/* check for distance */
		float dist = VectorDist(impact, center);
		dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0.0f;
		if (dist > fd->splrad)
			continue;

		if (fd->irgoggles) {
			if (isActor) {
				/* check whether this actor (check) is in the field of view of the 'shooter' (ent) */
				if (G_FrustumVis(ent, check->origin)) {
					if (!mock) {
						vec3_t eyeEnt;
						G_ActorGetEyeVector(ent, eyeEnt);
						if (!G_SmokeVis(eyeEnt, check)) {
							const unsigned int playerMask = G_TeamToPM(ent->getTeam()) ^ G_VisToPM(check->visflags);
							G_AppearPerishEvent(playerMask, true, *check, ent);
							G_VisFlagsAdd(*check, G_PMToVis(playerMask));
						}
					}
				}
			}
			continue;
		}

		/* check for walls */
		if (isActor && G_TestLine(impact, check->origin))
			continue;

		/* do damage */
		const int damage = shock ? 0 : fd->spldmg[0] * (1.0f - dist / fd->splrad);

		if (mock)
			mock->allow_self = true;
		/* Send hurt sounds for actors, but only if they'll recieve damage from this attack */
		if (G_Damage(check, fd, damage, ent, mock, nullptr) && isActor
				&& (G_ApplyProtection(check, fd->dmgweight,  damage) > 0) && !shock) {
			const teamDef_t* teamDef = check->chr.teamDef;
			const int gender = check->chr.gender;
			const char* sound = teamDef->getActorSound(gender, SND_HURT);
			G_EventSpawnSound(G_VisToPM(check->visflags), *check, nullptr, sound);
		}
		if (mock)
			mock->allow_self = false;
	}

	/** @todo splash might also hit other surfaces and the trace doesn't handle that */
	if (tr && G_FireAffectedSurface(tr->surface, fd)) {
		/* move a little away from the impact vector */
		VectorMA(impact, 1, tr->plane.normal, impact);
		G_SpawnParticle(impact, tr->contentFlags >> 8, "burning");
	}
Example #5
0
/**
 * @brief calculate how much check is "visible" by @c ent
 * @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 (one of the ACTOR_VIS_* constants) 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 Edict* ent, const Edict* check, bool full)
{
	vec3_t eyeEnt;
	G_ActorGetEyeVector(ent, eyeEnt);
	if (G_SmokeVis(eyeEnt, check))
		return ACTOR_VIS_0;

	vec3_t test, dir;
	float delta;
	/* 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] = eyeEnt[1] - check->origin[1];
	dir[1] = check->origin[0] - eyeEnt[0];
	dir[2] = 0;
	VectorNormalizeFast(dir);
	VectorMA(test, -7, dir, test);

	/* do 3 tests */
	int n = 0;
	for (int i = 0; i < 3; i++) {
		if (!G_LineVis(eyeEnt, 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;
	}
}