diff --git a/scripts/actions/abilities/random_deal.lua b/scripts/actions/abilities/random_deal.lua new file mode 100644 index 00000000000..3d46041127a --- /dev/null +++ b/scripts/actions/abilities/random_deal.lua @@ -0,0 +1,21 @@ +----------------------------------- +-- Ability: Random Deal +-- Has the possibility of resetting the reuse time of a job ability for each party member within area of effect. +-- Obtained: Corsair Level 50 +-- Recast Time: 0:20:00 +-- Duration: Instant +----------------------------------- +local abilityObject = {} + +abilityObject.onAbilityCheck = function(player, target, ability) + return 0, 0 +end + +abilityObject.onUseAbility = function(caster, target, ability, action) + ability:setMsg(xi.msg.basic.JA_RECEIVES_EFFECT_3) + if not caster:doRandomDeal(target) then + ability:setMsg(xi.msg.basic.JA_MISS_2) + end +end + +return abilityObject diff --git a/scripts/enum/mod.lua b/scripts/enum/mod.lua index 4eedfa414ab..6bd72fd7ae4 100644 --- a/scripts/enum/mod.lua +++ b/scripts/enum/mod.lua @@ -759,6 +759,7 @@ xi.mod = DAY_NUKE_BONUS = 565, -- Bonus damage from "Elemental magic affected by day" (Sorc. Tonban) IRIDESCENCE = 566, -- Iridescence trait (additional weather damage/penalty) BARSPELL_AMOUNT = 567, -- Additional elemental resistance granted by bar- spells + RANDOM_DEAL_BONUS = 220, -- % chance to reset 2 abilities BARSPELL_MDEF_BONUS = 827, -- Extra magic defense bonus granted to the bar- spell effect RAPTURE_AMOUNT = 568, -- Bonus amount added to Rapture effect EBULLIENCE_AMOUNT = 569, -- Bonus amount added to Ebullience effect diff --git a/scripts/enum/msg.lua b/scripts/enum/msg.lua index 931496ef4a4..2cec65dc1a3 100644 --- a/scripts/enum/msg.lua +++ b/scripts/enum/msg.lua @@ -148,6 +148,7 @@ xi.msg.basic = JA_REMOVE_EFFECT_2 = 321, -- uses . 's wears off. JA_NO_EFFECT_2 = 323, -- uses . No effect on . (2 line msg) JA_MISS_2 = 324, -- uses , but misses . (includes target name) + JA_RECEIVES_EFFECT_3 = 441, -- receives the effect of . JA_RECOVERS_MP = 451, -- uses . regains MP. JA_ATK_ENHANCED = 285, -- 's attacks are enhanced. STATUS_BOOST = 364, -- uses . All of 's status parameters are boosted. diff --git a/sql/item_mods.sql b/sql/item_mods.sql index 0081d5d4f85..43841c4c2f5 100644 --- a/sql/item_mods.sql +++ b/sql/item_mods.sql @@ -2625,6 +2625,7 @@ INSERT INTO `item_mods` VALUES (10686,8,5); -- STR: 5 INSERT INTO `item_mods` VALUES (10686,11,5); -- AGI: 5 INSERT INTO `item_mods` VALUES (10686,24,12); -- RATT: 12 INSERT INTO `item_mods` VALUES (10686,25,12); -- ACC: 12 +INSERT INTO `item_mods` VALUES (10686,220,5); -- RANDOM_DEAL_BONUS: 5 -- Pantin Tobe +2 INSERT INTO `item_mods` VALUES (10687,1,59); -- DEF: 59 @@ -6157,12 +6158,14 @@ INSERT INTO `item_mods` VALUES (11295,1,45); -- DEF: 45 INSERT INTO `item_mods` VALUES (11295,8,3); -- STR: 3 INSERT INTO `item_mods` VALUES (11295,24,8); -- RATT: 8 INSERT INTO `item_mods` VALUES (11295,25,8); -- ACC: 8 +INSERT INTO `item_mods` VALUES (11295,220,5); -- RANDOM_DEAL_BONUS: 5 -- Commodore Frac +1 INSERT INTO `item_mods` VALUES (11296,1,46); -- DEF: 46 INSERT INTO `item_mods` VALUES (11296,8,3); -- STR: 3 INSERT INTO `item_mods` VALUES (11296,24,10); -- RATT: 10 INSERT INTO `item_mods` VALUES (11296,25,10); -- ACC: 10 +INSERT INTO `item_mods` VALUES (11296,220,5); -- RANDOM_DEAL_BONUS: 5 -- Puppetry Tobe +1 INSERT INTO `item_mods` VALUES (11297,1,37); -- DEF: 37 @@ -48691,7 +48694,7 @@ INSERT INTO `item_mods` VALUES (23146,31,74); -- MEVA: 74 INSERT INTO `item_mods` VALUES (23146,68,59); -- EVA: 59 INSERT INTO `item_mods` VALUES (23146,161,-500); -- DMGPHYS: -5% INSERT INTO `item_mods` VALUES (23146,384,400); -- HASTE_GEAR: 4% --- TODO: "Random Deal" effect +50: Occasionally allows Random Deal to restore two used job abilities. +INSERT INTO `item_mods` VALUES (23146,220,50); -- RANDOM_DEAL_BONUS: 50 -- Pitre Tobe +2 INSERT INTO `item_mods` VALUES (23147,1,143); -- DEF: 143 @@ -64328,6 +64331,7 @@ INSERT INTO `item_mods` VALUES (26832,31,43); -- MEVA: 43 INSERT INTO `item_mods` VALUES (26832,68,23); -- EVA: 23 INSERT INTO `item_mods` VALUES (26832,161,-300); -- DMGPHYS: -300 INSERT INTO `item_mods` VALUES (26832,384,400); -- HASTE_GEAR: 400 +INSERT INTO `item_mods` VALUES (26832,220,50); -- RANDOM_DEAL_BONUS: 50 -- Lanun Frac +1 INSERT INTO `item_mods` VALUES (26833,1,131); -- DEF: 131 @@ -64348,6 +64352,7 @@ INSERT INTO `item_mods` VALUES (26833,31,64); -- MEVA: 64 INSERT INTO `item_mods` VALUES (26833,68,49); -- EVA: 49 INSERT INTO `item_mods` VALUES (26833,161,-400); -- DMGPHYS: -400 INSERT INTO `item_mods` VALUES (26833,384,400); -- HASTE_GEAR: 400 +INSERT INTO `item_mods` VALUES (26833,220,50); -- RANDOM_DEAL_BONUS: 50 -- Pitre Tobe INSERT INTO `item_mods` VALUES (26834,1,94); -- DEF: 94 diff --git a/sql/traits.sql b/sql/traits.sql index d3f18cd473d..ad99733b09d 100644 --- a/sql/traits.sql +++ b/sql/traits.sql @@ -468,8 +468,8 @@ INSERT INTO `traits` VALUES (89,'strafe',14,60,3,986,25,'TOAU',0); INSERT INTO `traits` VALUES (89,'strafe',14,80,4,986,30,'TOAU',0); INSERT INTO `traits` VALUES (90,'enchainment',16,75,1,0,0,'TOAU',0); INSERT INTO `traits` VALUES (91,'assimilation',16,75,1,0,0,'TOAU',0); -INSERT INTO `traits` VALUES (92,'winning streak',17,75,1,0,0,'TOAU',0); -INSERT INTO `traits` VALUES (93,'loaded deck',17,75,1,0,0,'TOAU',0); +INSERT INTO `traits` VALUES (92,'winning streak',17,75,1,0,0,'TOAU',3076); +INSERT INTO `traits` VALUES (93,'loaded deck',17,75,1,0,0,'TOAU',3078); INSERT INTO `traits` VALUES (94,'fine-tuning',18,75,1,0,0,'TOAU',3140); INSERT INTO `traits` VALUES (95,'optimization',18,75,1,0,0,'TOAU',3142); INSERT INTO `traits` VALUES (96,'closed position',19,75,1,0,0,'WOTG',3206); diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 561af1401bf..f631af59a76 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -13063,6 +13063,29 @@ void CLuaBaseEntity::doWildCard(CLuaBaseEntity* PEntity, uint8 total) battleutils::DoWildCardToEntity(static_cast(m_PBaseEntity), static_cast(PEntity->m_PBaseEntity), total); } +/************************************************************************ + * Function: doRandomDeal() + * Purpose : Executes the Random Deal job ability + * Example : player:doRandomDeal(target) + * Notes : Calls the DoRandomDealToEntity function of battleutils + ************************************************************************/ +bool CLuaBaseEntity::doRandomDeal(CLuaBaseEntity* PTarget) +{ + if (m_PBaseEntity->objtype != TYPE_PC) + { + ShowWarning("Invalid entity type calling function (%s).", m_PBaseEntity->getName()); + return false; + } + + if (!PTarget || !PTarget->m_PBaseEntity) + { + ShowWarning("Invalid entity type passed as target (%s).", m_PBaseEntity->getName()); + return false; + } + + return battleutils::DoRandomDealToEntity(static_cast(m_PBaseEntity), static_cast(PTarget->m_PBaseEntity)); +} + /************************************************************************ * Function: addCorsairRoll() * Purpose : Adds the Corsair Roll to the Target's Status Effect Container @@ -17815,6 +17838,7 @@ void CLuaBaseEntity::Register() SOL_REGISTER("fold", CLuaBaseEntity::fold); SOL_REGISTER("doWildCard", CLuaBaseEntity::doWildCard); + SOL_REGISTER("doRandomDeal", CLuaBaseEntity::doRandomDeal); SOL_REGISTER("addCorsairRoll", CLuaBaseEntity::addCorsairRoll); SOL_REGISTER("hasCorsairEffect", CLuaBaseEntity::hasCorsairEffect); SOL_REGISTER("hasBustEffect", CLuaBaseEntity::hasBustEffect); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 4cb8e7240b0..87f3bf2bf31 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -660,6 +660,7 @@ class CLuaBaseEntity void fold(); void doWildCard(CLuaBaseEntity* PEntity, uint8 total); + bool doRandomDeal(CLuaBaseEntity* PTarget); bool addCorsairRoll(uint8 casterJob, uint8 bustDuration, uint16 effectID, uint16 power, uint32 tick, uint32 duration, sol::object const& arg6, sol::object const& arg7, sol::object const& arg8); bool hasCorsairEffect(); diff --git a/src/map/modifier.h b/src/map/modifier.h index db1d2588787..b6dda0aa66f 100644 --- a/src/map/modifier.h +++ b/src/map/modifier.h @@ -601,6 +601,7 @@ enum class Mod EXP_BONUS = 382, // ROLL_RANGE = 528, // Additional range for COR roll abilities. JOB_BONUS_CHANCE = 542, // Chance to apply job bonus to COR roll without having the job in the party. + RANDOM_DEAL_BONUS = 220, // % chance to reset 2 abilities TRIPLE_SHOT_RATE = 999, // Percent increase to Triple Shot Rate QUICK_DRAW_RECAST = 1060, // Quick Draw Charge Reduction (seconds) @@ -1012,7 +1013,7 @@ enum class Mod // 570 through 825 used by WS DMG mods these are not spares. // // SPARE IDs: - // 220 to 222 + // 221 to 222 // 274 to 276 // // SPARE = 1082 and onward diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index 7228441f1e7..11cae619c6e 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -63,6 +63,7 @@ #include "modifier.h" #include "notoriety_container.h" #include "packets/char_abilities.h" +#include "packets/char_recast.h" #include "packets/char_sync.h" #include "packets/lock_on.h" #include "packets/pet_sync.h" @@ -5864,6 +5865,106 @@ namespace battleutils } } + /************************************************************************ + * * + * Does the random deal effect to a specific character (reset ability) * + * * + ************************************************************************/ + bool DoRandomDealToEntity(CCharEntity* PChar, CCharEntity* PTarget) + { + std::vector resetCandidateList; + std::vector activeCooldownList; + + if (PChar == nullptr || PTarget == nullptr) + { + // Invalid User or Target + return false; + } + + RecastList_t* recastList = PTarget->PRecastContainer->GetRecastList(RECAST_ABILITY); + + // Get position of abilites and add to the 2 lists + for (uint8 i = 0; i < recastList->size(); ++i) + { + Recast_t* recast = &recastList->at(i); + + // Do not reset 2hrs or Random Deal + if (recast->ID != 0 && recast->ID != 196) + { + resetCandidateList.push_back(i); + if (recast->RecastTime > 0) + { + activeCooldownList.push_back(i); + } + } + } + + if (resetCandidateList.size() == 0 || activeCooldownList.size() == 0) + { + // Evade because we have no abilities that can be reset + return false; + } + + uint8 loadedDeck = PChar->PMeritPoints->GetMeritValue(MERIT_LOADED_DECK, PChar); + uint8 loadedDeckChance = 50 + loadedDeck; + uint8 resetTwoChance = std::min(PChar->getMod(Mod::RANDOM_DEAL_BONUS), 50); + + if (loadedDeck > 0) // Loaded Deck Merit Version + { + if (activeCooldownList.size() > 1) + { + // Shuffle active cooldowns and take first (loaded deck) + std::shuffle(std::begin(activeCooldownList), std::end(activeCooldownList), xirand::rng()); + loadedDeckChance = 100; + } + + if (loadedDeckChance >= xirand::GetRandomNumber(100)) + { + PTarget->PRecastContainer->DeleteByIndex(RECAST_ABILITY, activeCooldownList.at(0)); + + // Reset 2 abilities by chance + if (activeCooldownList.size() > 1 && resetTwoChance >= xirand::GetRandomNumber(100)) + { + PTarget->PRecastContainer->DeleteByIndex(RECAST_ABILITY, activeCooldownList.at(1)); + } + if (PChar != PTarget) + { + // Update target's recast state; caster's will be handled in CCharEntity::OnAbility. + PTarget->pushPacket(new CCharRecastPacket(PTarget)); + } + return true; + } + + // Evade because we failed to reset with loaded deck + return false; + } + else // Standard Version + { + if (resetCandidateList.size() > 1) + { + // Shuffle if more than 1 ability + std::shuffle(std::begin(resetCandidateList), std::end(resetCandidateList), xirand::rng()); + } + + // Reset first ability (shuffled or only) + PTarget->PRecastContainer->DeleteByIndex(RECAST_ABILITY, resetCandidateList.at(0)); + + // Reset 2 abilities by chance (could be 2 abilitie that don't need resets) + if (resetCandidateList.size() > 1 && activeCooldownList.size() > 1 && resetTwoChance >= xirand::GetRandomNumber(1, 100)) + { + PTarget->PRecastContainer->DeleteByIndex(RECAST_ABILITY, resetCandidateList.at(1)); + } + + if (PChar != PTarget) + { + // Update target's recast state; caster's will be handled in CCharEntity::OnAbility. + PTarget->pushPacket(new CCharRecastPacket(PTarget)); + } + + return true; + } + } + /************************************************************************ * * * Get the Snapshot shot time reduction * diff --git a/src/map/utils/battleutils.h b/src/map/utils/battleutils.h index 7bd16311146..894c9fb44a5 100644 --- a/src/map/utils/battleutils.h +++ b/src/map/utils/battleutils.h @@ -248,8 +248,10 @@ namespace battleutils bool WeatherMatchesElement(WEATHER weather, uint8 element); bool DrawIn(CBattleEntity* PEntity, CMobEntity* PMob, float offset); void DoWildCardToEntity(CCharEntity* PCaster, CCharEntity* PTarget, uint8 roll); - void AddTraits(CBattleEntity* PEntity, TraitList_t* TraitList, uint8 level); - bool HasClaim(CBattleEntity* PEntity, CBattleEntity* PTarget); + bool DoRandomDealToEntity(CCharEntity* PChar, CCharEntity* PTarget); + + void AddTraits(CBattleEntity* PEntity, TraitList_t* TraitList, uint8 level); + bool HasClaim(CBattleEntity* PEntity, CBattleEntity* PTarget); uint32 CalculateSpellCastTime(CBattleEntity*, CMagicState*); uint16 CalculateSpellCost(CBattleEntity*, CSpell*);