Example #1
0
/**
 * @brief Prints some reaction fire data to the console
 * @param[in] target The target entity
 */
static void G_ReactionFirePrintSituation (Edict* target)
{
	if (!G_IsAlien(target))
		return;

	Com_Printf("Alien %i at %i/%i/%i TU:%i\n", target->number, target->pos[0], target->pos[1], target->pos[2], target->TU);

	Actor* shooter = nullptr;
	/* check all possible shooters */
	while ((shooter = G_EdictsGetNextLivingActor(shooter))) {
		if (G_IsAlien(shooter) || G_IsCivilian(shooter))
			continue;
		char msgHdr[100];
		Com_sprintf(msgHdr, sizeof(msgHdr), "S%i: at %i/%i/%i RF: ", shooter->number, shooter->pos[0], shooter->pos[1], shooter->pos[2]);
		int ttus = rft.getTriggerTUs(shooter, target);
		if (ttus == -2)
			Com_Printf("%s not initialized\n", msgHdr);
		if (ttus == -1)
			Com_Printf("%s not aiming\n", msgHdr);
		else if (rft.hasExpired(shooter, target, 0))
			Com_Printf("expired\n", msgHdr);
		else
			Com_Printf("%s not yet: %i\n", msgHdr, ttus);
	}
}
Example #2
0
/**
 * @brief Remove a reaction fire target for the given shooter.
 * @param[in] shooter The reaction firing actor
 * @param[in] target The potential reaction fire victim
 */
void ReactionFireTargets::remove (const Edict* shooter, const Edict* target)
{
	ReactionFireTargetList* rfts = find(shooter);

	assert(rfts);
	assert(target);

	for (int i = 0; i < rfts->count; i++) {
		ReactionFireTarget& t = rfts->targets[i];
		if (t.target != target)
			continue;

		/* not the last one? */
		if (i != rfts->count - 1) {
			t.target = rfts->targets[rfts->count - 1].target;
			t.triggerTUs = rfts->targets[rfts->count - 1].triggerTUs;
		}
		rfts->count--;
		G_EventReactionFireRemoveTarget(*shooter, *target, target->moveinfo.steps - 1);
#if DEBUG_RF
		if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
			Com_Printf("S%i: removed\n", shooter->number);
#endif
	}
}
Example #3
0
/**
 * @brief Check if given actor is an enemy.
 * @param[in] actor The actor that makes the check.
 * @returns @c true if enemies. @c false otherwise
 * @todo Should we really know if the other actor is controlled by the other team (STATE_XVI)?
 * aliens would of course know if an actor is infected (becomes part of the hive mind), but humans?
 */
bool Edict::isOpponent (const Actor* actor) const
{
	const bool actControlled = actor->isState(STATE_XVI);
	const bool selfControlled = G_IsState(this, STATE_XVI);
	if (actor->isSameTeamAs(this))
		return selfControlled ? !actControlled : actControlled;

	bool opponent = true;
	switch (this->getTeam()) {
	/* Aliens: hostile to everyone */
	case TEAM_ALIEN:
		opponent = !actControlled;
		break;
	/* Civilians: Only hostile to aliens */
	case TEAM_CIVILIAN:
		opponent = G_IsAlien(actor) || actControlled;
		break;
	/* PHALANX and MP teams: Hostile to aliens and rival teams
	 * (under AI control while under panic/rage or when g_aihumans is non-zero) */
	default:
		opponent = !G_IsCivilian(actor) || actControlled;
		break;
	}

	return selfControlled ? !opponent : opponent;
}
Example #4
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;
}
Example #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 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;
}
Example #6
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_t *from, const edict_t *check, int 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))
		return false;

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

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

	/* inverse team rules */
	if (team < 0 && (from->team == -team || G_IsCivilian(from) || check->team != -team))
		return false;

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

	if (!G_IsVisibleOnBattlefield(check))
		return false;

	/* view distance check */
	if (VectorDistSqr(from->origin, check->origin) > MAX_SPOT_DIST * MAX_SPOT_DIST)
		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_PARTICLE:
		return !G_LineVis(eye, check->origin);
	default:
		return false;
	}
}
Example #7
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;
}
Example #8
0
/**
 * @brief Add a reaction fire target for the given shooter.
 * @param[in] shooter The reaction firing actor
 * @param[in] target The potential reaction fire victim
 * @param[in] tusForShot The TUs needed for the shot
 */
void ReactionFireTargets::add (const Edict *shooter, const Edict *target, const int tusForShot)
{
	int i;
	ReactionFireTargetList *rfts = find(shooter);

	assert(rfts);
	assert(target);

	for (i = 0; i < rfts->count; i++) {
		if (rfts->targets[i].target == target)	/* found it ? */
			return;								/* shooter already knows that target */
	}
	assert(i < MAX_RF_TARGETS);
	rfts->targets[i].target = target;
	rfts->targets[i].triggerTUs = target->TU - tusForShot;
	rfts->count++;
#if DEBUG_RF
	if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
		Com_Printf("S%i: added\n", shooter->number);
#endif
}
Example #9
0
/**
 * @brief Function to calculate possible damages for mock pseudoaction.
 * @param[in,out] mock Pseudo action - only for calculating mock values - NULL for real action.
 * @param[in] shooter Pointer to attacker for this mock pseudoaction.
 * @param[in] struck Pointer to victim of this mock pseudoaction.
 * @param[in] damage Updates mock value of damage.
 * @note Called only from G_Damage().
 * @sa G_Damage
 */
static void G_UpdateShotMock (shot_mock_t *mock, const edict_t *shooter, const edict_t *struck, int damage)
{
	assert(struck->number != shooter->number || mock->allow_self);

	if (damage > 0) {
		if (!struck->inuse || G_IsDead(struck))
			return;
		else if (!G_IsVisibleForTeam(struck, shooter->team))
			return;
		else if (G_IsCivilian(struck))
			mock->civilian += 1;
		else if (struck->team == shooter->team)
			mock->friendCount += 1;
		else if (G_IsActor(struck))
			mock->enemyCount += 1;
		else
			return;

		mock->damage += damage;
	}
}
Example #10
0
/**
 * @brief Check whether we want to shoot at the target.
 * @param[in] shooter The entity that might be firing
 * @param[in] target The entity that might be fired at
 */
bool ReactionFire::isEnemy (const Actor* shooter, const Edict* target) const
{
	/* an entity can't reaction fire at itself */
	if (shooter == target)
		return false;

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

	if (G_IsDead(target))
		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->isSameTeamAs(shooter))
		if (!shooter->isShaken() || (float) shooter->morale / mor_shaken->value > frand())
			return false;

	return true;
}
Example #11
0
/**
 * @brief Remove a reaction fire target for the given shooter.
 * @param[in] shooter The reaction firing actor
 * @param[in] target The potential reaction fire victim
 */
void ReactionFireTargets::remove (Edict *shooter, const Edict *target)
{
	int i;
	ReactionFireTargetList *rfts = find(shooter);

	assert(rfts);
	assert(target);

	for (i = 0; i < rfts->count; i++) {
		if (rfts->targets[i].target == target) {	/* found it ? */
			if (i != rfts->count - 1) {				/* not the last one ? */
				rfts->targets[i].target = rfts->targets[rfts->count - 1].target;
				rfts->targets[i].triggerTUs = rfts->targets[rfts->count - 1].triggerTUs;
			}
			rfts->count--;
#if DEBUG_RF
			if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
				Com_Printf("S%i: removed\n", shooter->number);
#endif
		}
	}
}
Example #12
0
/**
 * @brief Function to calculate possible damages for mock pseudoaction.
 * @param[in,out] mock Pseudo action - only for calculating mock values - nullptr for real action.
 * @param[in] shooter Pointer to attacker for this mock pseudoaction.
 * @param[in] struck Pointer to victim of this mock pseudoaction.
 * @param[in] damage Updates mock value of damage.
 * @note Called only from G_Damage().
 * @sa G_Damage
 */
static void G_UpdateShotMock (shot_mock_t* mock, const Edict* shooter, const Edict* struck, int damage)
{
	assert(!struck->isSameAs(shooter) || mock->allow_self);

	if (damage <= 0)
		return;

	if (!struck->inuse || G_IsDead(struck))
		return;

	if (!G_IsAI(shooter) && !G_IsVisibleForTeam(struck, shooter->getTeam()))
		return;

	if (G_IsCivilian(struck))
		mock->civilian += 1;
	else if (struck->isSameTeamAs(shooter))
		mock->friendCount += 1;
	else if (G_IsActor(struck))
		mock->enemyCount += 1;
	else
		return;

	mock->damage += damage;
}
Example #13
0
/**
 * @brief Add a reaction fire target for the given shooter.
 * @param[in] shooter The reaction firing actor
 * @param[in] target The potential reaction fire victim
 * @param[in] tusForShot The TUs needed for the shot
 */
void ReactionFireTargets::add (const Edict* shooter, const Edict* target, const int tusForShot)
{
	int i;
	ReactionFireTargetList* rfts = find(shooter);

	assert(rfts);
	assert(target);

	for (i = 0; i < rfts->count; i++) {
		/* check if shooter already knows that target */
		if (rfts->targets[i].target == target)
			return;
	}
	if (i >= MAX_RF_TARGETS)
		return;
	rfts->targets[i].target = target;
	rfts->targets[i].triggerTUs = target->TU - tusForShot;
	rfts->count++;
	G_EventReactionFireAddTarget(*shooter, *target, tusForShot, target->moveinfo.steps - 1);
#if DEBUG_RF
	if (!(G_IsAlien(shooter) || G_IsCivilian(shooter)))
		Com_Printf("S%i: added\n", shooter->number);
#endif
}
Example #14
0
/**
 * @brief Mission trigger
 * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger
 * @note Don't set a client action here - otherwise the movement event might
 * be corrupted
 */
bool G_MissionTouch (Edict* self, Edict* activator)
{
	if (!G_IsLivingActor(activator))
		return false;

	Actor* actor = makeActor(activator);
	const char* const actorTeam = G_MissionGetTeamString(actor->getTeam());
	if (!G_IsCivilian(actor) && self->isOpponent(actor)) {
		if (!self->item && self->count) {
			if (self->targetname) {
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking the %s!"), actorTeam, self->targetname);
			} else {
				const char* const teamName = G_MissionGetTeamString(self->getTeam());
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking %s target zone!"),
						actorTeam, teamName);
			}

			/* reset king of the hill counter */
			self->count = 0;
		}
		return false;
	}
	if (self->count)
		return false;

	if (self->isSameTeamAs(actor)) {
		self->count = level.actualRound;
		if (!self->item) {
			linkedList_t* touched = self->touchedList;
			while (touched) {
				const Edict* const ent = static_cast<const Edict* const>(touched->data);
				if (!self->isSameTeamAs(ent) && !G_IsDead(ent)) {
					return true;
				}
				touched = touched->next;
			}
			if (self->targetname) {
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied the %s!"), actorTeam, self->targetname);
			} else {
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied their target zone!"), actorTeam);
			}
			return true;
		}
	}

	/* search the item in the activator's inventory */
	/* ignore items linked from any temp container the actor must have this in his hands */
	const Container* cont = nullptr;
	while ((cont = actor->chr.inv.getNextCont(cont))) {
		Item* item = nullptr;
		while ((item = cont->getNextItem(item))) {
			const objDef_t* od = item->def();
			/* check whether we found the searched item in the actor's inventory */
			if (!Q_streq(od->id, self->item))
				continue;

			/* drop the weapon - even if out of TUs */
			G_ActorInvMove(actor, cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false);
			if (self->targetname) {
				gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed at the %s."), item->def()->name, self->targetname);
			} else {
				gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed."), item->def()->name);
			}
			self->count = level.actualRound;
			return true;
		}
	}

	return false;
}
Example #15
0
static bool G_VisShouldStop (const edict_t *ent)
{
	return G_IsLivingActor(ent) && !G_IsCivilian(ent);
}
Example #16
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);
	}
}
Example #17
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);
		}
	}
}