/** * @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 - NULL for real shots * @param[in] impact impact location - @c NULL for splash damage * @sa G_SplashDamage * @sa G_TakeDamage * @sa G_PrintActorStats */ static void G_Damage (edict_t *target, const fireDef_t *fd, int damage, edict_t *attacker, shot_mock_t *mock, const vec3_t impact) { 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); bool isRobot; assert(target); /* Breakables */ if (G_IsBrushModel(target) && G_IsBreakable(target)) { /* Breakables are immune to stun & shock damage. */ if (stunEl || stunGas || shock || mock || smoke) return; 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->team, 0, attacker); /* check if attacker appears/perishes for any other team */ G_CheckVis(attacker); } else { G_TakeDamage(target, damage); } return; } /* Actors don't die again. */ if (!G_IsLivingActor(target)) return; /* only actors after this point - and they must have a teamdef */ assert(target->chr.teamDef); isRobot = CHRSH_IsTeamDefRobot(target->chr.teamDef); /* Apply armour effects. */ if (damage > 0) { damage = G_ApplyProtection(target, fd->dmgweight, damage); } else if (damage < 0) { /* Robots can't be healed. */ if (isRobot) return; } Com_DPrintf(DEBUG_GAME, " Total damage: %d\n", damage); /* Apply difficulty settings. */ if (sv_maxclients->integer == 1) { if (G_IsAlien(attacker) && !G_IsAlien(target)) damage *= pow(1.18, g_difficulty->value); else if (!G_IsAlien(attacker) && G_IsAlien(target)) damage *= pow(1.18, -g_difficulty->value); } assert(attacker->team >= 0 && attacker->team < MAX_TEAMS); assert(target->team >= 0 && target->team < MAX_TEAMS); if (g_nodamage != NULL && !g_nodamage->integer) { /* hit */ if (mock) { G_UpdateShotMock(mock, attacker, target, damage); } else if (stunEl) { target->STUN += damage; } else if (stunGas) { if (!isRobot) /* Can't stun robots with gas */ target->STUN += 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 && target->team != attacker->team) { /** @todo there should be a possible protection, too */ /* dazed entity wont reaction fire */ G_RemoveReaction(target); G_ActorReserveTUs(target, 0, target->chr.reservedTus.shot, target->chr.reservedTus.crouch); /* flashbangs kill TUs */ G_ActorSetTU(target, 0); G_SendStats(target); /* entity is dazed */ G_SetDazed(target); G_ClientPrintf(G_PLAYER_FROM_ENT(target), PRINT_HUD, _("Soldier is dazed!\nEnemy used flashbang!")); return; } } else { if (damage < 0) { /* The 'attacker' is healing the target. */ G_TreatActor(target, fd, damage, attacker->team); } else { /* Real damage was dealt. */ G_DamageActor(target, 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, target, fd, damage); } } } if (mock) return; G_CheckDeathOrKnockout(target, attacker, fd, damage); }
/** * @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; }