From 4d58b07a110f6bce046342d232514db3e75e63f9 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:12:46 -0700 Subject: [PATCH 1/2] [core] Add lua binding setter for mob TH level --- scripts/specs/core/CBaseEntity.lua | 6 ++++++ src/map/lua/lua_baseentity.cpp | 16 ++++++++++++++++ src/map/lua/lua_baseentity.h | 1 + 3 files changed, 23 insertions(+) diff --git a/scripts/specs/core/CBaseEntity.lua b/scripts/specs/core/CBaseEntity.lua index 4acab1a82b2..b044984d8d5 100644 --- a/scripts/specs/core/CBaseEntity.lua +++ b/scripts/specs/core/CBaseEntity.lua @@ -3938,6 +3938,12 @@ end function CBaseEntity:getTHlevel() end +---@nodiscard +---@param newLevel integer +---@return nil +function CBaseEntity:setTHlevel(newLevel) +end + ---@nodiscard ---@return integer function CBaseEntity:getAvailableTraverserStones() diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 72cd058fcd7..8b4deaea1c7 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -18040,6 +18040,21 @@ int16 CLuaBaseEntity::getTHlevel() return 0; } +/************************************************************************ + * Function: setTHlevel() + * Purpose : set mob's Treasure Hunter tier + * Example : target:setTHlevel(5) + ************************************************************************/ + +void CLuaBaseEntity::setTHlevel(int16 newLevel) +{ + if (m_PBaseEntity->objtype == TYPE_MOB) + { + CMobEntity* PMob = static_cast(m_PBaseEntity); + PMob->m_THLvl = newLevel; + } +} + /************************************************************************ * Function: getAvailableTraverserStones() * Purpose : Returns the number of Traverser Stones available for claim @@ -19427,6 +19442,7 @@ void CLuaBaseEntity::Register() SOL_REGISTER("getDespoilDebuff", CLuaBaseEntity::getDespoilDebuff); SOL_REGISTER("itemStolen", CLuaBaseEntity::itemStolen); SOL_REGISTER("getTHlevel", CLuaBaseEntity::getTHlevel); + SOL_REGISTER("setTHlevel", CLuaBaseEntity::setTHlevel); SOL_REGISTER("getPlayerTriggerAreaInZone", CLuaBaseEntity::getPlayerTriggerAreaInZone); SOL_REGISTER("updateToEntireZone", CLuaBaseEntity::updateToEntireZone); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 6cc863af2d2..a504a96ded7 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -899,6 +899,7 @@ class CLuaBaseEntity uint16 getDespoilDebuff(uint16 itemID); // gets the status effect id to apply to the mob on successful despoil bool itemStolen(); // sets mob's ItemStolen var = true int16 getTHlevel(); // Returns the Monster's current Treasure Hunter Tier + void setTHlevel(int16 newLevel); // Sets the Monster's current Treasure Hunter Tier uint32 getAvailableTraverserStones(); time_t getTraverserEpoch(); From 33ac36f76a64eac6435c0a9e4fe5e87866b3bf07 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:13:32 -0700 Subject: [PATCH 2/2] [lua] [sql] Implement Bounty Shot --- scripts/actions/abilities/bounty_shot.lua | 2 +- scripts/enum/mod.lua | 3 + scripts/enum/msg.lua | 1 + scripts/globals/job_utils/ranger.lua | 76 ++++++++++++++++++++++- sql/abilities.sql | 2 +- sql/item_mods.sql | 6 +- src/map/modifier.h | 2 +- 7 files changed, 86 insertions(+), 6 deletions(-) diff --git a/scripts/actions/abilities/bounty_shot.lua b/scripts/actions/abilities/bounty_shot.lua index 56bee60dfbe..64aa3866d33 100644 --- a/scripts/actions/abilities/bounty_shot.lua +++ b/scripts/actions/abilities/bounty_shot.lua @@ -12,7 +12,7 @@ abilityObject.onAbilityCheck = function(player, target, ability) end abilityObject.onUseAbility = function(player, target, ability, action) - -- target:addStatusEffect(xi.effect.BOUNTY_SHOT, 11, 1, 30) -- TODO: implement xi.effect.BOUNTY_SHOT + return xi.job_utils.ranger.useBountyShot(player, target, ability, action) end return abilityObject diff --git a/scripts/enum/mod.lua b/scripts/enum/mod.lua index d38194270ab..0d1855ca1bf 100644 --- a/scripts/enum/mod.lua +++ b/scripts/enum/mod.lua @@ -447,6 +447,9 @@ xi.mod = REPRISAL_SPIKES_BONUS = 1068, -- Increases Reprisal spikes damage by percentage (e.g. mod value of 50 will increase spikes damage by 50%) SHIELD_BARRIER = 1082, -- Grants a bonus to Protect spells cast by self while a shield is equipped. + -- Ranger + BOUNTY_SHOT_TH_BONUS = 826, -- Boosts base TH level of bounty shot + -- Dark Knight ARCANE_CIRCLE_DURATION = 858, -- Arcane Circle extended duration in seconds ARCANE_CIRCLE_POTENCY = 1069, -- Increases the potency of the Arcane Circle effect (e.g. mod value 2 = +2% Arcana Killer) diff --git a/scripts/enum/msg.lua b/scripts/enum/msg.lua index a98c3bc83f0..5c1b7103594 100644 --- a/scripts/enum/msg.lua +++ b/scripts/enum/msg.lua @@ -161,6 +161,7 @@ xi.msg.basic = STATUS_BOOST_2 = 365, -- All of 's status parameters are boosted. JA_MAGIC_BURST = 379, -- uses . Magic Burst! the takes damage. JA_ENMITY_DECREASE = 743, -- uses . 's enmity decreases. + JA_TH_EFFECTIVENESS = 608, -- uses . Treasure Hunter effectiveness against increases to . -- "Fortified against" messages FORTIFIED_DEMONS = 149, -- is fortified against demons. diff --git a/scripts/globals/job_utils/ranger.lua b/scripts/globals/job_utils/ranger.lua index 948eaa07193..47025219aed 100644 --- a/scripts/globals/job_utils/ranger.lua +++ b/scripts/globals/job_utils/ranger.lua @@ -88,7 +88,20 @@ xi.job_utils.ranger.checkDoubleShot = function(player, target, ability) end xi.job_utils.ranger.checkBountyShot = function(player, target, ability) - return 0, 0 + if target:getObjType() ~= xi.objType.MOB then + return xi.msg.basic.CANNOT_ATTACK_TARGET, 0 + end + + if + (player:getWeaponSkillType(xi.slot.RANGED) == xi.skill.MARKSMANSHIP and + player:getWeaponSkillType(xi.slot.AMMO) == xi.skill.MARKSMANSHIP) or + (player:getWeaponSkillType(xi.slot.RANGED) == xi.skill.ARCHERY and + player:getWeaponSkillType(xi.slot.AMMO) == xi.skill.ARCHERY) + then + return 0, 0 + end + + return xi.msg.basic.NO_RANGED_WEAPON, 0 end xi.job_utils.ranger.checkDecoyShot = function(player, target, ability) @@ -261,7 +274,66 @@ xi.job_utils.ranger.useDoubleShot = function(player, target, ability, action) end xi.job_utils.ranger.useBountyShot = function(player, target, ability, action) - return 0, 0 -- Not implemented yet + local mobTHLevel = target:getTHlevel() + local bountyShotTHLevel = 2 + player:getMod(xi.mod.BOUNTY_SHOT_TH_BONUS) + local playerTHLevel = player:getMod(xi.mod.TREASURE_HUNTER) + local newTHLevel = 0 + + player:removeAmmo() + action:speceffect(target:getID(), 0x01) -- functional, animation not correct without this + ability:setMsg(xi.msg.basic.JA_NO_EFFECT_2) + + target:updateClaim(player) + + -- pre-apply up to max value of TH4 + if mobTHLevel < 4 and playerTHLevel > mobTHLevel then + newTHLevel = math.min(4, playerTHLevel) + + target:setTHlevel(newTHLevel) + + mobTHLevel = newTHLevel + end + + -- 100% success rate if bounty shot level is higher than their TH level + if bountyShotTHLevel > mobTHLevel then + ability:setMsg(xi.msg.basic.JA_TH_EFFECTIVENESS) + target:setTHlevel(bountyShotTHLevel) + + return bountyShotTHLevel + end + + -- https://www.bg-wiki.com/ffxi/Bounty_Shot + -- https://wiki.ffo.jp/html/22203.html + if mobTHLevel < 12 + player:getMod(xi.mod.TREASURE_HUNTER_CAP) then + local treausureHunterLevelDiff = mobTHLevel - bountyShotTHLevel + + -- TODO: this rate is the same as THF treasure hunter procs. It is unclear if this has the same rate or better than THF auto attacks. + -- This also assumes proc rate bonus works on Bounty Shot, but without mountains of data I wouldn't be able to tell. + -- JP wiki implies these rates and functionality is the same as THF, but there's no data. + -- BG wiki claims proc rates are similar to SA + TA procs, which seems likely given the 1 min timer on bounty shot. + local procRate = 0.10 / math.pow(2, treausureHunterLevelDiff) + local procRateBonus = 1.0 + (target:getMod(xi.mod.TREASURE_HUNTER_PROC) + player:getMod(xi.mod.TREASURE_HUNTER_PROC)) / 100 + + if math.random() < procRate * procRateBonus then + newTHLevel = mobTHLevel + 1 + + ability:setMsg(xi.msg.basic.JA_TH_EFFECTIVENESS) + + target:setTHlevel(newTHLevel) + + return newTHLevel + end + end + + -- If we got here, TH was upgraded to 3 or 4 from gear + -- JP wiki indicates this doesn't happen, but printing incorrectly that the action didn't boost TH level seems weird + if newTHLevel > 0 then + ability:setMsg(xi.msg.basic.JA_TH_EFFECTIVENESS) + + return newTHLevel + end + + return 0 end xi.job_utils.ranger.useDecoyShot = function(player, target, ability, action) diff --git a/sql/abilities.sql b/sql/abilities.sql index d933ccf2777..1bef30e5c76 100644 --- a/sql/abilities.sql +++ b/sql/abilities.sql @@ -298,7 +298,7 @@ INSERT INTO `abilities` VALUES (280,'scarlet_delirium',8,95,1,90,44,100,0,250,20 -- INSERT INTO `abilities` VALUES (282,'run_wild',9,93,1,900,46,100,0,255,2000,0,6,20.0,0,0,0,0,0,NULL); -- needs animation INSERT INTO `abilities` VALUES (283,'tenuto',10,83,1,5,47,0,0,257,2000,0,6,20.0,0,0,0,0,0,'ABYSSEA'); INSERT INTO `abilities` VALUES (284,'marcato',10,95,1,600,48,0,0,251,2000,0,6,20.0,0,0,0,0,0,'ABYSSEA'); -INSERT INTO `abilities` VALUES (285,'bounty_shot',11,87,4,60,51,100,0,261,2000,0,6,20.0,0,0,0,0,0,NULL); +INSERT INTO `abilities` VALUES (285,'bounty_shot',11,87,4,60,51,100,0,189,2000,0,3,21.0,0,0,0,0,0,NULL); INSERT INTO `abilities` VALUES (286,'decoy_shot',11,95,4,300,52,100,0,261,2000,0,6,20.0,0,0,0,0,0,NULL); -- needs animation INSERT INTO `abilities` VALUES (287,'hamanoha',12,87,4,300,53,100,0,249,2000,0,6,12.0,0,0,0,0,0,NULL); INSERT INTO `abilities` VALUES (288,'hagakure',12,95,1,180,54,0,0,249,2000,0,6,20.0,0,1,80,0,0,'ABYSSEA'); diff --git a/sql/item_mods.sql b/sql/item_mods.sql index fe29d0d68ed..0e603c17eef 100644 --- a/sql/item_mods.sql +++ b/sql/item_mods.sql @@ -5026,6 +5026,7 @@ INSERT INTO `item_mods` VALUES (11114,1,25); -- DEF: 25 INSERT INTO `item_mods` VALUES (11114,8,10); -- STR: 10 INSERT INTO `item_mods` VALUES (11114,73,7); -- STORETP: 7 INSERT INTO `item_mods` VALUES (11114,104,7); -- ARCHERY: 7 +INSERT INTO `item_mods` VALUES (11114,826,1); -- BOUNTY_SHOT_TH_BONUS: 1 -- Unkai Kote +2 INSERT INTO `item_mods` VALUES (11115,1,31); -- DEF: 31 @@ -5719,6 +5720,7 @@ INSERT INTO `item_mods` VALUES (11214,1,23); -- DEF: 23 INSERT INTO `item_mods` VALUES (11214,8,7); -- STR: 7 INSERT INTO `item_mods` VALUES (11214,73,4); -- STORETP: 4 INSERT INTO `item_mods` VALUES (11214,104,5); -- ARCHERY: 5 +INSERT INTO `item_mods` VALUES (11214,826,1); -- BOUNTY_SHOT_TH_BONUS: 1 -- Unkai Kote +1 INSERT INTO `item_mods` VALUES (11215,1,29); -- DEF: 29 @@ -50761,7 +50763,7 @@ INSERT INTO `item_mods` VALUES (23229,73,10); -- STORETP: 10 INSERT INTO `item_mods` VALUES (23229,104,33); -- ARCHERY: 33 INSERT INTO `item_mods` VALUES (23229,160,-1000); -- DMG: -10% INSERT INTO `item_mods` VALUES (23229,384,500); -- HASTE_GEAR: 5% --- TODO: "Bounty Shot"+3 +INSERT INTO `item_mods` VALUES (23229,826,3); -- BOUNTY_SHOT_TH_BONUS: 3 -- Kasuga Kote +2 INSERT INTO `item_mods` VALUES (23230,1,124); -- DEF: 124 @@ -67829,6 +67831,7 @@ INSERT INTO `item_mods` VALUES (27072,68,12); -- EVA: 12 INSERT INTO `item_mods` VALUES (27072,73,8); -- STORETP: 8 INSERT INTO `item_mods` VALUES (27072,104,18); -- ARCHERY: 18 INSERT INTO `item_mods` VALUES (27072,384,400); -- HASTE_GEAR: 400 +INSERT INTO `item_mods` VALUES (27072,826,1); -- BOUNTY_SHOT_TH_BONUS: 1 -- Amini Glovelettes +1 INSERT INTO `item_mods` VALUES (27073,1,88); -- DEF: 88 @@ -67846,6 +67849,7 @@ INSERT INTO `item_mods` VALUES (27073,68,27); -- EVA: 27 INSERT INTO `item_mods` VALUES (27073,73,9); -- STORETP: 9 INSERT INTO `item_mods` VALUES (27073,104,28); -- ARCHERY: 28 INSERT INTO `item_mods` VALUES (27073,384,500); -- HASTE_GEAR: 500 +INSERT INTO `item_mods` VALUES (27073,826,2); -- BOUNTY_SHOT_TH_BONUS: 2 -- Kasuga Kote INSERT INTO `item_mods` VALUES (27074,1,77); -- DEF: 77 diff --git a/src/map/modifier.h b/src/map/modifier.h index 40a7ecbd6aa..5bad9b72a0e 100644 --- a/src/map/modifier.h +++ b/src/map/modifier.h @@ -540,6 +540,7 @@ enum class Mod SHARPSHOT = 314, // TRUE_SHOT_EFFECT = 1053, // TODO: True Shot Ranged Damage increase (percent) DEAD_AIM_EFFECT = 1054, // TODO: Dead Aim Critical Damage increase (percent) + BOUNTY_SHOT_TH_BONUS = 826, // Boosts base TH level of bounty shot // Samurai WARDING_CIRCLE_DURATION = 95, // Warding Circle extended duration in seconds @@ -1072,7 +1073,6 @@ enum class Mod // The spares take care of finding the next ID to use so long as we don't forget to list IDs that have been freed up by refactoring. // 570 through 825 used by WS DMG mods these are not spares. // - // SPARE ID: 826 // SPARE IDs: 1132 and onward };