From ad080a540bddd71cc1f120ae19e9a524bc6f9bbc Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 30 Mar 2026 02:53:24 +0100 Subject: [PATCH 01/11] [trust] August weaponskill/auto attack/daybreak scripts --- scripts/actions/mobskills/alabaster_burst.lua | 28 ++++++++++++ .../actions/mobskills/august_melee_axe.lua | 15 ++++--- .../actions/mobskills/august_melee_bow.lua | 15 ++++--- .../actions/mobskills/august_melee_h2h.lua | 13 ++++-- .../actions/mobskills/august_melee_sword.lua | 17 ++++--- scripts/actions/mobskills/daybreak.lua | 45 +++++++++++++++++++ scripts/actions/mobskills/fulminous_fury.lua | 39 ++++++++++++++++ scripts/actions/mobskills/no_quarter.lua | 26 +++++++++++ scripts/actions/mobskills/noble_frenzy.lua | 22 +++++++++ scripts/actions/mobskills/null_field.lua | 34 ++++++++++++++ scripts/actions/mobskills/tartaric_sigil.lua | 39 ++++++++++++++++ 11 files changed, 273 insertions(+), 20 deletions(-) create mode 100644 scripts/actions/mobskills/alabaster_burst.lua create mode 100644 scripts/actions/mobskills/daybreak.lua create mode 100644 scripts/actions/mobskills/fulminous_fury.lua create mode 100644 scripts/actions/mobskills/no_quarter.lua create mode 100644 scripts/actions/mobskills/noble_frenzy.lua create mode 100644 scripts/actions/mobskills/null_field.lua create mode 100644 scripts/actions/mobskills/tartaric_sigil.lua diff --git a/scripts/actions/mobskills/alabaster_burst.lua b/scripts/actions/mobskills/alabaster_burst.lua new file mode 100644 index 00000000000..8cc0399036e --- /dev/null +++ b/scripts/actions/mobskills/alabaster_burst.lua @@ -0,0 +1,28 @@ +----------------------------------- +-- Alabaster Burst +----------------------------------- +---@type TMobSkill +local mobskillObject = {} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) + local numhits = 2 + local accmod = 10 + local dmgmod = 4 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) + target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING) + + local duration = (skill:getTP() / 100) / 6 -- 2 sec min, 5 sec max + if duration < 2 then + duration = 2 + end + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.FLASH, 1, 0, duration) + + return dmg +end + +return mobskillObject diff --git a/scripts/actions/mobskills/august_melee_axe.lua b/scripts/actions/mobskills/august_melee_axe.lua index 301f18da423..5745ae05b3b 100644 --- a/scripts/actions/mobskills/august_melee_axe.lua +++ b/scripts/actions/mobskills/august_melee_axe.lua @@ -10,12 +10,17 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 1 - local accmod = 1 - local ftp = 1 - local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, ftp, xi.mobskills.physicalTpBonus.NO_EFFECT) - local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) - target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) + local accmod = 10 + local dmgmod = 2 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) + target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING) skill:setMsg(xi.msg.basic.HIT_DMG) + + if info.hitslanded == 0 then + skill:setMsg(xi.msg.basic.HIT_MISS) + end + return dmg end diff --git a/scripts/actions/mobskills/august_melee_bow.lua b/scripts/actions/mobskills/august_melee_bow.lua index 81a6226f0b0..9bc85abc170 100644 --- a/scripts/actions/mobskills/august_melee_bow.lua +++ b/scripts/actions/mobskills/august_melee_bow.lua @@ -10,12 +10,17 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 1 - local accmod = 1 - local ftp = 1 - local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, ftp, xi.mobskills.physicalTpBonus.NO_EFFECT) - local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) - target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) + local accmod = 100 + local dmgmod = 2 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.RANGED, xi.damageType.PIERCING, info.hitslanded) + target:takeDamage(dmg, mob, xi.attackType.RANGED, xi.damageType.PIERCING) skill:setMsg(xi.msg.basic.HIT_DMG) + + if info.hitslanded == 0 then + skill:setMsg(xi.msg.basic.HIT_MISS) + end + return dmg end diff --git a/scripts/actions/mobskills/august_melee_h2h.lua b/scripts/actions/mobskills/august_melee_h2h.lua index 651689e8775..ef65492be32 100644 --- a/scripts/actions/mobskills/august_melee_h2h.lua +++ b/scripts/actions/mobskills/august_melee_h2h.lua @@ -10,12 +10,17 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 1 - local accmod = 1 - local ftp = 1 - local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, ftp, xi.mobskills.physicalTpBonus.NO_EFFECT) - local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) + local accmod = 100 + local dmgmod = 2 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) skill:setMsg(xi.msg.basic.HIT_DMG) + + if info.hitslanded == 0 then + skill:setMsg(xi.msg.basic.HIT_MISS) + end + return dmg end diff --git a/scripts/actions/mobskills/august_melee_sword.lua b/scripts/actions/mobskills/august_melee_sword.lua index 68949fcc4c1..c4416c574a4 100644 --- a/scripts/actions/mobskills/august_melee_sword.lua +++ b/scripts/actions/mobskills/august_melee_sword.lua @@ -9,13 +9,18 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) - local numhits = 1 - local accmod = 1 - local ftp = 1 - local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, ftp, xi.mobskills.physicalTpBonus.NO_EFFECT) - local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) - target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) + local numhits = 2 + local accmod = 100 + local dmgmod = 2 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) + target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING) skill:setMsg(xi.msg.basic.HIT_DMG) + + if info.hitslanded == 0 then + skill:setMsg(xi.msg.basic.HIT_MISS) + end + return dmg end diff --git a/scripts/actions/mobskills/daybreak.lua b/scripts/actions/mobskills/daybreak.lua new file mode 100644 index 00000000000..d74230bf7cd --- /dev/null +++ b/scripts/actions/mobskills/daybreak.lua @@ -0,0 +1,45 @@ +----------------------------------- +-- Daybreak +-- Description: 1 min 30 sec duration, 1 min 30 sec cooldown after either No quater used or daybreak wears off +-- When August's HP drops below 66%, he uses Daybreak if it's available which partially restores some HP and MP, +-- resets his TP, and activates an aura with wings of light +-- Daybreak is a -50% PDT effect, full Erase +-- Daybreak is removed after the use of No Quarter. +-- Type: Magical +----------------------------------- +---@type TMobSkill +local mobskillObject = {} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) + skill:setFinalAnimationSub(5) + local hpHeal = mob:getMainLvl() * 7 + local mpHeal = mob:getMainLvl() * 7 + local params = {} + + params.baseDamage = mob:getMainLvl() + 2 + params.fTP = { 7, 7, 7 } + params.element = xi.element.LIGHT + params.attackType = xi.attackType.MAGICAL + params.damageType = xi.damageType.LIGHT + params.shadowBehavior = xi.mobskills.shadowBehavior.IGNORE_SHADOWS + + local info = xi.mobskills.mobMagicalMove(mob, target, skill, action, params) + + if xi.mobskills.processDamage(mob, target, skill, action, info) then + target:takeDamage(info.damage, mob, info.attackType, info.damageType) + end + + mob:eraseAllStatusEffect() -- erase all negetive effects + mob:addHP(hpHeal) -- restore hp + mob:addMP(mpHeal) -- restore mp + mob:setMod(xi.mod.DMGPHYS, -5000) -- Phyisical damage taken -50% + mob:setTP(0) -- daybreak uses all tp, so set to 0 + + return info.damage +end + +return mobskillObject diff --git a/scripts/actions/mobskills/fulminous_fury.lua b/scripts/actions/mobskills/fulminous_fury.lua new file mode 100644 index 00000000000..fc0dadf51cd --- /dev/null +++ b/scripts/actions/mobskills/fulminous_fury.lua @@ -0,0 +1,39 @@ +----------------------------------- +-- Fulminous Fury +----------------------------------- +---@type TMobSkill +local mobskillObject = {} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) + local params = {} + + params.baseDamage = mob:getMainLvl() * 6 + params.fTP = { 1.25, 1.25, 1.25 } + params.element = xi.element.DARK + params.attackType = xi.attackType.MAGICAL + params.damageType = xi.damageType.DARK + params.shadowBehavior = xi.mobskills.shadowBehavior.WIPE_SHADOWS + + local info = xi.mobskills.mobMagicalMove(mob, target, skill, action, params) + + if xi.mobskills.processDamage(mob, target, skill, action, info) then + target:takeDamage(info.damage, mob, info.attackType, info.damageType) + local duration = (skill:getTP() / 100) / 6 -- 2 sec min, 5 sec max + if duration < 2 then + duration = 2 + end + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.STUN, 1, 0, duration) + end + + if info.damage > 0 then + mob:addTP(134) + end + + return info.damage +end + +return mobskillObject diff --git a/scripts/actions/mobskills/no_quarter.lua b/scripts/actions/mobskills/no_quarter.lua new file mode 100644 index 00000000000..da3d3ce10e2 --- /dev/null +++ b/scripts/actions/mobskills/no_quarter.lua @@ -0,0 +1,26 @@ +----------------------------------- +-- No Quarter +----------------------------------- +---@type TMobSkill +local mobskillObject = {} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) + skill:setFinalAnimationSub(0) + local numhits = 3 + local accmod = 3 + local dmgmod = mob:getWeaponDmg() * 0.35 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) + target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING) + + mob:setMod(xi.mod.DMGPHYS, 0) -- Remove the Phyisical damage taken effect + mob:setLocalVar("DaybreakEndTime", os.time()) + + return dmg +end + +return mobskillObject diff --git a/scripts/actions/mobskills/noble_frenzy.lua b/scripts/actions/mobskills/noble_frenzy.lua new file mode 100644 index 00000000000..79b6b5232d5 --- /dev/null +++ b/scripts/actions/mobskills/noble_frenzy.lua @@ -0,0 +1,22 @@ +----------------------------------- +-- Noble Frenzy +----------------------------------- +---@type TMobSkill +local mobskillObject = {} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) + local numhits = 5 + local accmod = 2 + local dmgmod = 3.5 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.DMG_VARIES, 1.5625, 1.875, 2.50) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) + target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING) + + return dmg +end + +return mobskillObject diff --git a/scripts/actions/mobskills/null_field.lua b/scripts/actions/mobskills/null_field.lua new file mode 100644 index 00000000000..4e0502f6579 --- /dev/null +++ b/scripts/actions/mobskills/null_field.lua @@ -0,0 +1,34 @@ +----------------------------------- +-- Null Field +----------------------------------- +---@type TMobSkill +local mobskillObject = {} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) + local params = {} + + params.baseDamage = mob:getMainLvl() * 4.3 + params.fTP = { 1, 1, 1 } + params.element = xi.element.FIRE + params.attackType = xi.attackType.MAGICAL + params.damageType = xi.damageType.FIRE + params.shadowBehavior = xi.mobskills.shadowBehavior.WIPE_SHADOWS + + local info = xi.mobskills.mobMagicalMove(mob, target, skill, action, params) + + if xi.mobskills.processDamage(mob, target, skill, action, info) then + target:takeDamage(info.damage, mob, info.attackType, info.damageType) + end + + if info.damage > 0 then + mob:addTP(134) + end + + return info.damage +end + +return mobskillObject diff --git a/scripts/actions/mobskills/tartaric_sigil.lua b/scripts/actions/mobskills/tartaric_sigil.lua new file mode 100644 index 00000000000..2437dc21a2c --- /dev/null +++ b/scripts/actions/mobskills/tartaric_sigil.lua @@ -0,0 +1,39 @@ +----------------------------------- +-- Tartaric Sigil +----------------------------------- +---@type TMobSkill +local mobskillObject = {} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) + local params = {} + + params.baseDamage = mob:getMainLvl() * 3 + params.fTP = { 1.25, 1.25, 1.25 } + params.element = xi.element.DARK + params.attackType = xi.attackType.MAGICAL + params.damageType = xi.damageType.DARK + params.shadowBehavior = xi.mobskills.shadowBehavior.WIPE_SHADOWS + + local info = xi.mobskills.mobMagicalMove(mob, target, skill, action, params) + + if xi.mobskills.processDamage(mob, target, skill, action, info) then + target:takeDamage(info.damage, mob, info.attackType, info.damageType) + local duration = (skill:getTP() / 100) / 6 -- 2 sec min, 5 sec max + if duration < 2 then + duration = 2 + end + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.AMNESIA, 1, 0, duration) + end + + if info.damage > 0 then + mob:addTP(134) + end + + return info.damage +end + +return mobskillObject From d925ea5b4928e0dc1badcac8f257b325e954a0bf Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 30 Mar 2026 02:54:46 +0100 Subject: [PATCH 02/11] [trust] gambit changes for August --- scripts/globals/gambits.lua | 106 +++++++++++----------- src/map/ai/helpers/gambits_container.cpp | 107 ++++++++++++++++++++++- src/map/ai/helpers/gambits_container.h | 107 ++++++++++++----------- 3 files changed, 217 insertions(+), 103 deletions(-) diff --git a/scripts/globals/gambits.lua b/scripts/globals/gambits.lua index f860e6abda9..7cf0936bf26 100644 --- a/scripts/globals/gambits.lua +++ b/scripts/globals/gambits.lua @@ -7,18 +7,20 @@ ai = ai or {} -- Target ai.target = { - SELF = 0, - PARTY = 1, - TARGET = 2, - MASTER = 3, - TANK = 4, - MELEE = 5, - RANGED = 6, - CASTER = 7, - TOP_ENMITY = 8, - CURILLA = 9, -- Special case for Rainemard - PARTY_DEAD = 10, - PARTY_MULTI = 11, + SELF = 0, + PARTY = 1, + TARGET = 2, + MASTER = 3, + TANK = 4, + MELEE = 5, + RANGED = 6, + CASTER = 7, + TOP_ENMITY = 8, + CURILLA = 9, -- Special case for Rainemard + PARTY_DEAD = 10, + PARTY_MULTI = 11, + TRIGGER_SELF_ACTION_TARGET = 12, -- Triggers get checked on the trust but the action target is the battleTarget + TRIGGER_TARGET_ACTION_SELF = 13, -- Triggers get checked on the battleTarget but the action target is the trust } ai.t = ai.target @@ -62,32 +64,35 @@ ai.condition = MPP_GTE = 4, TP_LT = 5, TP_GTE = 6, - STATUS = 7, - NOT_STATUS = 8, - STATUS_FLAG = 9, - HAS_TOP_ENMITY = 10, - NOT_HAS_TOP_ENMITY = 11, - SC_AVAILABLE = 12, - NOT_SC_AVAILABLE = 13, - MB_AVAILABLE = 14, - READYING_WS = 15, - READYING_MS = 16, - READYING_JA = 17, - CASTING_MA = 18, - RANDOM = 19, - NO_SAMBA = 20, - NO_STORM = 21, - PT_HAS_TANK = 22, - NOT_PT_HAS_TANK = 23, - IS_ECOSYSTEM = 24, - HP_MISSING = 25, - CASTING_ELEMENT_MA = 26, - CAST_ELE_MA_SELF = 27, - CASTING_ELE_MA_AOE = 28, - NEED_ELE_BAREFFECT = 29, - NO_MAX_RUNE = 30, - HAS_RUNES = 31, - LUNGE_MB_AVAILABLE = 32, + LVL_LT = 7, + LVL_GTE = 8, + STATUS = 9, + NOT_STATUS = 10, + STATUS_FLAG = 11, + HAS_TOP_ENMITY = 12, + NOT_HAS_TOP_ENMITY = 13, + SC_AVAILABLE = 14, + NOT_SC_AVAILABLE = 15, + MB_AVAILABLE = 16, + READYING_WS = 17, + READYING_MS = 18, + READYING_JA = 19, + CASTING_MA = 20, + RANDOM = 21, + NO_SAMBA = 22, + NO_STORM = 23, + PT_HAS_TANK = 24, + NOT_PT_HAS_TANK = 25, + IS_ECOSYSTEM = 26, + HP_MISSING = 27, + CASTING_ELEMENT_MA = 28, + CAST_ELE_MA_SELF = 29, + CASTING_ELE_MA_AOE = 30, + NEED_ELE_BAREFFECT = 31, + NO_MAX_RUNE = 32, + HAS_RUNES = 33, + LUNGE_MB_AVAILABLE = 34, + SUB_ANIMATION = 35, } ai.c = ai.condition @@ -112,18 +117,19 @@ ai.select = RANDOM = 3, MB_ELEMENT = 4, SPECIAL_AYAME = 5, - BEST_AGAINST_TARGET = 6, - BEST_SAMBA = 7, - HIGHEST_WALTZ = 8, - ENTRUSTED = 9, - BEST_INDI = 10, - STORM_DAY = 11, - HELIX_DAY = 12, - EN_MOB_WEAKNESS = 13, - STORM_MOB_WEAKNESS = 14, - HELIX_MOB_WEAKNESS = 15, - DEF_BAR_ELEMENT = 16, - RUNE_DAY = 17, + SPECIAL_AUGUST = 6, + BEST_AGAINST_TARGET = 7, + BEST_SAMBA = 8, + HIGHEST_WALTZ = 9, + ENTRUSTED = 10, + BEST_INDI = 11, + STORM_DAY = 12, + HELIX_DAY = 13, + EN_MOB_WEAKNESS = 14, + STORM_MOB_WEAKNESS = 15, + HELIX_MOB_WEAKNESS = 16, + DEF_BAR_ELEMENT = 17, + RUNE_DAY = 18, } ai.s = ai.select diff --git a/src/map/ai/helpers/gambits_container.cpp b/src/map/ai/helpers/gambits_container.cpp index bbbec4aa123..703b8cccc25 100644 --- a/src/map/ai/helpers/gambits_container.cpp +++ b/src/map/ai/helpers/gambits_container.cpp @@ -37,7 +37,6 @@ #include "ai/controllers/player_controller.h" #include "ai/controllers/trust_controller.h" -#include "weapon_skill.h" #include #include @@ -100,7 +99,7 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task auto* controller = static_cast(POwner->PAI->GetController()); uint8 currentPartyPos = controller->GetPartyPosition(); - auto position_offset = static_cast(currentPartyPos * 10); + auto position_offset = static_cast(currentPartyPos * 100); if ((tick + position_offset) < m_lastAction) { @@ -116,7 +115,7 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task co_return; } - auto random_offset = static_cast(xirand::GetRandomNumber(1000, 2500)); + auto random_offset = static_cast(xirand::GetRandomNumber(2000, 3000)); m_lastAction = tick + random_offset; // Deal with TP skills before any gambits @@ -156,6 +155,15 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task auto* mob = POwner->GetBattleTarget(); potentialTargets.push_back(mob); } + else if (targetType == G_TARGET::TRIGGER_SELF_ACTION_TARGET) + { + potentialTargets.push_back(POwner); + } + else if (targetType == G_TARGET::TRIGGER_TARGET_ACTION_SELF) + { + auto* mob = POwner->GetBattleTarget(); + potentialTargets.push_back(mob); + } else if (targetType == G_TARGET::PARTY) { // clang-format off @@ -287,6 +295,16 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task if (targetMatchAllPredicates) { + if (gambit.target_selector == G_TARGET::TRIGGER_SELF_ACTION_TARGET) + { + target = POwner->GetBattleTarget(); // switch back to target before action for correct target selection in actions + break; + } + else if (gambit.target_selector == G_TARGET::TRIGGER_TARGET_ACTION_SELF) + { + target = POwner; // switch to self before action for correct target selection in actions + break; + } target = potentialTarget; break; } @@ -766,6 +784,16 @@ bool CGambitsContainer::CheckTrigger(const CBattleEntity* triggerTarget, Predica predicateResults.push_back(!triggerTarget->StatusEffectContainer->HasStatusEffect(static_cast(predicate.condition_arg))); continue; } + case G_CONDITION::LVL_LT: + { + predicateResults.push_back(triggerTarget->GetMLevel() < predicate.condition_arg); + continue; + } + case G_CONDITION::LVL_GTE: + { + predicateResults.push_back(triggerTarget->GetMLevel() >= predicate.condition_arg); + continue; + } case G_CONDITION::HAS_RUNES: { bool hasRunes = !triggerTarget->StatusEffectContainer->GetAllRuneEffects().empty(); @@ -1045,6 +1073,11 @@ bool CGambitsContainer::CheckTrigger(const CBattleEntity* triggerTarget, Predica predicateResults.push_back((triggerTarget->health.maxhp - triggerTarget->health.hp) >= (int16)predicate.condition_arg); continue; } + case G_CONDITION::SUB_ANIMATION: + { + predicateResults.push_back(triggerTarget->animationsub == predicate.condition_arg); + continue; + } default: { predicateResults.push_back(false); @@ -1271,6 +1304,74 @@ bool CGambitsContainer::TryTrustSkill() break; } + case G_SELECT::SPECIAL_AUGUST: + { + static const uint32 NO_QUARTER = 3658; + static const std::unordered_set daybreak_ws = { 3656, 3657 }; + static const std::unordered_set regular_ws = { 3653, 3654, 3655 }; + + bool maybeDaybreakActive = POwner->animationsub == 5; // Daybreak active is sub animation 5, retail does the same thing. + uint32 lastSkillUsed = POwner->GetLocalVar("[Gambit]LastDaybreakSkill"); + + std::vector candidates; + + if (maybeDaybreakActive) + { + // Only trigger No Quarter if the last skill used was ACTUALLY a Daybreak opener + if (daybreak_ws.count(lastSkillUsed)) + { + for (auto const& tskill : tp_skills) + { + if (tskill.skill_id == NO_QUARTER) + { + chosen_skill = tskill; + break; + } + } + } + + // If we didn't pick No Quarter (either lastSkill was 0 or a regular skill) + if (!chosen_skill) + { + for (auto const& tskill : tp_skills) + { + if (daybreak_ws.count(tskill.skill_id)) + { + candidates.push_back(tskill); + } + } + } + } + else + { + // Normal state: use standard rotation + for (auto const& tskill : tp_skills) + { + if (regular_ws.count(tskill.skill_id)) + { + candidates.push_back(tskill); + } + } + // Clear the localVar so the next Daybreak starts fresh + if (lastSkillUsed != 0) + { + POwner->SetLocalVar("[Gambit]LastDaybreakSkill", 0); + } + } + + if (!candidates.empty() && !chosen_skill) + { + chosen_skill = xirand::GetRandomElement(candidates); + } + + // Only update the localVar if actually picked a skill + applyTo(chosen_skill, [&](const TrustSkill_t& skill) + { + POwner->SetLocalVar("[Gambit]LastDaybreakSkill", skill.skill_id); + }); + + break; + } default: { break; diff --git a/src/map/ai/helpers/gambits_container.h b/src/map/ai/helpers/gambits_container.h index fc530de9783..8e8fa6d074f 100644 --- a/src/map/ai/helpers/gambits_container.h +++ b/src/map/ai/helpers/gambits_container.h @@ -38,18 +38,20 @@ namespace gambits enum class G_TARGET : uint16 { - SELF = 0, - PARTY = 1, - TARGET = 2, - MASTER = 3, - TANK = 4, - MELEE = 5, - RANGED = 6, - CASTER = 7, - TOP_ENMITY = 8, - CURILLA = 9, // Special case for Rainemard - PARTY_DEAD = 10, - PARTY_MULTI = 11, + SELF = 0, + PARTY = 1, + TARGET = 2, + MASTER = 3, + TANK = 4, + MELEE = 5, + RANGED = 6, + CASTER = 7, + TOP_ENMITY = 8, + CURILLA = 9, // Special case for Rainemard + PARTY_DEAD = 10, + PARTY_MULTI = 11, + TRIGGER_SELF_ACTION_TARGET = 12, + TRIGGER_TARGET_ACTION_SELF = 13, }; enum class G_LOGIC : uint16 @@ -67,32 +69,35 @@ enum class G_CONDITION : uint16 MPP_GTE = 4, TP_LT = 5, TP_GTE = 6, - STATUS = 7, - NOT_STATUS = 8, - STATUS_FLAG = 9, - HAS_TOP_ENMITY = 10, - NOT_HAS_TOP_ENMITY = 11, - SC_AVAILABLE = 12, - NOT_SC_AVAILABLE = 13, - MB_AVAILABLE = 14, - READYING_WS = 15, - READYING_MS = 16, - READYING_JA = 17, - CASTING_MA = 18, - RANDOM = 19, - NO_SAMBA = 20, - NO_STORM = 21, - PT_HAS_TANK = 22, - NOT_PT_HAS_TANK = 23, - IS_ECOSYSTEM = 24, - HP_MISSING = 25, - CASTING_ELEMENT_MA = 26, - CAST_ELE_MA_SELF = 27, - CASTING_ELE_MA_AOE = 28, - NEED_ELE_BAREFFECT = 29, - NO_MAX_RUNE = 30, - HAS_RUNES = 31, - LUNGE_MB_AVAILABLE = 32, + LVL_LT = 7, + LVL_GTE = 8, + STATUS = 9, + NOT_STATUS = 10, + STATUS_FLAG = 11, + HAS_TOP_ENMITY = 12, + NOT_HAS_TOP_ENMITY = 13, + SC_AVAILABLE = 14, + NOT_SC_AVAILABLE = 15, + MB_AVAILABLE = 16, + READYING_WS = 17, + READYING_MS = 18, + READYING_JA = 19, + CASTING_MA = 20, + RANDOM = 21, + NO_SAMBA = 22, + NO_STORM = 23, + PT_HAS_TANK = 24, + NOT_PT_HAS_TANK = 25, + IS_ECOSYSTEM = 26, + HP_MISSING = 27, + CASTING_ELEMENT_MA = 28, + CAST_ELE_MA_SELF = 29, + CASTING_ELE_MA_AOE = 30, + NEED_ELE_BAREFFECT = 31, + NO_MAX_RUNE = 32, + HAS_RUNES = 33, + LUNGE_MB_AVAILABLE = 34, + SUB_ANIMATION = 35, }; enum class G_REACTION : uint16 @@ -113,18 +118,20 @@ enum class G_SELECT : uint16 RANDOM = 3, MB_ELEMENT = 4, SPECIAL_AYAME = 5, - BEST_AGAINST_TARGET = 6, - BEST_SAMBA = 7, - HIGHEST_WALTZ = 8, - ENTRUSTED = 9, - BEST_INDI = 10, - STORM_DAY = 11, - HELIX_DAY = 12, - EN_MOB_WEAKNESS = 13, - STORM_MOB_WEAKNESS = 14, - HELIX_MOB_WEAKNESS = 15, - DEF_BAR_ELEMENT = 16, - RUNE_DAY = 17, + SPECIAL_AUGUST = 6, + BEST_AGAINST_TARGET = 7, + BEST_SAMBA = 8, + HIGHEST_WALTZ = 9, + ENTRUSTED = 10, + BEST_INDI = 11, + STORM_DAY = 12, + HELIX_DAY = 13, + EN_MOB_WEAKNESS = 14, + STORM_MOB_WEAKNESS = 15, + HELIX_MOB_WEAKNESS = 16, + DEF_BAR_ELEMENT = 17, + RUNE_DAY = 18, + }; enum class G_TP_TRIGGER : uint16 From 6f92d45831bc168be6c43273102767cd9ddf32a4 Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 30 Mar 2026 02:58:15 +0100 Subject: [PATCH 03/11] [trust] sql changes for August + fix: Emela-ntouka wrong skillList --- sql/mob_pools.sql | 4 ++-- sql/mob_skill_lists.sql | 9 +++++++-- sql/mob_skills.sql | 17 +++++++++-------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/sql/mob_pools.sql b/sql/mob_pools.sql index 25c631e567f..fd5dbb885b9 100644 --- a/sql/mob_pools.sql +++ b/sql/mob_pools.sql @@ -4762,7 +4762,7 @@ INSERT INTO `mob_pools` VALUES (4702,'Delicieuse_Delphine','Delicieuse_Delphine' INSERT INTO `mob_pools` VALUES (4703,'Yatagarasu','Yatagarasu',125,0x0000530100000000000000000000000000000000,1,1,6,240,100,0,1,1,1,2,0,0,7,159,0,0,0,0,0,125,125,3,64); INSERT INTO `mob_pools` VALUES (4704,'Mellonia','Mellonia',131,0x0000DD0700000000000000000000000000000000,6,6,3,240,100,0,1,1,0,2,0,32,7,671,8,0,0,0,0,61,131,3,13); INSERT INTO `mob_pools` VALUES (4705,'Aqrabuamelu','Aqrabuamelu',402,0x00001C0100000000000000000000000000000000,1,1,6,240,100,0,1,0,1,2,6150,0,1,1157,0,0,0,0,0,402,402,2,32); -INSERT INTO `mob_pools` VALUES (4706,'Emela-ntouka','Emela-ntouka',174,0x0000480100000000000000000000000000000000,6,6,7,230,100,0,1,0,1,2,6150,0,509,1153,0,0,0,0,0,1099,174,0,13); +INSERT INTO `mob_pools` VALUES (4706,'Emela-ntouka','Emela-ntouka',174,0x0000480100000000000000000000000000000000,6,6,7,230,100,0,1,0,1,2,6150,0,509,1153,0,0,0,0,0,1199,174,0,13); INSERT INTO `mob_pools` VALUES (4707,'Ratatoskr','Ratatoskr',206,0x00000D0100000000000000000000000000000000,3,3,7,240,100,0,1,1,0,2,6150,32,0,153,0,0,20,0,0,206,206,0,11); INSERT INTO `mob_pools` VALUES (4708,'Kalasutrax','Kalasutrax',472,0x0000110800000000000000000000000000000000,4,1,10,240,100,0,1,0,0,2,0,32,7,159,0,0,2,0,0,472,472,3,35); INSERT INTO `mob_pools` VALUES (4709,'Rw_Nw_Prt_M_Hrw','Rw_Nw_Prt_M_Hrw',53,0x0000140800000000000000000000000000000000,20,20,11,240,100,0,1,0,1,2,0,0,7,135,16,0,0,0,0,149,53,3,12); @@ -6042,7 +6042,7 @@ INSERT INTO `mob_pools` VALUES (5980,'yoran-oran_uc','Yoran-Oran',153,0x0000170C INSERT INTO `mob_pools` VALUES (5981,'sylvie_uc','Sylvie',149,0x0000180C00000000000000000000000000000000,21,3,3,240,100,0,0,0,0,0,0,32,0,3,0,0,394,0,0,1096,149,0,15); INSERT INTO `mob_pools` VALUES (5982,'abquhbah','Abquhbah',149,0x00001A0C00000000000000000000000000000000,1,0,3,240,100,0,0,0,0,0,0,32,0,3,0,0,0,0,0,1097,149,1,17); INSERT INTO `mob_pools` VALUES (5983,'balamor','Balamor',492,0x00001B0C00000000000000000000000000000000,8,0,3,240,100,0,0,0,0,0,0,32,0,3,0,0,396,0,0,1098,492,0,21); -INSERT INTO `mob_pools` VALUES (5984,'august','August',149,0x00001C0C00000000000000000000000000000000,7,1,3,240,100,0,0,0,0,0,0,32,0,3,0,0,397,0,0,1099,149,0,17); +INSERT INTO `mob_pools` VALUES (5984,'august','August',149,0x00001C0C00000000000000000000000000000000,7,1,3,180,100,0,0,0,0,0,0,32,0,3,0,0,397,0,0,1099,149,0,17); INSERT INTO `mob_pools` VALUES (5985,'rosulatia','Rosulatia',455,0x00001D0C00000000000000000000000000000000,4,0,3,240,100,0,0,0,0,0,0,32,0,3,0,0,398,0,0,1100,455,0,10); INSERT INTO `mob_pools` VALUES (5986,'teodor','Teodor',149,0x00001F0C00000000000000000000000000000000,4,6,3,240,100,0,0,0,0,0,0,32,0,3,0,0,399,0,0,1101,149,0,12); INSERT INTO `mob_pools` VALUES (5987,'ullegore','Ullegore',74,0x0000210C00000000000000000000000000000000,4,0,3,240,100,0,0,0,0,0,0,32,0,3,0,0,400,0,0,1102,74,1,12); diff --git a/sql/mob_skill_lists.sql b/sql/mob_skill_lists.sql index 16a3572683e..87f507f666a 100644 --- a/sql/mob_skill_lists.sql +++ b/sql/mob_skill_lists.sql @@ -3576,7 +3576,12 @@ INSERT INTO `mob_skill_lists` VALUES ('TRUST_Abquhbah',1097,1); -- Combo INSERT INTO `mob_skill_lists` VALUES ('TRUST_Abquhbah',1097,4); -- Backhand Blow INSERT INTO `mob_skill_lists` VALUES ('TRUST_Abquhbah',1097,3541); -- Salaheem Spirit -- INSERT INTO `mob_skill_lists` VALUES ('TRUST_Balamor',1098,0); --- INSERT INTO `mob_skill_lists` VALUES ('TRUST_August',1099,0); +INSERT INTO `mob_skill_lists` VALUES ('TRUST_August',1099,3653); -- Tartaric Sigil +INSERT INTO `mob_skill_lists` VALUES ('TRUST_August',1099,3654); -- Null Field +INSERT INTO `mob_skill_lists` VALUES ('TRUST_August',1099,3655); -- Alabaster Burst +INSERT INTO `mob_skill_lists` VALUES ('TRUST_August',1099,3656); -- Noble Frenzy +INSERT INTO `mob_skill_lists` VALUES ('TRUST_August',1099,3657); -- Fulminous Fury +INSERT INTO `mob_skill_lists` VALUES ('TRUST_August',1099,3658); -- No Quarter -- INSERT INTO `mob_skill_lists` VALUES ('TRUST_Rosulatia',1100,0); -- INSERT INTO `mob_skill_lists` VALUES ('TRUST_Teodor',1101,0); -- INSERT INTO `mob_skill_lists` VALUES ('TRUST_Ullegore',1102,0); @@ -3847,7 +3852,7 @@ INSERT INTO `mob_skill_lists` VALUES ('TRUST_August_Melee',1197,3651); -- august INSERT INTO `mob_skill_lists` VALUES ('Fahrafahr_the_Bloodied',1198,2083); -- drop_hammer -INSERT INTO `mob_skill_lists` VALUES ('Emela-ntouka',1099,368); -- blockhead +INSERT INTO `mob_skill_lists` VALUES ('Emela-ntouka',1199,368); -- blockhead INSERT INTO `mob_skill_lists` VALUES ('Flockbock',2000,269); -- petribreath diff --git a/sql/mob_skills.sql b/sql/mob_skills.sql index c0cc12c7c48..f08efe6b132 100644 --- a/sql/mob_skills.sql +++ b/sql/mob_skills.sql @@ -3676,14 +3676,15 @@ INSERT INTO `mob_skills` VALUES (3647,82,'merciless_strike',0,0.0,7.0,2000,1500, INSERT INTO `mob_skills` VALUES (3648,2457,'august_melee_sword',0,0.0,7.0,2000,0,4,4,0,0,0,0,0); -- Capture shows 409. Adding 2048 for it to work properly. INSERT INTO `mob_skills` VALUES (3649,2458,'august_melee_axe',0,0.0,7.0,2000,0,4,4,0,0,0,0,0); -- Capture shows 410. Adding 2048 for it to work properly. INSERT INTO `mob_skills` VALUES (3650,2459,'august_melee_h2h',0,0.0,7.0,2000,0,4,4,0,0,0,0,0); -- Capture shows 411. Adding 2048 for it to work properly. -INSERT INTO `mob_skills` VALUES (3651,2460,'august_melee_bow',0,0.0,7.0,2000,0,4,4,0,0,0,0,0); -- Capture shows 412. Adding 2048 for it to work properly. -INSERT INTO `mob_skills` VALUES (3652,413,'daybreak',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (3653,414,'tartaric_sigil',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (3654,415,'null_field',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (3655,416,'alabaster_burst',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (3656,417,'noble_frenzy',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (3657,418,'fulminous_fury',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (3658,419,'no_quarter',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (3651,2460,'august_melee_bow',0,0.0,21.0,2000,0,4,4,0,0,0,0,0); -- Capture shows 412. Adding 2048 for it to work properly. +INSERT INTO `mob_skills` VALUES (3652,2461,'daybreak',0,0.0,7.0,7000,0,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (3653,2462,'tartaric_sigil',0,0.0,7.0,3500,0,4,0,0,0,2,4,0); +INSERT INTO `mob_skills` VALUES (3654,2463,'null_field',0,0.0,7.0,4500,0,4,0,0,0,11,1,0); +INSERT INTO `mob_skills` VALUES (3655,2464,'alabaster_burst',0,0.0,7.0,4500,0,4,0,0,0,10,6,0); +INSERT INTO `mob_skills` VALUES (3656,2465,'noble_frenzy',0,0.0,7.0,4500,0,4,0,0,0,9,4,0); +INSERT INTO `mob_skills` VALUES (3657,2466,'fulminous_fury',0,0.0,7.0,5000,0,4,0,0,0,12,4,0); +INSERT INTO `mob_skills` VALUES (3658,2467,'no_quarter',0,0.0,7.0,7000,0,4,0,0,0,13,10,0); + INSERT INTO `mob_skills` VALUES (3659,387,'',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (3660,388,'',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (3661,389,'',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); From 6b099f168c8eff7f5f087e04f064b70d6e32a7c2 Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 30 Mar 2026 03:00:04 +0100 Subject: [PATCH 04/11] [trust] August's gambit/mods --- scripts/actions/spells/trust/august.lua | 135 +++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/scripts/actions/spells/trust/august.lua b/scripts/actions/spells/trust/august.lua index b5302d1e3b8..8c825d5bf78 100644 --- a/scripts/actions/spells/trust/august.lua +++ b/scripts/actions/spells/trust/august.lua @@ -20,9 +20,142 @@ spellObject.onMobSpawn = function(mob) [xi.magic.spell.MORIMAR] = xi.trust.messageOffset.TEAMWORK_4, }) + local killerEffects = + { + xi.mod.VERMIN_KILLER, + xi.mod.BIRD_KILLER, + xi.mod.AMORPH_KILLER, + xi.mod.LIZARD_KILLER, + xi.mod.AQUAN_KILLER, + xi.mod.PLANTOID_KILLER, + xi.mod.BEAST_KILLER, + xi.mod.UNDEAD_KILLER, + xi.mod.ARCANA_KILLER, + xi.mod.DRAGON_KILLER, + xi.mod.DEMON_KILLER, + xi.mod.EMPTY_KILLER, + -- xi.mod.HUMANOID_KILLER, -- This can be uncommented once https://github.com/LandSandBoat/server/issues/7610 has been fixed + xi.mod.LUMINIAN_KILLER, + xi.mod.LUMINION_KILLER, + } + + for i = 1, #killerEffects do + mob:addMod(killerEffects[i], 8) -- this is what you would get using all the Founders Gear set + end + + local lvl = mob:getMainLvl() + local shieldMasteryPower = 0 + + if lvl >= 96 then + shieldMasteryPower = 40 + elseif lvl >= 75 then + shieldMasteryPower = 30 + elseif lvl >= 50 then + shieldMasteryPower = 20 + elseif lvl >= 25 then + shieldMasteryPower = 10 + end + + mob:setMod(xi.mod.SHIELD_MASTERY_TP, shieldMasteryPower) + mob:addMod(xi.mod.DMG, -10) + mob:addMod(xi.mod.UFASTCAST, 50) -- August casts stupid fast + mob:setMod(xi.mod.SHIELDBLOCKRATE, xi.trust.modGrowthValMax(mob, 35)) -- around 35% max block rate at 99 from testing + + -- Founders gear mods: August gets all effects from founders gear + -- see xi.trust.modGrowthVal in trust.lua for current curve value + mob:addMod(xi.mod.MDEF, xi.trust.modGrowthValMax(mob, 12)) -- Founders gear: MDEF + 12 + mob:addMod(xi.mod.SPELLINTERRUPT, xi.trust.modGrowthValMax(mob, 30)) -- Founders gear: SIRD +30 + mob:addMod(xi.mod.HASTE_GEAR, xi.trust.modGrowthValMax(mob, 22)) -- Founders gear: Gear Haste +22 + mob:addMod(xi.mod.ACC, xi.trust.modGrowthValMax(mob, 60)) -- Founders gear: ACC + 60 + mob:addMod(xi.mod.ATT, xi.trust.modGrowthValMax(mob, 60)) -- Founders gear: ATT +60 + mob:addMod(xi.mod.MACC, xi.trust.modGrowthValMax(mob, 60)) -- Founders gear: MACC + 60 + mob:addMod(xi.mod.EVA, xi.trust.modGrowthValMax(mob, 183)) -- Founders gear: EVA + 183 + mob:addMod(xi.mod.MEVA, xi.trust.modGrowthValMax(mob, 299)) -- Founders gear: MEVA + 299 + mob:addMod(xi.mod.MATT, xi.trust.modGrowthValMax(mob, 60)) -- Founders gear: MATT + 60 + -- there is a few more, but these make the most sense for now. + + --[[ + -- [DEBUGGING] Remove comments when finished + local modList = + { + {xi.mod.MDEF,'MDEF:'}, + {xi.mod.SPELLINTERRUPT, 'SIRD:'}, + {xi.mod.HASTE_GEAR, 'HASTE_GEAR:'}, + {xi.mod.ACC, 'ACC:'}, + {xi.mod.ATT, 'ATT:'}, + {xi.mod.MACC, 'ACC:'}, + {xi.mod.EVA, 'EVA:'}, + {xi.mod.MEVA, 'MEVA:'}, + {xi.mod.MATT, 'MATT'}, + } + + for i = 1, #modList do + print(modList[i][2]..' ['..mob:getMod(modList[i][1])..']') + end + ]] + + mob:setMobMod(xi.mobMod.CAN_SHIELD_BLOCK, 1) + mob:setMobMod(xi.mobMod.CAN_PARRY, 3) + + mob:setAnimationSub(0) -- this is probably not needed but its here because August's daybreak status is tested against it. + + -- mob:addImmunity(xi.immunity.TERROR) -- this is wrong but we currently have no TERRORRES mod and no way to use one + + local daybreakUsed = false + + mob:addListener('COMBAT_TICK', 'AUGUST_CTICK', function(mob) + local daybreakRecast = 180 -- 3 minutes + local daybreakActive = false + local daybreakEndTime = mob:getLocalVar("DaybreakEndTime") + local daybreakReady = false + local hppLow = mob:getHPP() <= 66 + local now = os.time() + if + mob:getMainLvl() >= 51 and + mob:getAnimationSub() == 0 and + (daybreakEndTime == 0 or now >= daybreakEndTime + daybreakRecast) and + hppLow and + not daybreakUsed + then + mob:useMobAbility(3652) + daybreakUsed = true + end + end) + + ----------------------------------- + -- Gambits + ----------------------------------- + + mob:addGambit(ai.t.TARGET, { ai.c.NOT_STATUS, xi.effect.FLASH }, { ai.r.MA, ai.s.SPECIFIC, xi.magic.spell.FLASH }) + mob:addGambit(ai.t.SELF, { ai.c.NOT_HAS_TOP_ENMITY, 0 }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.PROVOKE }) + mob:addGambit(ai.t.SELF, { ai.c.HPP_LT, 75 }, { ai.r.MA, ai.s.HIGHEST, xi.magic.spellFamily.CURE }) + mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.SENTINEL }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.SENTINEL }) + mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.REPRISAL }, { ai.r.MA, ai.s.SPECIFIC, xi.magic.spell.REPRISAL }) + mob:addGambit(ai.t.PARTY, { ai.c.HPP_LT, 50 }, { ai.r.MA, ai.s.HIGHEST, xi.magic.spellFamily.CURE }) + mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.DIVINE_EMBLEM }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.DIVINE_EMBLEM }) + mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.PALISADE }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.PALISADE }) + -- Only uses Holy when daybreak active + mob:addGambit(ai.t.TRIGGER_SELF_ACTION_TARGET, { ai.c.SUB_ANIMATION, 5 }, { ai.r.MA, ai.s.HIGHEST, xi.magic.spellFamily.HOLY }, 45) + mob:setMobSkillAttack(1197) - mob:setTrustTPSkillSettings(ai.tp.CLOSER_UNTIL_TP, ai.s.HIGHEST, 2500) + mob:setTrustTPSkillSettings(ai.tp.OPENER, ai.s.SPECIAL_AUGUST) + + mob:addListener('WEAPONSKILL_USE', 'AUGUST_WEAPONSKILL_USE', function(mobArg, target, skill, tp, action) + if skill:getID() == 3652 then -- Daybreak + mob:timer(2000, function() + mob:entityAnimationPacket("ids1") -- Wings on + end) + mob:entityAnimationPacket("ids1") + elseif skill:getID() == 3658 then -- No Quarter + -- Come! Show me your finest form! + xi.trust.message(mobArg, xi.trust.messageOffset.SPECIAL_MOVE_1) + daybreakUsed = false + mob:timer(1000, function() + mob:entityAnimationPacket("ids2") -- Wings off + end) + end + end) end spellObject.onMobDespawn = function(mob) From cb9cb9680660403491f8c9cd98d43b04d8a06f2b Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 30 Mar 2026 03:01:02 +0100 Subject: [PATCH 05/11] [trust] New mod growth function in trust.lua (proposal) --- scripts/globals/trust.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/globals/trust.lua b/scripts/globals/trust.lua index 0f8fb8cc9d6..36307780df2 100644 --- a/scripts/globals/trust.lua +++ b/scripts/globals/trust.lua @@ -406,6 +406,21 @@ xi.trust.spawn = function(caster, spell) return 0 end +-- Exponent curves +-- Curve | lvl for val > 0 | lvl for 1/2 full value | lvl for full val +-- 2.0 30 71 99 +-- 1.5 20 63 99 +-- 1.2 13 56 99 +-- 1.0 10 50 99 +xi.trust.modGrowthValMax = function(mob, maxVal) + local lvl = math.max(mob:getMainLvl(), 1) -- Ensure lvl is at least 1 + local curve = 1.5 -- Gentle curve: starts increasing around lvl 20, this needs testing more, but seems to work well at this value. + local progress = (lvl - 1) / 98 -- Normalize level to 0.0 - 1.0 range (98 is the span between 1 and 99) + local exponentGrowth = math.pow(progress, curve) + + return math.floor(maxVal * exponentGrowth) +end + -- pageOffset is: (summon_message_id - 1) / 100 -- Example: Shantotto II summon message ID: 11201 -- pageOffset: (11201 - 1) / 100 = 112 From 6825e6b7823c1962e59c88b17b90dafffb70a9f3 Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 30 Mar 2026 03:08:51 +0100 Subject: [PATCH 06/11] [trust] Adjusted accmod for auto attacks and alabaster burst --- scripts/actions/mobskills/alabaster_burst.lua | 2 +- scripts/actions/mobskills/august_melee_axe.lua | 2 +- scripts/actions/mobskills/august_melee_bow.lua | 2 +- scripts/actions/mobskills/august_melee_h2h.lua | 2 +- scripts/actions/mobskills/august_melee_sword.lua | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/actions/mobskills/alabaster_burst.lua b/scripts/actions/mobskills/alabaster_burst.lua index 8cc0399036e..78d523f7800 100644 --- a/scripts/actions/mobskills/alabaster_burst.lua +++ b/scripts/actions/mobskills/alabaster_burst.lua @@ -10,7 +10,7 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 2 - local accmod = 10 + local accmod = 3 local dmgmod = 4 local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) diff --git a/scripts/actions/mobskills/august_melee_axe.lua b/scripts/actions/mobskills/august_melee_axe.lua index 5745ae05b3b..1156a412d01 100644 --- a/scripts/actions/mobskills/august_melee_axe.lua +++ b/scripts/actions/mobskills/august_melee_axe.lua @@ -10,7 +10,7 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 1 - local accmod = 10 + local accmod = 4 local dmgmod = 2 local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) diff --git a/scripts/actions/mobskills/august_melee_bow.lua b/scripts/actions/mobskills/august_melee_bow.lua index 9bc85abc170..596a684236d 100644 --- a/scripts/actions/mobskills/august_melee_bow.lua +++ b/scripts/actions/mobskills/august_melee_bow.lua @@ -10,7 +10,7 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 1 - local accmod = 100 + local accmod = 4 local dmgmod = 2 local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.RANGED, xi.damageType.PIERCING, info.hitslanded) diff --git a/scripts/actions/mobskills/august_melee_h2h.lua b/scripts/actions/mobskills/august_melee_h2h.lua index ef65492be32..7e791bdec49 100644 --- a/scripts/actions/mobskills/august_melee_h2h.lua +++ b/scripts/actions/mobskills/august_melee_h2h.lua @@ -10,7 +10,7 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 1 - local accmod = 100 + local accmod = 4 local dmgmod = 2 local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) diff --git a/scripts/actions/mobskills/august_melee_sword.lua b/scripts/actions/mobskills/august_melee_sword.lua index c4416c574a4..cc730586ad1 100644 --- a/scripts/actions/mobskills/august_melee_sword.lua +++ b/scripts/actions/mobskills/august_melee_sword.lua @@ -10,7 +10,7 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 2 - local accmod = 100 + local accmod = 4 local dmgmod = 2 local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) From 686d914d4994dac26a0f64f47a9ae1fb46553fab Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 6 Apr 2026 00:49:05 +0100 Subject: [PATCH 07/11] [trust] cleaned august.lua and trust.lua --- scripts/actions/spells/trust/august.lua | 35 ++++++++----------------- scripts/globals/trust.lua | 5 ++-- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/scripts/actions/spells/trust/august.lua b/scripts/actions/spells/trust/august.lua index 8c825d5bf78..27c0e106fac 100644 --- a/scripts/actions/spells/trust/august.lua +++ b/scripts/actions/spells/trust/august.lua @@ -60,7 +60,9 @@ spellObject.onMobSpawn = function(mob) mob:addMod(xi.mod.DMG, -10) mob:addMod(xi.mod.UFASTCAST, 50) -- August casts stupid fast mob:setMod(xi.mod.SHIELDBLOCKRATE, xi.trust.modGrowthValMax(mob, 35)) -- around 35% max block rate at 99 from testing - + mob:addMod(xi.mod.HPP, 10) + mob:addMod(xi.mod.ENMITY, 25) + -- Founders gear mods: August gets all effects from founders gear -- see xi.trust.modGrowthVal in trust.lua for current curve value mob:addMod(xi.mod.MDEF, xi.trust.modGrowthValMax(mob, 12)) -- Founders gear: MDEF + 12 @@ -74,26 +76,6 @@ spellObject.onMobSpawn = function(mob) mob:addMod(xi.mod.MATT, xi.trust.modGrowthValMax(mob, 60)) -- Founders gear: MATT + 60 -- there is a few more, but these make the most sense for now. - --[[ - -- [DEBUGGING] Remove comments when finished - local modList = - { - {xi.mod.MDEF,'MDEF:'}, - {xi.mod.SPELLINTERRUPT, 'SIRD:'}, - {xi.mod.HASTE_GEAR, 'HASTE_GEAR:'}, - {xi.mod.ACC, 'ACC:'}, - {xi.mod.ATT, 'ATT:'}, - {xi.mod.MACC, 'ACC:'}, - {xi.mod.EVA, 'EVA:'}, - {xi.mod.MEVA, 'MEVA:'}, - {xi.mod.MATT, 'MATT'}, - } - - for i = 1, #modList do - print(modList[i][2]..' ['..mob:getMod(modList[i][1])..']') - end - ]] - mob:setMobMod(xi.mobMod.CAN_SHIELD_BLOCK, 1) mob:setMobMod(xi.mobMod.CAN_PARRY, 3) @@ -132,10 +114,15 @@ spellObject.onMobSpawn = function(mob) mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.SENTINEL }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.SENTINEL }) mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.REPRISAL }, { ai.r.MA, ai.s.SPECIFIC, xi.magic.spell.REPRISAL }) mob:addGambit(ai.t.PARTY, { ai.c.HPP_LT, 50 }, { ai.r.MA, ai.s.HIGHEST, xi.magic.spellFamily.CURE }) - mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.DIVINE_EMBLEM }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.DIVINE_EMBLEM }) mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.PALISADE }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.PALISADE }) - -- Only uses Holy when daybreak active - mob:addGambit(ai.t.TRIGGER_SELF_ACTION_TARGET, { ai.c.SUB_ANIMATION, 5 }, { ai.r.MA, ai.s.HIGHEST, xi.magic.spellFamily.HOLY }, 45) + + -- Only uses Divine Emblen and Holy when daybreak active (subAnimation 5) + mob:addGambit(ai.t.SELF, { + { ai.c.SUB_ANIMATION, 5 }, + { ai.c.NOT_STATUS, xi.effect.DIVINE_EMBLEM }, }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.DIVINE_EMBLEM }) + mob:addGambit(ai.t.TRIGGER_SELF_ACTION_TARGET, { + { ai.c.SUB_ANIMATION, 5 }, + { ai.c.STATUS, xi.effect.DIVINE_EMBLEM }, }, { ai.r.MA, ai.s.HIGHEST, xi.magic.spellFamily.HOLY }) mob:setMobSkillAttack(1197) diff --git a/scripts/globals/trust.lua b/scripts/globals/trust.lua index 36307780df2..c39f71abee6 100644 --- a/scripts/globals/trust.lua +++ b/scripts/globals/trust.lua @@ -406,12 +406,13 @@ xi.trust.spawn = function(caster, spell) return 0 end --- Exponent curves --- Curve | lvl for val > 0 | lvl for 1/2 full value | lvl for full val +-- Exponent curves for xi.trust.modGrowthValMax +-- Curve | lvl for val > 0 | lvl for 1/2 full value | lvl for full value -- 2.0 30 71 99 -- 1.5 20 63 99 -- 1.2 13 56 99 -- 1.0 10 50 99 +-- NOTE: This does take into account iLevel, iLevel is different and trust get much more of an aggressive curve. xi.trust.modGrowthValMax = function(mob, maxVal) local lvl = math.max(mob:getMainLvl(), 1) -- Ensure lvl is at least 1 local curve = 1.5 -- Gentle curve: starts increasing around lvl 20, this needs testing more, but seems to work well at this value. From 5c7662bab46e398195d690fdb4b236bc35bf75c2 Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 6 Apr 2026 01:03:30 +0100 Subject: [PATCH 08/11] [trust] Added some safe guards in gambits_container.cpp for better flow --- src/map/ai/helpers/gambits_container.cpp | 514 ++++++++++++++++------- src/map/ai/helpers/gambits_container.h | 1 - 2 files changed, 371 insertions(+), 144 deletions(-) diff --git a/src/map/ai/helpers/gambits_container.cpp b/src/map/ai/helpers/gambits_container.cpp index 703b8cccc25..14e0c1d7756 100644 --- a/src/map/ai/helpers/gambits_container.cpp +++ b/src/map/ai/helpers/gambits_container.cpp @@ -64,15 +64,21 @@ std::string CGambitsContainer::AddGambit(const Gambit_t& gambit) if (!spell::CanUseSpell(static_cast(POwner), static_cast(action.select_arg))) { available = false; + break; } } } - if (available) + + if (!available) { - gambits.emplace_back(gambit); - return gambit.identifier; + return ""; } - return ""; + + // Make a modifiable copy, assign a new identifier and store it + Gambit_t stored = gambit; + stored.identifier = NewGambitIdentifier(stored); + gambits.emplace_back(std::move(stored)); + return gambits.back().identifier; } void CGambitsContainer::RemoveGambit(const std::string& id) @@ -316,198 +322,414 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task continue; } - // Execute all actions defined on the Gambit - // TODO: When multiple actions are defined: - // - Recast of all actions should be considered before executing - // - Casting 2 spells in a row does not yet work - for (auto& action : gambit.actions) + // Pre resolve actions and perform simple recast/availability checks. + // This prevents attempting to cast multiple spells in the same tick (which doesn't work) + // and ensures all actions are available before starting. + std::vector canExecute(gambit.actions.size(), true); + std::vector> resolvedSpells(gambit.actions.size(), std::nullopt); + + // Helper to resolve a spell action to a valid SpellID if possible. + auto ResolveMA = [&](const Action_t& action, CBattleEntity* resolvedTarget) -> Maybe { - if (action.reaction == G_REACTION::RATTACK) + if (action.select == G_SELECT::SPECIFIC) { - controller->RangedAttack(target->targid); + return POwner->SpellContainer->GetAvailable(static_cast(action.select_arg)); } - else if (action.reaction == G_REACTION::MA) + else if (action.select == G_SELECT::HIGHEST) { - if (action.select == G_SELECT::SPECIFIC) + return POwner->SpellContainer->GetBestAvailable(static_cast(action.select_arg)); + } + else if (action.select == G_SELECT::LOWEST) + { + // Not implemented yet: treat as unavailable + return std::nullopt; + } + else if (action.select == G_SELECT::BEST_INDI) + { + auto* PMaster = static_cast(POwner->PMaster); + return POwner->SpellContainer->GetBestIndiSpell(PMaster); + } + else if (action.select == G_SELECT::ENTRUSTED) + { + auto* PMaster = static_cast(POwner->PMaster); + // Entrusted spells target master + return POwner->SpellContainer->GetBestEntrustedSpell(PMaster); + } + else if (action.select == G_SELECT::BEST_AGAINST_TARGET) + { + auto spell_to_cast = static_cast(action.select_arg); + return POwner->SpellContainer->GetBestAgainstTargetWeakness(resolvedTarget, spell_to_cast); + } + else if (action.select == G_SELECT::DEF_BAR_ELEMENT) + { + auto maybeSpellId = POwner->SpellContainer->GetAvailable(SpellID::Barfire); + auto element = POwner->GetLocalVar("[Gambit]CastElement"); + + if (element != 0) { - auto spell_id = POwner->SpellContainer->GetAvailable(static_cast(action.select_arg)); - if (spell_id.has_value()) + switch (element) { - controller->Cast(target->targid, spell_id.value()); + case ELEMENT_FIRE: + maybeSpellId = SpellID::Barfire; + break; + case ELEMENT_ICE: + maybeSpellId = SpellID::Barblizzard; + break; + case ELEMENT_WIND: + maybeSpellId = SpellID::Baraero; + break; + case ELEMENT_EARTH: + maybeSpellId = SpellID::Barstone; + break; + case ELEMENT_THUNDER: + maybeSpellId = SpellID::Barthunder; + break; + case ELEMENT_WATER: + maybeSpellId = SpellID::Barwater; + break; + default: + break; } } - else if (action.select == G_SELECT::HIGHEST) + return maybeSpellId; + } + else if (action.select == G_SELECT::STORM_DAY) + { + return POwner->SpellContainer->GetStormDay(); + } + else if (action.select == G_SELECT::HELIX_DAY) + { + return POwner->SpellContainer->GetHelixDay(); + } + else if (action.select == G_SELECT::EN_MOB_WEAKNESS) + { + CBattleEntity* battleTarget = POwner->GetBattleTarget(); + return POwner->SpellContainer->EnSpellAgainstTargetWeakness(battleTarget); + } + else if (action.select == G_SELECT::STORM_MOB_WEAKNESS) + { + return POwner->SpellContainer->StormDayAgainstTargetWeakness(resolvedTarget); + } + else if (action.select == G_SELECT::HELIX_MOB_WEAKNESS) + { + return POwner->SpellContainer->StormDayAgainstTargetWeakness(resolvedTarget); + } + else if (action.select == G_SELECT::RANDOM) + { + return POwner->SpellContainer->GetSpell(); + } + else if (action.select == G_SELECT::MB_ELEMENT) + { + CStatusEffect* PSCEffect = resolvedTarget->StatusEffectContainer->GetStatusEffect(EFFECT_SKILLCHAIN, 0); + if (PSCEffect == nullptr) { - auto spell_id = POwner->SpellContainer->GetBestAvailable(static_cast(action.select_arg)); - if (spell_id.has_value()) - { - controller->Cast(target->targid, spell_id.value()); - } + return std::nullopt; } - else if (action.select == G_SELECT::LOWEST) + + std::list resonanceProperties; + if (uint16 power = PSCEffect->GetPower()) { - // TODO - // auto spell_id = POwner->SpellContainer->GetWorstAvailable(static_cast(gambit.action.select_arg)); - // if (spell_id.has_value()) - //{ - // controller->Cast(target->targid, static_cast(spell_id.value())); - //} + resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power & 0xF)); + resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power >> 4 & 0xF)); + resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power >> 8)); } - else if (action.select == G_SELECT::BEST_INDI) + + Maybe spell_id; + for (auto& resonance_element : resonanceProperties) { - auto* PMaster = static_cast(POwner->PMaster); - auto spell_id = POwner->SpellContainer->GetBestIndiSpell(PMaster); - if (spell_id.has_value()) + for (auto& chain_element : battleutils::GetSkillchainMagicElement(resonance_element)) { - controller->Cast(target->targid, spell_id.value()); + for (size_t ii = POwner->SpellContainer->m_damageList.size(); ii > 0; --ii) + { + auto spell = POwner->SpellContainer->m_damageList[ii - 1]; + auto spell_element = spell::GetSpell(spell)->getElement(); + if (spell_element == chain_element) + { + spell_id = spell; + break; + } + } } } - else if (action.select == G_SELECT::ENTRUSTED) + return spell_id; + } + + return std::nullopt; + }; + + // First pass: validate availability and prevent executing multiple spells in the same tick. + int firstMAIndex = -1; + for (size_t i = 0; i < gambit.actions.size(); ++i) + { + const auto& action = gambit.actions[i]; + if (action.reaction == G_REACTION::MA) + { + if (firstMAIndex == -1) { - auto* PMaster = static_cast(POwner->PMaster); - auto spell_id = POwner->SpellContainer->GetBestEntrustedSpell(PMaster); - target = PMaster; - if (spell_id.has_value()) + firstMAIndex = static_cast(i); + auto maybeSpell = ResolveMA(action, target); + if (!maybeSpell.has_value()) { - controller->Cast(target->targid, spell_id.value()); + // Couldn't resolve first spell -> cannot use this gambit now + canExecute[i] = false; + } + else + { + resolvedSpells[i] = maybeSpell; } } - else if (action.select == G_SELECT::BEST_AGAINST_TARGET) + else + { + // More than one spell present: skip additional spells to avoid casting two spells in a row. + canExecute[i] = false; + } + } + else if (action.reaction == G_REACTION::JA) + { + // Basic validation: SPECIFIC must exist + if (action.select == G_SELECT::SPECIFIC) { - auto spell_to_cast = static_cast(action.select_arg); - auto spell_id = POwner->SpellContainer->GetBestAgainstTargetWeakness(target, spell_to_cast); - if (spell_id.has_value()) + if (ability::GetAbility(action.select_arg) == nullptr) { - controller->Cast(target->targid, spell_id.value()); + canExecute[i] = false; } } - else if (action.select == G_SELECT::DEF_BAR_ELEMENT) + // Other JA selects rely on TP checks; allow for now. + } + else if (action.reaction == G_REACTION::MS) + { + if (action.select == G_SELECT::SPECIFIC) { - auto maybeSpellId = POwner->SpellContainer->GetAvailable(SpellID::Barfire); - auto element = POwner->GetLocalVar("[Gambit]CastElement"); + // No further pre-check available here; assume ok. + } + } + else if (action.reaction == G_REACTION::RATTACK) + { + // Always available + } + } - if (element != 0) + // If none of the actions are executable, skip this gambit + bool anyExecutable = std::ranges::any_of(canExecute, [](bool v) + { + return v; + }); + if (!anyExecutable) + { + continue; + } + + // Execute actions that passed the pre-checks. Use resolvedSpells for spells where available. + bool executedAnyAction = false; + for (size_t i = 0; i < gambit.actions.size(); ++i) + { + if (!canExecute[i]) + { + continue; + } + + const auto& action = gambit.actions[i]; + + if (action.reaction == G_REACTION::RATTACK) + { + controller->RangedAttack(target->targid); + executedAnyAction = true; + } + else if (action.reaction == G_REACTION::MA) + { + // Use the pre resolved spell if present, otherwise fall back + if (resolvedSpells[i].has_value()) + { + controller->Cast(target->targid, resolvedSpells[i].value()); + executedAnyAction = true; + } + else + { + // Fallback to original, keeps existing behavior for selection + if (action.select == G_SELECT::SPECIFIC) { - switch (element) + auto spell_id = POwner->SpellContainer->GetAvailable(static_cast(action.select_arg)); + if (spell_id.has_value()) { - case ELEMENT_FIRE: - maybeSpellId = SpellID::Barfire; - break; - case ELEMENT_ICE: - maybeSpellId = SpellID::Barblizzard; - break; - case ELEMENT_WIND: - maybeSpellId = SpellID::Baraero; - break; - case ELEMENT_EARTH: - maybeSpellId = SpellID::Barstone; - break; - case ELEMENT_THUNDER: - maybeSpellId = SpellID::Barthunder; - break; - case ELEMENT_WATER: - maybeSpellId = SpellID::Barwater; - break; - default: - break; + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; } - - if (maybeSpellId.has_value()) + } + else if (action.select == G_SELECT::HIGHEST) + { + auto spell_id = POwner->SpellContainer->GetBestAvailable(static_cast(action.select_arg)); + if (spell_id.has_value()) { - controller->Cast(target->targid, maybeSpellId.value()); + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; } } - } - else if (action.select == G_SELECT::STORM_DAY) - { - auto spell_id = POwner->SpellContainer->GetStormDay(); - if (spell_id.has_value()) + else if (action.select == G_SELECT::BEST_INDI) { - controller->Cast(target->targid, spell_id.value()); + auto* PMaster = static_cast(POwner->PMaster); + auto spell_id = POwner->SpellContainer->GetBestIndiSpell(PMaster); + if (spell_id.has_value()) + { + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } } - } - else if (action.select == G_SELECT::HELIX_DAY) - { - auto spell_id = POwner->SpellContainer->GetHelixDay(); - if (spell_id.has_value()) + else if (action.select == G_SELECT::ENTRUSTED) { - controller->Cast(target->targid, spell_id.value()); + auto* PMaster = static_cast(POwner->PMaster); + auto spell_id = POwner->SpellContainer->GetBestEntrustedSpell(PMaster); + target = PMaster; + if (spell_id.has_value()) + { + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } } - } - else if (action.select == G_SELECT::EN_MOB_WEAKNESS) - { - CBattleEntity* battleTarget = POwner->GetBattleTarget(); - auto spell_id = POwner->SpellContainer->EnSpellAgainstTargetWeakness(battleTarget); - if (spell_id.has_value()) + else if (action.select == G_SELECT::BEST_AGAINST_TARGET) { - controller->Cast(POwner->targid, spell_id.value()); + auto spell_to_cast = static_cast(action.select_arg); + auto spell_id = POwner->SpellContainer->GetBestAgainstTargetWeakness(target, spell_to_cast); + if (spell_id.has_value()) + { + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } } - } - else if (action.select == G_SELECT::STORM_MOB_WEAKNESS) - { - auto spell_id = POwner->SpellContainer->StormDayAgainstTargetWeakness(target); - if (spell_id.has_value()) + else if (action.select == G_SELECT::DEF_BAR_ELEMENT) + { + auto maybeSpellId = POwner->SpellContainer->GetAvailable(SpellID::Barfire); + auto element = POwner->GetLocalVar("[Gambit]CastElement"); + + if (element != 0) + { + switch (element) + { + case ELEMENT_FIRE: + maybeSpellId = SpellID::Barfire; + break; + case ELEMENT_ICE: + maybeSpellId = SpellID::Barblizzard; + break; + case ELEMENT_WIND: + maybeSpellId = SpellID::Baraero; + break; + case ELEMENT_EARTH: + maybeSpellId = SpellID::Barstone; + break; + case ELEMENT_THUNDER: + maybeSpellId = SpellID::Barthunder; + break; + case ELEMENT_WATER: + maybeSpellId = SpellID::Barwater; + break; + default: + break; + } + + if (maybeSpellId.has_value()) + { + controller->Cast(target->targid, maybeSpellId.value()); + executedAnyAction = true; + } + } + } + else if (action.select == G_SELECT::STORM_DAY) { - controller->Cast(POwner->targid, spell_id.value()); + auto spell_id = POwner->SpellContainer->GetStormDay(); + if (spell_id.has_value()) + { + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } } - } - else if (action.select == G_SELECT::HELIX_MOB_WEAKNESS) - { - auto spell_id = POwner->SpellContainer->StormDayAgainstTargetWeakness(target); - if (spell_id.has_value()) + else if (action.select == G_SELECT::HELIX_DAY) { - controller->Cast(target->targid, spell_id.value()); + auto spell_id = POwner->SpellContainer->GetHelixDay(); + if (spell_id.has_value()) + { + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } } - } - else if (action.select == G_SELECT::RANDOM) - { - auto spell_id = POwner->SpellContainer->GetSpell(); - if (spell_id.has_value()) + else if (action.select == G_SELECT::EN_MOB_WEAKNESS) { - controller->Cast(target->targid, spell_id.value()); + CBattleEntity* battleTarget = POwner->GetBattleTarget(); + auto spell_id = POwner->SpellContainer->EnSpellAgainstTargetWeakness(battleTarget); + if (spell_id.has_value()) + { + controller->Cast(POwner->targid, spell_id.value()); + executedAnyAction = true; + } } - } - else if (action.select == G_SELECT::MB_ELEMENT) - { - CStatusEffect* PSCEffect = target->StatusEffectContainer->GetStatusEffect(EFFECT_SKILLCHAIN, 0); - - if (PSCEffect == nullptr) + else if (action.select == G_SELECT::STORM_MOB_WEAKNESS) { - ShowError("G_SELECT::MB_ELEMENT: PSCEffect was null."); - co_return; + auto spell_id = POwner->SpellContainer->StormDayAgainstTargetWeakness(target); + if (spell_id.has_value()) + { + controller->Cast(POwner->targid, spell_id.value()); + executedAnyAction = true; + } } - - std::list resonanceProperties; - if (uint16 power = PSCEffect->GetPower()) + else if (action.select == G_SELECT::HELIX_MOB_WEAKNESS) { - resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power & 0xF)); - resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power >> 4 & 0xF)); - resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power >> 8)); + auto spell_id = POwner->SpellContainer->StormDayAgainstTargetWeakness(target); + if (spell_id.has_value()) + { + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } } - - Maybe spell_id; - for (auto& resonance_element : resonanceProperties) + else if (action.select == G_SELECT::RANDOM) { - for (auto& chain_element : battleutils::GetSkillchainMagicElement(resonance_element)) + auto spell_id = POwner->SpellContainer->GetSpell(); + if (spell_id.has_value()) { - // TODO: SpellContianer->GetBestByElement(ELEMENT) - // NOTE: Iterating this list in reverse guarantees finding the best match - for (size_t i = POwner->SpellContainer->m_damageList.size(); i > 0; --i) + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } + } + else if (action.select == G_SELECT::MB_ELEMENT) + { + CStatusEffect* PSCEffect = target->StatusEffectContainer->GetStatusEffect(EFFECT_SKILLCHAIN, 0); + + if (PSCEffect == nullptr) + { + ShowError("G_SELECT::MB_ELEMENT: PSCEffect was null."); + co_return; + } + + std::list resonanceProperties; + if (uint16 power = PSCEffect->GetPower()) + { + resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power & 0xF)); + resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power >> 4 & 0xF)); + resonanceProperties.emplace_back((SKILLCHAIN_ELEMENT)(power >> 8)); + } + + Maybe spell_id; + for (auto& resonance_element : resonanceProperties) + { + for (auto& chain_element : battleutils::GetSkillchainMagicElement(resonance_element)) { - auto spell = POwner->SpellContainer->m_damageList[i - 1]; - auto spell_element = spell::GetSpell(spell)->getElement(); - if (spell_element == chain_element) + // NOTE: Iterating this list in reverse guarantees finding the best match + for (size_t ii = POwner->SpellContainer->m_damageList.size(); ii > 0; --ii) { - spell_id = spell; - break; + auto spell = POwner->SpellContainer->m_damageList[ii - 1]; + auto spell_element = spell::GetSpell(spell)->getElement(); + if (spell_element == chain_element) + { + spell_id = spell; + break; + } } } } - } - if (spell_id.has_value()) - { - controller->Cast(target->targid, spell_id.value()); + if (spell_id.has_value()) + { + controller->Cast(target->targid, spell_id.value()); + executedAnyAction = true; + } } } } @@ -516,7 +738,8 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task auto* PAbility = ability::GetAbility(action.select_arg); if (PAbility == nullptr) { - co_return; + // If SPECIFIC was validated earlier this shouldn't happen; skip this action. + continue; } auto mLevel = POwner->GetMLevel(); @@ -574,6 +797,7 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task { PAbility = PWaltzAbility; controller->Ability(target->targid, PAbility->getID()); + executedAnyAction = true; } } } @@ -591,6 +815,7 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task if (action.select == G_SELECT::SPECIFIC) { controller->Ability(target->targid, PAbility->getID()); + executedAnyAction = true; } if (action.select == G_SELECT::BEST_SAMBA) @@ -659,6 +884,7 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task if (tpCost != 0 && (currentTP >= tpCost)) { controller->Ability(target->targid, PAbility->getID()); + executedAnyAction = true; } } else if (action.select == G_SELECT::RUNE_DAY) @@ -703,6 +929,7 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task break; } controller->Ability(target->targid, ability); + executedAnyAction = true; } } else if (action.reaction == G_REACTION::MS) @@ -710,12 +937,13 @@ auto CGambitsContainer::Tick(timer::time_point tick) -> Task if (action.select == G_SELECT::SPECIFIC) { controller->MobSkill(target->targid, action.select_arg, std::nullopt); + executedAnyAction = true; } } } - // Assume success - if (gambit.retry_delay != 0) + // If we executed any action and the gambit has a retry_delay, set last_used + if (executedAnyAction && gambit.retry_delay != 0) { gambit.last_used = tick; } diff --git a/src/map/ai/helpers/gambits_container.h b/src/map/ai/helpers/gambits_container.h index 8e8fa6d074f..8a2e374d9dc 100644 --- a/src/map/ai/helpers/gambits_container.h +++ b/src/map/ai/helpers/gambits_container.h @@ -131,7 +131,6 @@ enum class G_SELECT : uint16 HELIX_MOB_WEAKNESS = 16, DEF_BAR_ELEMENT = 17, RUNE_DAY = 18, - }; enum class G_TP_TRIGGER : uint16 From 0e5c6b4031dc5fac2a9e3d91e0669a644cba701f Mon Sep 17 00:00:00 2001 From: MrSent Date: Mon, 6 Apr 2026 01:42:16 +0100 Subject: [PATCH 09/11] style checks, change os.time() for GetSystemTime() --- scripts/actions/mobskills/alabaster_burst.lua | 9 ++++--- scripts/actions/mobskills/daybreak.lua | 2 +- scripts/actions/mobskills/fulminous_fury.lua | 1 + scripts/actions/mobskills/no_quarter.lua | 2 +- scripts/actions/mobskills/tartaric_sigil.lua | 1 + scripts/actions/spells/trust/august.lua | 27 +++++++++---------- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/actions/mobskills/alabaster_burst.lua b/scripts/actions/mobskills/alabaster_burst.lua index 78d523f7800..1281243f405 100644 --- a/scripts/actions/mobskills/alabaster_burst.lua +++ b/scripts/actions/mobskills/alabaster_burst.lua @@ -10,16 +10,17 @@ end mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) local numhits = 2 - local accmod = 3 - local dmgmod = 4 - local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) - local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) + local accmod = 3 + local dmgmod = 4 + local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.physicalTpBonus.NO_EFFECT) + local dmg = xi.mobskills.mobFinalAdjustments(info, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.SLASHING, info.hitslanded) target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING) local duration = (skill:getTP() / 100) / 6 -- 2 sec min, 5 sec max if duration < 2 then duration = 2 end + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.FLASH, 1, 0, duration) return dmg diff --git a/scripts/actions/mobskills/daybreak.lua b/scripts/actions/mobskills/daybreak.lua index d74230bf7cd..9c234bba137 100644 --- a/scripts/actions/mobskills/daybreak.lua +++ b/scripts/actions/mobskills/daybreak.lua @@ -1,7 +1,7 @@ ----------------------------------- -- Daybreak -- Description: 1 min 30 sec duration, 1 min 30 sec cooldown after either No quater used or daybreak wears off --- When August's HP drops below 66%, he uses Daybreak if it's available which partially restores some HP and MP, +-- When August's HP drops below 66%, he uses Daybreak if it's available which partially restores some HP and MP, -- resets his TP, and activates an aura with wings of light -- Daybreak is a -50% PDT effect, full Erase -- Daybreak is removed after the use of No Quarter. diff --git a/scripts/actions/mobskills/fulminous_fury.lua b/scripts/actions/mobskills/fulminous_fury.lua index fc0dadf51cd..b84f040b610 100644 --- a/scripts/actions/mobskills/fulminous_fury.lua +++ b/scripts/actions/mobskills/fulminous_fury.lua @@ -26,6 +26,7 @@ mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) if duration < 2 then duration = 2 end + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.STUN, 1, 0, duration) end diff --git a/scripts/actions/mobskills/no_quarter.lua b/scripts/actions/mobskills/no_quarter.lua index da3d3ce10e2..3bc6fa584c7 100644 --- a/scripts/actions/mobskills/no_quarter.lua +++ b/scripts/actions/mobskills/no_quarter.lua @@ -18,7 +18,7 @@ mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING) mob:setMod(xi.mod.DMGPHYS, 0) -- Remove the Phyisical damage taken effect - mob:setLocalVar("DaybreakEndTime", os.time()) + mob:setLocalVar('DaybreakEndTime', GetSystemTime()) return dmg end diff --git a/scripts/actions/mobskills/tartaric_sigil.lua b/scripts/actions/mobskills/tartaric_sigil.lua index 2437dc21a2c..bb818b967d1 100644 --- a/scripts/actions/mobskills/tartaric_sigil.lua +++ b/scripts/actions/mobskills/tartaric_sigil.lua @@ -26,6 +26,7 @@ mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) if duration < 2 then duration = 2 end + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.AMNESIA, 1, 0, duration) end diff --git a/scripts/actions/spells/trust/august.lua b/scripts/actions/spells/trust/august.lua index 27c0e106fac..ef0a2336efa 100644 --- a/scripts/actions/spells/trust/august.lua +++ b/scripts/actions/spells/trust/august.lua @@ -62,7 +62,7 @@ spellObject.onMobSpawn = function(mob) mob:setMod(xi.mod.SHIELDBLOCKRATE, xi.trust.modGrowthValMax(mob, 35)) -- around 35% max block rate at 99 from testing mob:addMod(xi.mod.HPP, 10) mob:addMod(xi.mod.ENMITY, 25) - + -- Founders gear mods: August gets all effects from founders gear -- see xi.trust.modGrowthVal in trust.lua for current curve value mob:addMod(xi.mod.MDEF, xi.trust.modGrowthValMax(mob, 12)) -- Founders gear: MDEF + 12 @@ -85,21 +85,19 @@ spellObject.onMobSpawn = function(mob) local daybreakUsed = false - mob:addListener('COMBAT_TICK', 'AUGUST_CTICK', function(mob) + mob:addListener('COMBAT_TICK', 'AUGUST_CTICK', function(mobArg) local daybreakRecast = 180 -- 3 minutes - local daybreakActive = false - local daybreakEndTime = mob:getLocalVar("DaybreakEndTime") - local daybreakReady = false - local hppLow = mob:getHPP() <= 66 - local now = os.time() + local daybreakEndTime = mobArg:getLocalVar('DaybreakEndTime') + local hppLow = mobArg:getHPP() <= 66 + local now = GetSystemTime() if - mob:getMainLvl() >= 51 and - mob:getAnimationSub() == 0 and + mobArg:getMainLvl() >= 51 and + mobArg:getAnimationSub() == 0 and (daybreakEndTime == 0 or now >= daybreakEndTime + daybreakRecast) and hppLow and not daybreakUsed then - mob:useMobAbility(3652) + mobArg:useMobAbility(3652) daybreakUsed = true end end) @@ -117,8 +115,8 @@ spellObject.onMobSpawn = function(mob) mob:addGambit(ai.t.SELF, { ai.c.NOT_STATUS, xi.effect.PALISADE }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.PALISADE }) -- Only uses Divine Emblen and Holy when daybreak active (subAnimation 5) - mob:addGambit(ai.t.SELF, { - { ai.c.SUB_ANIMATION, 5 }, + mob:addGambit(ai.t.SELF, { + { ai.c.SUB_ANIMATION, 5 }, { ai.c.NOT_STATUS, xi.effect.DIVINE_EMBLEM }, }, { ai.r.JA, ai.s.SPECIFIC, xi.ja.DIVINE_EMBLEM }) mob:addGambit(ai.t.TRIGGER_SELF_ACTION_TARGET, { { ai.c.SUB_ANIMATION, 5 }, @@ -131,15 +129,14 @@ spellObject.onMobSpawn = function(mob) mob:addListener('WEAPONSKILL_USE', 'AUGUST_WEAPONSKILL_USE', function(mobArg, target, skill, tp, action) if skill:getID() == 3652 then -- Daybreak mob:timer(2000, function() - mob:entityAnimationPacket("ids1") -- Wings on + mob:entityAnimationPacket('ids1') -- Wings on end) - mob:entityAnimationPacket("ids1") elseif skill:getID() == 3658 then -- No Quarter -- Come! Show me your finest form! xi.trust.message(mobArg, xi.trust.messageOffset.SPECIAL_MOVE_1) daybreakUsed = false mob:timer(1000, function() - mob:entityAnimationPacket("ids2") -- Wings off + mob:entityAnimationPacket('ids2') -- Wings off end) end end) From 867a07bdb37536da7b96f23228afe324917227a1 Mon Sep 17 00:00:00 2001 From: MrSent Date: Fri, 10 Apr 2026 16:24:27 +0100 Subject: [PATCH 10/11] Changed daybreak var to localVar, added skills to mob_skill.lua --- scripts/actions/mobskills/daybreak.lua | 1 + scripts/actions/spells/trust/august.lua | 15 +++++++-------- scripts/enum/mob_skill.lua | 8 ++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/scripts/actions/mobskills/daybreak.lua b/scripts/actions/mobskills/daybreak.lua index 9c234bba137..eea824c0c47 100644 --- a/scripts/actions/mobskills/daybreak.lua +++ b/scripts/actions/mobskills/daybreak.lua @@ -38,6 +38,7 @@ mobskillObject.onMobWeaponSkill = function(mob, target, skill, action) mob:addMP(mpHeal) -- restore mp mob:setMod(xi.mod.DMGPHYS, -5000) -- Phyisical damage taken -50% mob:setTP(0) -- daybreak uses all tp, so set to 0 + mob:setLocalVar('DaybreakUsed', 1) return info.damage end diff --git a/scripts/actions/spells/trust/august.lua b/scripts/actions/spells/trust/august.lua index ef0a2336efa..5106fc6b99c 100644 --- a/scripts/actions/spells/trust/august.lua +++ b/scripts/actions/spells/trust/august.lua @@ -83,11 +83,10 @@ spellObject.onMobSpawn = function(mob) -- mob:addImmunity(xi.immunity.TERROR) -- this is wrong but we currently have no TERRORRES mod and no way to use one - local daybreakUsed = false - mob:addListener('COMBAT_TICK', 'AUGUST_CTICK', function(mobArg) local daybreakRecast = 180 -- 3 minutes local daybreakEndTime = mobArg:getLocalVar('DaybreakEndTime') + local daybreakUsed = mobArg:getLocalVar('DaybreakUsed') local hppLow = mobArg:getHPP() <= 66 local now = GetSystemTime() if @@ -95,10 +94,10 @@ spellObject.onMobSpawn = function(mob) mobArg:getAnimationSub() == 0 and (daybreakEndTime == 0 or now >= daybreakEndTime + daybreakRecast) and hppLow and - not daybreakUsed + daybreakUsed ~= 1 then - mobArg:useMobAbility(3652) - daybreakUsed = true + mobArg:useMobAbility(xi.mobSkill.DAYBREAK) + daybreakUsed = 1 end end) @@ -127,14 +126,14 @@ spellObject.onMobSpawn = function(mob) mob:setTrustTPSkillSettings(ai.tp.OPENER, ai.s.SPECIAL_AUGUST) mob:addListener('WEAPONSKILL_USE', 'AUGUST_WEAPONSKILL_USE', function(mobArg, target, skill, tp, action) - if skill:getID() == 3652 then -- Daybreak + if skill:getID() == xi.mobSkill.DAYBREAK then -- Daybreak mob:timer(2000, function() mob:entityAnimationPacket('ids1') -- Wings on end) - elseif skill:getID() == 3658 then -- No Quarter + elseif skill:getID() == xi.mobSkill.NO_QUARTER then -- No Quarter -- Come! Show me your finest form! xi.trust.message(mobArg, xi.trust.messageOffset.SPECIAL_MOVE_1) - daybreakUsed = false + mobArg:setLocalVar('DaybreakUsed', 0) mob:timer(1000, function() mob:entityAnimationPacket('ids2') -- Wings off end) diff --git a/scripts/enum/mob_skill.lua b/scripts/enum/mob_skill.lua index a2f821cb738..032f78fb1d1 100644 --- a/scripts/enum/mob_skill.lua +++ b/scripts/enum/mob_skill.lua @@ -922,6 +922,14 @@ xi.mobSkill = -- AZURE_LORE = 3481, BOLSTER = 3482, + DAYBREAK = 3652, -- August Trust + TARTARIC_SIGIL = 3653, -- August Trust + NULL_FIELD = 3654, -- August Trust + ALABASTER_BURST = 3655, -- August Trust + NOBLE_FRENZY = 3656, -- August Trust + FULMINOUS_FURY = 3657, -- August Trust + NO_QUARTER = 3658, -- August Trust + CROSS_REAVER_3 = 3706, -- Ark Angel HM Trust ARROGANCE_INCARNATE_2 = 3710, -- Ark Angel EV Trust From 4058fb6fc0998c213c1a3f3f8883d084cde2470e Mon Sep 17 00:00:00 2001 From: MrSent Date: Sat, 11 Apr 2026 15:47:07 +0100 Subject: [PATCH 11/11] Added _TRUST identifier to August's skills in mob_skill.lua --- scripts/actions/spells/trust/august.lua | 6 +++--- scripts/enum/mob_skill.lua | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/actions/spells/trust/august.lua b/scripts/actions/spells/trust/august.lua index 5106fc6b99c..ed3fb8b24b7 100644 --- a/scripts/actions/spells/trust/august.lua +++ b/scripts/actions/spells/trust/august.lua @@ -96,7 +96,7 @@ spellObject.onMobSpawn = function(mob) hppLow and daybreakUsed ~= 1 then - mobArg:useMobAbility(xi.mobSkill.DAYBREAK) + mobArg:useMobAbility(xi.mobSkill.DAYBREAK_TRUST) daybreakUsed = 1 end end) @@ -126,11 +126,11 @@ spellObject.onMobSpawn = function(mob) mob:setTrustTPSkillSettings(ai.tp.OPENER, ai.s.SPECIAL_AUGUST) mob:addListener('WEAPONSKILL_USE', 'AUGUST_WEAPONSKILL_USE', function(mobArg, target, skill, tp, action) - if skill:getID() == xi.mobSkill.DAYBREAK then -- Daybreak + if skill:getID() == xi.mobSkill.DAYBREAK_TRUST then -- Daybreak mob:timer(2000, function() mob:entityAnimationPacket('ids1') -- Wings on end) - elseif skill:getID() == xi.mobSkill.NO_QUARTER then -- No Quarter + elseif skill:getID() == xi.mobSkill.NO_QUARTER_TRUST then -- No Quarter -- Come! Show me your finest form! xi.trust.message(mobArg, xi.trust.messageOffset.SPECIAL_MOVE_1) mobArg:setLocalVar('DaybreakUsed', 0) diff --git a/scripts/enum/mob_skill.lua b/scripts/enum/mob_skill.lua index 032f78fb1d1..d035a97a447 100644 --- a/scripts/enum/mob_skill.lua +++ b/scripts/enum/mob_skill.lua @@ -922,13 +922,13 @@ xi.mobSkill = -- AZURE_LORE = 3481, BOLSTER = 3482, - DAYBREAK = 3652, -- August Trust - TARTARIC_SIGIL = 3653, -- August Trust - NULL_FIELD = 3654, -- August Trust - ALABASTER_BURST = 3655, -- August Trust - NOBLE_FRENZY = 3656, -- August Trust - FULMINOUS_FURY = 3657, -- August Trust - NO_QUARTER = 3658, -- August Trust + DAYBREAK_TRUST = 3652, -- August Trust + TARTARIC_SIGIL_TRUST = 3653, -- August Trust + NULL_FIELD_TRUST = 3654, -- August Trust + ALABASTER_BURST_TRUST = 3655, -- August Trust + NOBLE_FRENZY_TRUST = 3656, -- August Trust + FULMINOUS_FURY_TRUST = 3657, -- August Trust + NO_QUARTER_TRUST = 3658, -- August Trust CROSS_REAVER_3 = 3706, -- Ark Angel HM Trust