/** * @brief Applies morale behaviour on actors * @note only called when mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleStopPanic */ void G_MoraleBehaviour (int team) { edict_t *ent = NULL; int newMorale; while ((ent = G_EdictsGetNextInUse(ent))) { /* this only applies to ET_ACTOR but not to ET_ACTOR2x2 */ if (ent->type == ET_ACTOR && ent->team == team && !G_IsDead(ent)) { /* civilians have a 1:1 chance to randomly run away in multiplayer */ if (sv_maxclients->integer >= 2 && level.activeTeam == TEAM_CIVILIAN && 0.5 > frand()) G_MoralePanic(ent, qfalse); /* multiplayer needs enabled sv_enablemorale */ /* singleplayer has this in every case */ if (G_IsMoraleEnabled()) { /* if panic, determine what kind of panic happens: */ if (ent->morale <= mor_panic->value && !G_IsPaniced(ent) && !G_IsRaged(ent)) { qboolean sanity; if ((float) ent->morale / mor_panic->value > (m_sanity->value * frand())) sanity = qtrue; else sanity = qfalse; if ((float) ent->morale / mor_panic->value > (m_rage->value * frand())) G_MoralePanic(ent, sanity); else G_MoraleRage(ent, sanity); /* if shaken, well .. be shaken; */ } else if (ent->morale <= mor_shaken->value && !G_IsPaniced(ent) && !G_IsRaged(ent)) { /* shaken is later reset along with reaction fire */ G_SetShaken(ent); G_SetState(ent, STATE_REACTION); G_EventSendState(G_VisToPM(ent->visflags), ent); G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("%s is currently shaken.\n"), ent->chr.name); } else { if (G_IsPaniced(ent)) G_MoraleStopPanic(ent); else if (G_IsRaged(ent)) G_MoraleStopRage(ent); } } G_ActorSetMaxs(ent); /* morale-regeneration, capped at max: */ newMorale = ent->morale + MORALE_RANDOM(mor_regeneration->value); if (newMorale > GET_MORALE(ent->chr.score.skills[ABILITY_MIND])) ent->morale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]); else ent->morale = newMorale; /* send phys data and state: */ G_SendStats(ent); gi.EndEvents(); } } }
/** * @brief Applies morale behaviour on actors * @note only called when mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleStopPanic */ void G_MoraleBehaviour (int team) { bool enabled = G_IsMoraleEnabled(team); if (!enabled) return; Edict* ent = nullptr; while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team)) != nullptr) { /* this only applies to ET_ACTOR but not to ET_ACTOR2x2 */ if (ent->type != ET_ACTOR || CHRSH_IsTeamDefRobot(ent->chr.teamDef)) continue; /* if panic, determine what kind of panic happens: */ if (!G_IsPanicked(ent) && !G_IsRaged(ent)) { if (ent->morale <= mor_panic->integer) { const float ratio = (float) ent->morale / mor_panic->value; const bool sanity = ratio > (m_sanity->value * frand()); if (!sanity) G_SetInsane(ent); if (ratio > (m_rage->value * frand())) G_MoralePanic(ent); else G_MoraleRage(ent); /* if shaken, well .. be shaken; */ } else if (ent->morale <= mor_shaken->integer) { /* shaken is later reset along with reaction fire */ G_SetShaken(ent); G_ClientStateChange(ent->getPlayer(), ent, STATE_REACTION, false); G_EventSendState(G_VisToPM(ent->visflags), *ent); G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("%s is currently shaken."), ent->chr.name); G_PrintStats("%s is shaken (entnum %i).", ent->chr.name, ent->getIdNum()); } } else { if (G_IsPanicked(ent)) G_MoraleStopPanic(ent); else if (G_IsRaged(ent)) G_MoraleStopRage(ent); } G_ActorSetMaxs(ent); /* morale-regeneration, capped at max: */ int newMorale = ent->morale + MORALE_RANDOM(mor_regeneration->value); const int maxMorale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]); if (newMorale > maxMorale) ent->morale = maxMorale; else ent->morale = newMorale; /* send phys data and state: */ G_SendStats(*ent); } }
/** * @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); } } }
/** * @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); } }