From 06645ab9ababc70670e62cdb98e0bde0e6dd128e Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:13:53 -0700 Subject: [PATCH 1/3] [core] Implement SA/TA th proc bonuses --- src/map/attack.cpp | 20 ++++++++++++++------ src/map/attack.h | 4 ++++ src/map/entities/battleentity.cpp | 2 +- src/map/utils/battleutils.cpp | 28 ++++++++++++++++++++++++---- src/map/utils/battleutils.h | 3 ++- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/map/attack.cpp b/src/map/attack.cpp index 06b4fd82e5f..140a8eeffec 100644 --- a/src/map/attack.cpp +++ b/src/map/attack.cpp @@ -461,6 +461,16 @@ bool CAttack::CheckAnticipated() } } +bool CAttack::CheckHadSneakAttack() const +{ + return m_isSA; +} + +bool CAttack::CheckHadTrickAttack() const +{ + return m_isTA; +} + bool CAttack::IsCountered() const { return m_isCountered; @@ -563,22 +573,20 @@ bool CAttack::CheckCover() ************************************************************************/ void CAttack::ProcessDamage() { - auto saDmgBonus = false; - auto taDmgBonus = false; // Sneak attack. if (m_attacker->GetMJob() == JOB_THF && m_isFirstSwing && m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_SNEAK_ATTACK) && (behind(m_attacker->loc.p, m_victim->loc.p, 64) || m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_HIDE) || m_victim->StatusEffectContainer->HasStatusEffect(EFFECT_DOUBT))) { m_bonusBasePhysicalDamage += m_attacker->DEX() * (1.0f + m_attacker->getMod(Mod::SNEAK_ATK_DEX) / 100.0f); - saDmgBonus = true; + m_isSA = true; } // Trick attack. if (m_attacker->GetMJob() == JOB_THF && m_isFirstSwing && m_attackRound->GetTAEntity() != nullptr) { m_bonusBasePhysicalDamage += m_attacker->AGI() * (1.0f + m_attacker->getMod(Mod::TRICK_ATK_AGI) / 100.0f); - taDmgBonus = true; + m_isTA = true; } // Consume mana @@ -644,13 +652,13 @@ void CAttack::ProcessDamage() attackutils::CheckForDamageMultiplier((CCharEntity*)m_attacker, dynamic_cast(m_attacker->m_Weapons[slot]), m_damage, m_attackType, slot, m_isFirstSwing); // Apply Sneak Attack Augment Mod - if (m_attacker->getMod(Mod::AUGMENTS_SA) > 0 && saDmgBonus && m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_SNEAK_ATTACK)) + if (m_attacker->getMod(Mod::AUGMENTS_SA) > 0 && CheckHadSneakAttack() && m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_SNEAK_ATTACK)) { m_damage += (int32)(m_damage * ((100 + (m_attacker->getMod(Mod::AUGMENTS_SA))) / 100.0f)); } // Apply Trick Attack Augment Mod - if (m_attacker->getMod(Mod::AUGMENTS_TA) > 0 && taDmgBonus && m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_TRICK_ATTACK)) + if (m_attacker->getMod(Mod::AUGMENTS_TA) > 0 && CheckHadTrickAttack() && m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_TRICK_ATTACK)) { m_damage += (int32)(m_damage * ((100 + (m_attacker->getMod(Mod::AUGMENTS_TA))) / 100.0f)); } diff --git a/src/map/attack.h b/src/map/attack.h index 6cbb3fe2dd6..26f43661890 100644 --- a/src/map/attack.h +++ b/src/map/attack.h @@ -92,6 +92,8 @@ class CAttack bool IsAnticipated() const; bool IsDeflected() const; bool CheckAnticipated(); + bool CheckHadSneakAttack() const; + bool CheckHadTrickAttack() const; bool IsCountered() const; bool CheckCounter(); bool IsCovered() const; // Returns the covered flag. @@ -115,6 +117,8 @@ class CAttack bool m_isCountered{ false }; bool m_isCovered{ false }; // Flag: Is someone covering the victim? bool m_anticipated{ false }; + bool m_isSA{ false }; // Attack had a valid SA proc + bool m_isTA{ false }; // Attack had a valid TA proc bool m_isFirstSwing{ false }; // Flag: Is this attack the first swing? float m_damageRatio{ false }; // The damage ratio. int32 m_damage{ 0 }; // The damage for this attack. diff --git a/src/map/entities/battleentity.cpp b/src/map/entities/battleentity.cpp index 8bbf69376df..78dce4c2f72 100644 --- a/src/map/entities/battleentity.cpp +++ b/src/map/entities/battleentity.cpp @@ -2570,7 +2570,7 @@ bool CBattleEntity::OnAttack(CAttackState& state, action_t& action) if ((actionTarget.reaction & REACTION::MISS) == REACTION::NONE && attack.GetAttackType() != PHYSICAL_ATTACK_TYPE::DAKEN) { battleutils::HandleEnspell(this, PTarget, &actionTarget, attack.IsFirstSwing(), (CItemWeapon*)this->m_Weapons[attack.GetWeaponSlot()], - attack.GetDamage()); + attack.GetDamage(), attack); battleutils::HandleSpikesDamage(this, PTarget, &actionTarget, attack.GetDamage()); } diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index 2b845739c34..f36257924f3 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -1121,7 +1121,7 @@ namespace battleutils * * ************************************************************************/ - void HandleEnspell(CBattleEntity* PAttacker, CBattleEntity* PDefender, actionTarget_t* Action, bool isFirstSwing, CItemWeapon* weapon, int32 finaldamage) + void HandleEnspell(CBattleEntity* PAttacker, CBattleEntity* PDefender, actionTarget_t* Action, bool isFirstSwing, CItemWeapon* weapon, int32 finaldamage, CAttack& attack) { CCharEntity* PChar = nullptr; @@ -1156,15 +1156,35 @@ namespace battleutils float procRate = 0.04f / std::pow(2.f, std::max(0, THdiff)); // Numbers below diff of -1 (i.e. TH 10 vs mobs TH8 level) are not known. Assume no extra bonus for now. - // Impossible to tell if SA or TA was used currently. Allegedly it gives a 10x(!) multiplicative bonus. - // TODO: fix that // Not known if Feint and Gifts are multiplicative or additive. Currently assuming additive // The mob has an evasion down from feint that applies this mod. - // The player has job point gifts that apply thsi mod. + // The player has job point gifts that apply this mod. float procRateBonus = 1.f + (PChar->getMod(Mod::TREASURE_HUNTER_PROC) + PMob->getMod(Mod::TREASURE_HUNTER_PROC)) / 100.f; procRate *= procRateBonus; + // It's unlikely that SATA bonus is multiplicative SA * TA bonus -- the rate would be astronomically higher if it was + // Add the two together if they exist + float sneakAttackTrickAttackBonus = 0.f; + + // BG wiki claims 10x bonus for SA + if (attack.CheckHadSneakAttack()) + { + sneakAttackTrickAttackBonus += 10.f; + } + + // BG wiki claims 10x bonus for TA + if (attack.CheckHadTrickAttack()) + { + sneakAttackTrickAttackBonus += 10.f; + } + + // way greater than epsilon just in case... + if (sneakAttackTrickAttackBonus > 1.f) + { + procRateBonus *= sneakAttackTrickAttackBonus; + } + if (xirand::GetRandomNumber(0.f, 1.f) <= procRate) { PMob->m_THLvl++; diff --git a/src/map/utils/battleutils.h b/src/map/utils/battleutils.h index 1108b3d141e..dd7d3807176 100644 --- a/src/map/utils/battleutils.h +++ b/src/map/utils/battleutils.h @@ -32,6 +32,7 @@ #include "entities/battleentity.h" class CAbility; +class CAttack; class CItemWeapon; class CMobSkill; class CPetSkill; @@ -171,7 +172,7 @@ namespace battleutils bool HandleParrySpikesDamage(CBattleEntity* PAttacker, CBattleEntity* PDefender, actionTarget_t* Action, int32 damage); bool HandleSpikesEquip(CBattleEntity* PAttacker, CBattleEntity* PDefender, actionTarget_t* Action, uint8 damage, SUBEFFECT spikesType, uint8 chance); void HandleSpikesStatusEffect(CBattleEntity* PAttacker, CBattleEntity* PDefender, actionTarget_t* Action); - void HandleEnspell(CBattleEntity* PAttacker, CBattleEntity* PDefender, actionTarget_t* Action, bool isFirstSwing, CItemWeapon* weapon, int32 damage); + void HandleEnspell(CBattleEntity* PAttacker, CBattleEntity* PDefender, actionTarget_t* Action, bool isFirstSwing, CItemWeapon* weapon, int32 damage, CAttack& attack); uint8 GetRangedHitRate(CBattleEntity* PAttacker, CBattleEntity* PDefender, bool isBarrage); uint8 GetRangedHitRate(CBattleEntity* PAttacker, CBattleEntity* PDefender, bool isBarrage, int16 accBonus); int32 CalculateEnspellDamage(CBattleEntity* PAttacker, CBattleEntity* PDefender, uint8 Tier, uint8 element); From 260c3c647113224be7b120c666f93cf912889902 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:31:24 -0700 Subject: [PATCH 2/3] [core] [settings] Add setting to disable TH procs --- settings/default/map.lua | 3 + src/map/utils/battleutils.cpp | 107 +++++++++++++++++----------------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/settings/default/map.lua b/settings/default/map.lua index 6c3e97dcee9..dc8179d5cd3 100644 --- a/settings/default/map.lua +++ b/settings/default/map.lua @@ -95,6 +95,9 @@ xi.settings.map = -- Disables ability to equip higher level gear when level cap/sync effect is on player. DISABLE_GEAR_SCALING = false, + -- Disables Treasure Hunter procs (Era behavior wants this true) + DISABLE_TREASURE_HUNTER_PROCS = false, + -- Weaponskill point base (before skillchain) for breaking latent - whole numbers only. retail is 1. WS_POINTS_BASE = 1, diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index f36257924f3..316147a14ad 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -1129,70 +1129,73 @@ namespace battleutils { PChar = dynamic_cast(PAttacker); - // TODO: cleanup lua further so we can handle all this add effect stuff somewhere else - // Treasure hunter takes priority over enspells - if (PChar && - finaldamage > 0 && - isFirstSwing && - PDefender->objtype == TYPE_MOB && - PChar->GetMJob() == JOB_THF && - PChar->hasTrait(TRAITTYPE::TRAIT_TREASURE_HUNTER)) // TH trait as a requirement is assumed, but likely. Could this just be a level 15 check instead? - { - auto PMob = dynamic_cast(PDefender); - if (PMob && PMob->m_THLvl < (12 + PChar->getMod(Mod::TREASURE_HUNTER_CAP))) // TH proc cap is 12 + job gifts - { - int16 playerTH = PChar->getMod(Mod::TREASURE_HUNTER); + if (!settings::get("map.DISABLE_TREASURE_HUNTER_PROCS")) + { + // TODO: cleanup lua further so we can handle all this add effect stuff somewhere else + // Treasure hunter takes priority over enspells + if (PChar && + finaldamage > 0 && + isFirstSwing && + PDefender->objtype == TYPE_MOB && + PChar->GetMJob() == JOB_THF && + PChar->hasTrait(TRAITTYPE::TRAIT_TREASURE_HUNTER)) // TH trait as a requirement is assumed, but likely. Could this just be a level 15 check instead? + { + auto PMob = dynamic_cast(PDefender); + if (PMob && PMob->m_THLvl < (12 + PChar->getMod(Mod::TREASURE_HUNTER_CAP))) // TH proc cap is 12 + job gifts + { + int16 playerTH = PChar->getMod(Mod::TREASURE_HUNTER); - int16 THdiff = PMob->m_THLvl - playerTH; + int16 THdiff = PMob->m_THLvl - playerTH; - // Auto upgrade TH to mod level up to TH8 - if (THdiff < 0 && PMob->m_THLvl < 8) - { - PMob->m_THLvl = std::min(8, playerTH); + // Auto upgrade TH to mod level up to TH8 + if (THdiff < 0 && PMob->m_THLvl < 8) + { + PMob->m_THLvl = std::min(8, playerTH); - // Recalculate diff - THdiff = PMob->m_THLvl - playerTH; - } + // Recalculate diff + THdiff = PMob->m_THLvl - playerTH; + } - float procRate = 0.04f / std::pow(2.f, std::max(0, THdiff)); // Numbers below diff of -1 (i.e. TH 10 vs mobs TH8 level) are not known. Assume no extra bonus for now. + float procRate = 0.04f / std::pow(2.f, std::max(0, THdiff)); // Numbers below diff of -1 (i.e. TH 10 vs mobs TH8 level) are not known. Assume no extra bonus for now. - // Not known if Feint and Gifts are multiplicative or additive. Currently assuming additive - // The mob has an evasion down from feint that applies this mod. - // The player has job point gifts that apply this mod. - float procRateBonus = 1.f + (PChar->getMod(Mod::TREASURE_HUNTER_PROC) + PMob->getMod(Mod::TREASURE_HUNTER_PROC)) / 100.f; + // Not known if Feint and Gifts are multiplicative or additive. Currently assuming additive + // The mob has an evasion down from feint that applies this mod. + // The player has job point gifts that apply this mod. + float procRateBonus = 1.f + (PChar->getMod(Mod::TREASURE_HUNTER_PROC) + PMob->getMod(Mod::TREASURE_HUNTER_PROC)) / 100.f; - procRate *= procRateBonus; + procRate *= procRateBonus; - // It's unlikely that SATA bonus is multiplicative SA * TA bonus -- the rate would be astronomically higher if it was - // Add the two together if they exist - float sneakAttackTrickAttackBonus = 0.f; + // It's unlikely that SATA bonus is multiplicative SA * TA bonus -- the rate would be astronomically higher if it was + // Add the two together if they exist + float sneakAttackTrickAttackBonus = 0.f; - // BG wiki claims 10x bonus for SA - if (attack.CheckHadSneakAttack()) - { - sneakAttackTrickAttackBonus += 10.f; - } + // BG wiki claims 10x bonus for SA + if (attack.CheckHadSneakAttack()) + { + sneakAttackTrickAttackBonus += 10.f; + } - // BG wiki claims 10x bonus for TA - if (attack.CheckHadTrickAttack()) - { - sneakAttackTrickAttackBonus += 10.f; - } + // BG wiki claims 10x bonus for TA + if (attack.CheckHadTrickAttack()) + { + sneakAttackTrickAttackBonus += 10.f; + } - // way greater than epsilon just in case... - if (sneakAttackTrickAttackBonus > 1.f) - { - procRateBonus *= sneakAttackTrickAttackBonus; - } + // way greater than epsilon just in case... + if (sneakAttackTrickAttackBonus > 1.f) + { + procRateBonus *= sneakAttackTrickAttackBonus; + } - if (xirand::GetRandomNumber(0.f, 1.f) <= procRate) - { - PMob->m_THLvl++; + if (xirand::GetRandomNumber(0.f, 1.f) <= procRate) + { + PMob->m_THLvl++; - Action->additionalEffect = SUBEFFECT_LIGHT_DAMAGE; // Looks like enlight, and is reflected in the capture - Action->addEffectMessage = static_cast(MsgStd::TreasureHunterProc); - Action->addEffectParam = PMob->m_THLvl; - return; + Action->additionalEffect = SUBEFFECT_LIGHT_DAMAGE; // Looks like enlight, and is reflected in the capture + Action->addEffectMessage = static_cast(MsgStd::TreasureHunterProc); + Action->addEffectParam = PMob->m_THLvl; + return; + } } } } From f2bd7beb5fa3ab21f43d9b81d75a183a424fb7ce Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:33:38 -0700 Subject: [PATCH 3/3] [core] Fix a few shadowed variables in battleutils::HandleEnspell --- src/map/utils/battleutils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index 316147a14ad..ef19d3038d7 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -1455,7 +1455,7 @@ namespace battleutils if (PAttacker->objtype == TYPE_PC && PAttacker->PParty != nullptr) { - if (auto* PChar = dynamic_cast(PAttacker)) + if (PChar) { // clang-format off PChar->ForPartyWithTrusts([&](CBattleEntity* PMember) @@ -1470,10 +1470,10 @@ namespace battleutils } else if (PAttacker->objtype == TYPE_TRUST) { - if (auto* PChar = dynamic_cast(PAttacker->PMaster)) + if (auto* PMaster = dynamic_cast(PAttacker->PMaster)) { // clang-format off - PChar->ForPartyWithTrusts([&](CBattleEntity* PMember) + PMaster->ForPartyWithTrusts([&](CBattleEntity* PMember) { if (attackerID == PMember->id) {