/** * @sa G_MoraleStopPanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoralePanic (edict_t * ent, qboolean sanity) { G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("%s panics!\n"), ent->chr.name); /* drop items in hands */ if (!sanity && ent->chr.teamDef->weapons) { if (RIGHT(ent)) G_ActorInvMove(ent, INVDEF(gi.csi->idRight), RIGHT(ent), INVDEF(gi.csi->idFloor), NONE, NONE, qtrue); if (LEFT(ent)) G_ActorInvMove(ent, INVDEF(gi.csi->idLeft), LEFT(ent), INVDEF(gi.csi->idFloor), NONE, NONE, qtrue); } /* get up */ G_RemoveCrouched(ent); G_ActorSetMaxs(ent); /* send panic */ G_SetPanic(ent); G_EventSendState(G_VisToPM(ent->visflags), ent); /* center view */ G_EventCenterView(ent); /* move around a bit, try to avoid opponents */ AI_ActorThink(G_PLAYER_FROM_ENT(ent), ent); /* kill TUs */ G_ActorSetTU(ent, 0); }
/** * @sa G_MoraleStopPanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoralePanic (Edict* ent) { G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("%s panics!"), ent->chr.name); G_PrintStats("%s panics (entnum %i).", ent->chr.name, ent->getIdNum()); /* drop items in hands */ if (G_IsInsane(ent) && ent->chr.teamDef->weapons) { if (ent->getRightHandItem()) G_ActorInvMove(ent, INVDEF(CID_RIGHT), ent->getRightHandItem(), INVDEF(CID_FLOOR), NONE, NONE, true); if (ent->getLeftHandItem()) G_ActorInvMove(ent, INVDEF(CID_LEFT), ent->getLeftHandItem(), INVDEF(CID_FLOOR), NONE, NONE, true); G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, false); } /* get up */ G_RemoveCrouched(ent); G_ActorSetMaxs(ent); /* send panic */ G_SetPanic(ent); G_EventSendState(G_VisToPM(ent->visflags), *ent); /* center view */ G_EventCenterView(*ent); /* move around a bit, try to avoid opponents */ AI_ActorThink(ent->getPlayer(), ent); /* kill TUs */ G_ActorSetTU(ent, 0); }
/** * @brief Stops the rage state of an actor * @note This is only called when cvar mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopPanic * @sa G_MoraleBehaviour */ static void G_MoraleStopRage (edict_t * ent) { if (ent->morale / mor_panic->value > m_rage_stop->value * frand()) { G_RemoveInsane(ent); G_EventSendState(G_VisToPM(ent->visflags), ent); } else G_MoralePanic(ent, qtrue); /* regains sanity */ }
/** * @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 Stops the panic state of an actor * @note This is only called when cvar mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleStopPanic (Edict *ent) { if (ent->morale / mor_panic->value > m_panic_stop->value * frand()) { G_RemovePanic(ent); G_PrintStats("%s is no longer panicked (entnum %i).", ent->chr.name, ent->number); G_EventSendState(G_VisToPM(ent->visflags), *ent); } else { G_MoralePanic(ent, true); } }
/** * @brief Stops the rage state of an actor * @note This is only called when cvar mor_panic is not zero * @sa G_MoralePanic * @sa G_MoraleRage * @sa G_MoraleStopPanic * @sa G_MoraleBehaviour */ static void G_MoraleStopRage (Edict *ent) { if (ent->morale / mor_panic->value > m_rage_stop->value * frand()) { G_RemoveInsane(ent); G_EventSendState(G_VisToPM(ent->visflags), *ent); G_PrintStats("%s is no longer insane (entnum %i).", ent->chr.name, ent->number); } else { G_MoralePanic(ent, true); /* regains sanity */ } }
/** * @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); } }
/** * @sa G_MoralePanic * @sa G_MoraleStopPanic * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleRage (edict_t * ent, qboolean sanity) { if (sanity) G_SetRage(ent); else G_SetInsane(ent); G_EventSendState(G_VisToPM(ent->visflags), ent); if (sanity) gi.BroadcastPrintf(PRINT_HUD, _("%s is on a rampage.\n"), ent->chr.name); else gi.BroadcastPrintf(PRINT_HUD, _("%s is consumed by mad rage!\n"), ent->chr.name); AI_ActorThink(G_PLAYER_FROM_ENT(ent), ent); }
/** * @sa G_MoralePanic * @sa G_MoraleStopPanic * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleRage (Edict* ent) { G_SetRage(ent); if (!G_IsInsane(ent)) { gi.BroadcastPrintf(PRINT_HUD, _("%s is on a rampage!"), ent->chr.name); G_PrintStats("%s is on a rampage (entnum %i).", ent->chr.name, ent->getIdNum()); } else { gi.BroadcastPrintf(PRINT_HUD, _("%s is consumed by mad rage!"), ent->chr.name); G_PrintStats("%s is consumed by mad rage (entnum %i).", ent->chr.name, ent->getIdNum()); } G_EventSendState(G_VisToPM(ent->visflags), *ent); G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, false); AI_ActorThink(ent->getPlayer(), ent); }
/** * @sa G_MoralePanic * @sa G_MoraleStopPanic * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoraleRage (edict_t *ent, bool sanity) { if (sanity) { G_SetRage(ent); gi.BroadcastPrintf(PRINT_HUD, _("%s is on a rampage!"), ent->chr.name); G_PrintStats("%s is on a rampage (entnum %i).", ent->chr.name, ent->number); } else { G_SetInsane(ent); gi.BroadcastPrintf(PRINT_HUD, _("%s is consumed by mad rage!"), ent->chr.name); G_PrintStats("%s is consumed by mad rage (entnum %i).", ent->chr.name, ent->number); } G_EventSendState(G_VisToPM(ent->visflags), ent); AI_ActorThink(G_PLAYER_FROM_ENT(ent), ent); }
/** * @brief After an actor changed his state, he might get visible for other * players. Check the vis here and send the state change to the clients that * are seeing him already. * @param ent The actor edict */ static void G_ClientStateChangeUpdate (edict_t *ent) { /* Send the state change. */ G_EventSendState(G_VisToPM(ent->visflags), ent); /* Check if the player appears/perishes, seen from other teams. */ G_CheckVis(ent); /* Calc new vis for this player. */ G_CheckVisTeamAll(ent->team, 0, ent); /* Send the new TUs. */ G_SendStats(ent); /* End the event. */ G_EventEnd(); }
/** * @brief Updates the reaction fire settings in case something was moved into a hand or from a hand * that would make the current settings invalid * @param[in,out] actor The actor edict to check the settings for * @param[in] fmIdx The fire mode index that should be used for reaction fire * @param[in] hand The hand that should be used for reaction fire * @param[in] od The object/weapon for the reaction fire */ void G_ReactionFireSettingsUpdate (Actor* actor, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t* od) { actor->chr.RFmode.set(hand, fmIdx, od); /* FiremodeSettings */ if (!G_ActorHasWorkingFireModeSet(actor)) { /* Disable reaction fire if no valid firemode was found. */ G_ClientStateChange(actor->getPlayer(), actor, ~STATE_REACTION, false); G_EventReactionFireChange(*actor); G_EventSendState(G_VisToPM(actor->visflags), *actor); return; } G_EventReactionFireChange(*actor); /* If reaction fire is active, update the reserved TUs */ if (actor->isReaction()) { G_ReactionFireSettingsReserveTUs(actor); } }
/** * @brief Deals damage of a give type and amount to a target. * @param[in,out] target What we want to damage. * @param[in] fd The fire definition that defines what type of damage is dealt. * @param[in] damage The value of the damage. * @param[in] attacker The attacker. * @param[in] mock pseudo shooting - only for calculating mock values - nullptr for real shots * @param[in] impact impact location - @c nullptr for splash damage * @return @c true if damage could be dealt (even if it was 0) @c false otherwise * @sa G_SplashDamage * @sa G_TakeDamage * @sa G_PrintActorStats */ static bool G_Damage (Edict* target, const fireDef_t* fd, int damage, Actor* attacker, shot_mock_t* mock, const vec3_t impact) { assert(target); const bool stunEl = (fd->obj->dmgtype == gi.csi->damStunElectro); const bool stunGas = (fd->obj->dmgtype == gi.csi->damStunGas); const bool shock = (fd->obj->dmgtype == gi.csi->damShock); const bool smoke = (fd->obj->dmgtype == gi.csi->damSmoke); /* Breakables */ if (G_IsBrushModel(target) && G_IsBreakable(target)) { /* Breakables are immune to stun & shock damage. */ if (stunEl || stunGas || shock || mock || smoke) return false; if (damage >= target->HP) { /* don't reset the HP value here, this value is used to distinguish * between triggered destroy and a shoot */ assert(target->destroy); target->destroy(target); /* maybe the attacker is seeing something new? */ G_CheckVisTeamAll(attacker->getTeam(), 0, attacker); /* check if attacker appears/perishes for any other team */ G_CheckVis(attacker); } else { G_TakeDamage(target, damage); } return true; } /* Actors don't die again. */ if (!G_IsLivingActor(target)) return false; /* Now we know that the target is an actor */ Actor* victim = makeActor(target); /* only actors after this point - and they must have a teamdef */ assert(victim->chr.teamDef); const bool isRobot = CHRSH_IsTeamDefRobot(victim->chr.teamDef); /* Apply armour effects. */ if (damage > 0) { damage = G_ApplyProtection(victim, fd->dmgweight, damage); } else if (damage < 0) { /* Robots can't be healed. */ if (isRobot) return false; } Com_DPrintf(DEBUG_GAME, " Total damage: %d\n", damage); /* Apply difficulty settings. */ if (G_IsSinglePlayer()) { if (G_IsAlien(attacker) && !G_IsAlien(victim)) damage *= pow(1.18f, g_difficulty->value); else if (!G_IsAlien(attacker) && G_IsAlien(victim)) damage *= pow(1.18f, -g_difficulty->value); } assert(attacker->getTeam() >= 0 && attacker->getTeam() < MAX_TEAMS); assert(victim->getTeam() >= 0 && victim->getTeam() < MAX_TEAMS); if ((g_nodamage != nullptr && !g_nodamage->integer) || mock) { /* hit */ if (mock) { G_UpdateShotMock(mock, attacker, victim, damage); } else if (stunEl) { victim->addStun(damage); } else if (stunGas) { if (!isRobot) /* Can't stun robots with gas */ victim->addStun(damage); } else if (shock) { /* Only do this if it's not one from our own team ... they should have known that there was a flashbang coming. */ if (!isRobot && !victim->isSameTeamAs(attacker)) { /** @todo there should be a possible protection, too */ /* dazed entity wont reaction fire */ victim->removeReaction(); G_ActorReserveTUs(victim, 0, victim->chr.reservedTus.shot, victim->chr.reservedTus.crouch); /* flashbangs kill TUs */ G_ActorSetTU(victim, 0); G_SendStats(*victim); /* entity is dazed */ victim->setDazed(); G_EventSendState(G_VisToPM(victim->visflags), *victim); return !mock; } else { return false; } } else { if (damage < 0) { /* The 'attacker' is healing the victim. */ G_TreatActor(victim, fd, damage, attacker->getTeam()); } else { /* Real damage was dealt. */ G_DamageActor(victim, damage, impact); /* Update overall splash damage for stats/score. */ if (!mock && damage > 0 && fd->splrad) /**< Check for >0 and splrad to not count this as direct hit. */ G_UpdateHitScore(attacker, victim, fd, damage); } } } if (mock) return false; G_CheckDeathOrKnockout(victim, attacker, fd, damage); return true; }