Пример #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;
}
Пример #2
0
/**
 * @brief Check whether ent can reaction fire at target, i.e. that it can see it and neither is dead etc.
 * @param[in] ent The entity that might be firing
 * @param[in] target The entity that might be fired at
 * @return @c true if 'ent' can actually fire at 'target', @c false otherwise
 */
static bool G_ReactionFireIsPossible (const edict_t *ent, const edict_t *target)
{
	float actorVis;
	bool frustum;

	/* an entity can't reaction fire at itself */
	if (ent == target)
		return false;

	/* Don't react in your own turn */
	if (ent->team == level.activeTeam)
		return false;

	/* ent can't use RF if is in STATE_DAZED (flashbang impact) */
	if (G_IsDazed(ent))
		return false;

	if (G_IsDead(target))
		return false;

	/* check ent has reaction fire enabled */
	if (!G_IsShaken(ent) && !G_IsReaction(ent))
		return false;

	/* check ent has weapon in RF hand */
	/* @todo Should this situation even happen when G_IsReaction(ent) is true? */
	if (!ACTOR_GET_INV(ent, ent->chr.RFmode.hand)) {
		/* print character info if this happens, for now */
		gi.DPrintf("Reaction fire enabled but no weapon for hand (name=%s,hand=%i,fmIdx=%i)\n",
				ent->chr.name, ent->chr.RFmode.hand, ent->chr.RFmode.fmIdx);
		return false;
	}

	if (!G_IsVisibleForTeam(target, ent->team))
		return false;

	/* If reaction fire is triggered by a friendly unit
	 * and the shooter is still sane, don't shoot;
	 * well, if the shooter isn't sane anymore... */
	if (G_IsCivilian(target) || target->team == ent->team)
		if (!G_IsShaken(ent) || (float) ent->morale / mor_shaken->value > frand())
			return false;

	/* check in range and visible */
	if (VectorDistSqr(ent->origin, target->origin) > MAX_SPOT_DIST * MAX_SPOT_DIST)
		return false;

	frustum = G_FrustumVis(ent, target->origin);
	if (!frustum)
		return false;

	actorVis = G_ActorVis(ent->origin, ent, target, true);
	if (actorVis <= 0.2)
		return false;

	/* okay do it then */
	return true;
}
Пример #3
0
/**
 * @brief Check whether ent can reaction fire at target, i.e. that it can see it and neither is dead etc.
 * @param[in] ent The entity that might be firing
 * @param[in] target The entity that might be fired at
 * @return @c true if 'ent' can actually fire at 'target', @c false otherwise
 */
static bool G_ReactionFireIsPossible (Edict *ent, const Edict *target)
{
	/* an entity can't reaction fire at itself */
	if (ent == target)
		return false;

	/* Don't react in your own turn */
	if (ent->team == level.activeTeam)
		return false;

	/* ent can't use RF if is in STATE_DAZED (flashbang impact) */
	if (G_IsDazed(ent))
		return false;

	if (G_IsDead(target))
		return false;

	/* check ent has reaction fire enabled */
	if (!G_IsReaction(ent))
		return false;

	/* check ent has weapon in RF hand */
	if (!ent->getHandItem(ent->chr.RFmode.getHand())) {
		/* print character info if this happens, for now */
		gi.DPrintf("Reaction fire enabled but no weapon for hand (name=%s,entnum=%i,hand=%i,fmIdx=%i)\n",
				ent->chr.name, ent->number, ent->chr.RFmode.getHand(), ent->chr.RFmode.getFmIdx());
		G_RemoveReaction(ent);
		return false;
	}

	if (!G_IsVisibleForTeam(target, ent->team))
		return false;

	/* If reaction fire is triggered by a friendly unit
	 * and the shooter is still sane, don't shoot;
	 * well, if the shooter isn't sane anymore... */
	if (G_IsCivilian(target) || target->team == ent->team)
		if (!G_IsShaken(ent) || (float) ent->morale / mor_shaken->value > frand())
			return false;

	/* check in range and visible */
	const int spotDist = G_VisCheckDist(ent);
	if (VectorDistSqr(ent->origin, target->origin) > spotDist * spotDist)
		return false;

	const bool frustum = G_FrustumVis(ent, target->origin);
	if (!frustum)
		return false;

	const float actorVis = G_ActorVis(ent->origin, ent, target, true);
	if (actorVis <= 0.2)
		return false;

	/* okay do it then */
	return true;
}
Пример #4
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;
	}
}
Пример #5
0
/**
 * @brief Check whether ent can reaction fire at target, i.e. that it can see it and neither is dead etc.
 * @param[in] ent The entity that might be firing
 * @param[in] target The entity that might be fired at
 * @return @c true if 'ent' can actually fire at 'target', @c false otherwise
 */
static qboolean G_ReactionFireIsPossible (const edict_t *ent, const edict_t *target)
{
	float actorVis;
	qboolean frustum;

	/* an entity can't reaction fire at itself */
	if (ent == target)
		return qfalse;

	/* Don't react in your own turn */
	if (ent->team == level.activeTeam)
		return qfalse;

	/* ent can't use RF if is in STATE_DAZED (flashbang impact) */
	if (G_IsDazed(ent))
		return qfalse;

	if (G_IsDead(target))
		return qfalse;

	/* check ent has reaction fire enabled */
	if (!G_IsShaken(ent) && !G_IsReaction(ent))
		return qfalse;

	if (!G_IsVisibleForTeam(target, ent->team))
		return qfalse;

	/* If reaction fire is triggered by a friendly unit
	 * and the shooter is still sane, don't shoot;
	 * well, if the shooter isn't sane anymore... */
	if (G_IsCivilian(target) || target->team == ent->team)
		if (!G_IsShaken(ent) || (float) ent->morale / mor_shaken->value > frand())
			return qfalse;

	/* check in range and visible */
	if (VectorDistSqr(ent->origin, target->origin) > MAX_SPOT_DIST * MAX_SPOT_DIST)
		return qfalse;

	frustum = G_FrustumVis(ent, target->origin);
	if (!frustum)
		return qfalse;

	actorVis = G_ActorVis(ent->origin, target, qtrue);
	if (actorVis <= 0.2)
		return qfalse;

	/* okay do it then */
	return qtrue;
}
Пример #6
0
/**
 * @brief Check whether shooter can see his target well enough
 * @param[in] shooter The entity that might be firing
 * @param[in] target The entity that might be fired at
 */
bool ReactionFire::canSee (const Actor* shooter, const Edict* target) const
{
	if (!G_IsVisibleForTeam(target, shooter->getTeam()))
		return false;

	/* check in range and visible */
	const int spotDist = G_VisCheckDist(shooter);
	if (VectorDistSqr(shooter->origin, target->origin) > spotDist * spotDist)
		return false;

	const bool frustum = G_FrustumVis(shooter, target->origin);
	if (!frustum)
		return false;

	const float actorVis = G_ActorVis(shooter->origin, shooter, target, true);
	if (actorVis < 0.1)
		return false;

	return true;
}
Пример #7
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_t *from = NULL;
	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 */
			VectorCopy(from->origin, eye);
			if (G_IsCrouched(from))
				eye[2] += EYE_CROUCH;
			else
				eye[2] += EYE_STAND;

			/* line of sight */
			if (!G_TestLine(eye, point))
				return true;
		}
	}

	/* not visible */
	return false;
}
Пример #8
0
/**
 * @brief Applies morale changes to actors around a wounded or killed actor.
 * @note only called when mor_panic is not zero
 * @param[in] type Type of morale modifier (@sa morale_modifiers)
 * @param[in] victim An actor being a victim of the attack.
 * @param[in] attacker An actor being attacker in this attack.
 * @param[in] param Used to modify morale changes, for G_Damage() it is value of damage.
 * @sa G_Damage
 */
static void G_Morale (int type, const edict_t * victim, const edict_t * attacker, int param)
{
	edict_t *ent = NULL;
	int newMorale;
	float mod;

	while ((ent = G_EdictsGetNextInUse(ent))) {
		/* this only applies to ET_ACTOR but not ET_ACTOR2x2 */
		if (ent->type == ET_ACTOR && !G_IsDead(ent) && ent->team != TEAM_CIVILIAN) {
			switch (type) {
			case ML_WOUND:
			case ML_DEATH:
				/* morale damage depends on the damage */
				mod = mob_wound->value * param;
				/* death hurts morale even more than just damage */
				if (type == ML_DEATH)
					mod += mob_death->value;
				/* seeing how someone gets shot increases the morale change */
				if (ent == victim || (G_FrustumVis(ent, victim->origin) && G_ActorVis(ent->origin, ent, victim, false)))
					mod *= mof_watching->value;
				if (attacker != NULL && ent->team == attacker->team) {
					/* teamkills are considered to be bad form, but won't cause an increased morale boost for the enemy */
					/* morale boost isn't equal to morale loss (it's lower, but morale gets regenerated) */
					if (victim->team == attacker->team)
						mod *= mof_teamkill->value;
					else
						mod *= mof_enemy->value;
				}
				/* seeing a civilian die is more "acceptable" */
				if (G_IsCivilian(victim))
					mod *= mof_civilian->value;
				/* if an ally (or in singleplayermode, as human, a civilian) got shot, lower the morale, don't heighten it. */
				if (victim->team == ent->team || (G_IsCivilian(victim) && ent->team != TEAM_ALIEN && sv_maxclients->integer == 1))
					mod *= -1;
				if (attacker != NULL) {
					/* if you stand near to the attacker or the victim, the morale change is higher. */
					mod *= mor_default->value + pow(0.5, VectorDist(ent->origin, victim->origin) / mor_distance->value)
						* mor_victim->value + pow(0.5, VectorDist(ent->origin, attacker->origin) / mor_distance->value)
						* mor_attacker->value;
				} else {
					mod *= mor_default->value + pow(0.5, VectorDist(ent->origin, victim->origin) / mor_distance->value)
						* mor_victim->value;
				}
				/* morale damage depends on the number of living allies */
				mod *= (1 - mon_teamfactor->value)
					+ mon_teamfactor->value * (level.num_spawned[victim->team] + 1)
					/ (level.num_alive[victim->team] + 1);
				/* being hit isn't fun */
				if (ent == victim)
					mod *= mor_pain->value;
				break;
			default:
				gi.DPrintf("Undefined morale modifier type %i\n", type);
				mod = 0;
				break;
			}
			/* clamp new morale */
			/*+0.9 to allow weapons like flamethrowers to inflict panic (typecast rounding) */
			newMorale = ent->morale + (int) (MORALE_RANDOM(mod) + 0.9);
			if (newMorale > GET_MORALE(ent->chr.score.skills[ABILITY_MIND]))
				ent->morale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]);
			else if (newMorale < 0)
				ent->morale = 0;
			else
				ent->morale = newMorale;

			/* send phys data */
			G_SendStats(ent);
		}
	}
}
Пример #9
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 - NULL for real shots
 * @param[in] tr The trace where the grenade hits something (or not)
 */
static void G_SplashDamage (edict_t *ent, const fireDef_t *fd, vec3_t impact, shot_mock_t *mock, const trace_t* tr)
{
	edict_t *check = NULL;
	vec3_t center;
	float dist;
	int damage;

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

	assert(fd->splrad > 0.0);

	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;

		if (G_IsBrushModel(check) && G_IsBreakable(check))
			VectorCenterFromMinsMaxs(check->absmin, check->absmax, center);
		else if (G_IsLivingActor(check) || G_IsBreakable(check))
			VectorCopy(check->origin, center);
		else
			continue;

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

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

		/* check for walls */
		if (G_IsLivingActor(check) && !G_ActorVis(impact, ent, check, false))
			continue;

		/* do damage */
		if (shock)
			damage = 0;
		else
			damage = fd->spldmg[0] * (1.0 - dist / fd->splrad);

		if (mock)
			mock->allow_self = true;
		G_Damage(check, fd, damage, ent, mock, NULL);
		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");
	}
Пример #10
0
/**
 * @brief Applies morale changes to actors around a wounded or killed actor.
 * @note only called when mor_panic is not zero
 * @param[in] type Type of morale modifier (@sa morale_modifiers)
 * @param[in] victim An actor being a victim of the attack.
 * @param[in] attacker An actor being attacker in this attack.
 * @param[in] param Used to modify morale changes, for G_Damage() it is value of damage.
 * @sa G_Damage
 */
static void G_Morale (morale_modifiers type, const Edict* victim, const Edict* attacker, int param)
{
	Actor* actor = nullptr;
	while ((actor = G_EdictsGetNextLivingActor(actor))) {
		/* this only applies to ET_ACTOR but not ET_ACTOR2x2 */
		if (actor->type != ET_ACTOR)
			continue;
		if (G_IsCivilian(actor))
			continue;

		/* morale damage depends on the damage */
		float mod = mob_wound->value * param;
		if (type == ML_SHOOT)
			mod *= mob_shoot->value;
		/* death hurts morale even more than just damage */
		if (type == ML_DEATH)
			mod += mob_death->value;
		/* seeing how someone gets shot increases the morale change */
		if (actor == victim || (G_FrustumVis(actor, victim->origin) && G_ActorVis(actor, victim, false)))
			mod *= mof_watching->value;
		if (attacker != nullptr && actor->isSameTeamAs(attacker)) {
			/* teamkills are considered to be bad form, but won't cause an increased morale boost for the enemy */
			/* morale boost isn't equal to morale loss (it's lower, but morale gets regenerated) */
			if (victim->isSameTeamAs(attacker))
				mod *= mof_teamkill->value;
			else
				mod *= mof_enemy->value;
		}
		/* seeing a civilian die is more "acceptable" */
		if (G_IsCivilian(victim))
			mod *= mof_civilian->value;
		/* if an ally (or in singleplayermode, as human, a civilian) got shot, lower the morale, don't heighten it. */
		if (victim->isSameTeamAs(actor) || (G_IsCivilian(victim) && !G_IsAlien(actor) && G_IsSinglePlayer()))
			mod *= -1;
		if (attacker != nullptr) {
			/* if you stand near to the attacker or the victim, the morale change is higher. */
			mod *= mor_default->value + pow(0.5f, VectorDist(actor->origin, victim->origin) / mor_distance->value)
				* mor_victim->value + pow(0.5f, VectorDist(actor->origin, attacker->origin) / mor_distance->value)
				* mor_attacker->value;
		} else {
			mod *= mor_default->value + pow(0.5f, VectorDist(actor->origin, victim->origin) / mor_distance->value)
				* mor_victim->value;
		}
		/* morale damage depends on the number of living allies */
		mod *= (1 - mon_teamfactor->value)
			+ mon_teamfactor->value * (level.num_spawned[victim->getTeam()] + 1)
			/ (level.num_alive[victim->getTeam()] + 1);
		/* being hit isn't fun */
		if (actor == victim)
			mod *= mor_pain->value;
		/* clamp new morale */
		/*+0.9 to allow weapons like flamethrowers to inflict panic (typecast rounding) */
		const int newMorale = actor->morale + (int) (MORALE_RANDOM(mod) + 0.9);
		if (newMorale > GET_MORALE(actor->chr.score.skills[ABILITY_MIND]))
			actor->setMorale(GET_MORALE(actor->chr.score.skills[ABILITY_MIND]));
		else if (newMorale < 0)
			actor->setMorale(0);
		else
			actor->setMorale(newMorale);

		/* send phys data */
		G_SendStats(*actor);
	}
}
Пример #11
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");
	}