diff --git a/scripts/actions/mobskills/bilgestorm.lua b/scripts/actions/mobskills/bilgestorm.lua index 163ff88daae..faa97346a18 100644 --- a/scripts/actions/mobskills/bilgestorm.lua +++ b/scripts/actions/mobskills/bilgestorm.lua @@ -10,16 +10,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1840 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/brainjack.lua b/scripts/actions/mobskills/brainjack.lua index 6f5263e82d3..3ecce7000f5 100644 --- a/scripts/actions/mobskills/brainjack.lua +++ b/scripts/actions/mobskills/brainjack.lua @@ -1,5 +1,7 @@ ----------------------------------- -- BrainJack +-- Charms a player and inflicts a 25/tick dot while charmed +-- Only used by Long-Armed Chariot and Long-Horned Chariot. Last roughly 90 seconds; DoT takes about 25HP/tick. ----------------------------------- local mobskillObject = {} @@ -8,16 +10,28 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - local numhits = 1 - local accmod = 1 - local dmgmod = 2.3 - local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.magicalTpBonus.NO_EFFECT) - local dmg = xi.mobskills.mobFinalAdjustments(info.dmg, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) + local duration = 90 + local typeEffect = xi.effect.CHARM_I - xi.mobskills.mobPhysicalStatusEffectMove(mob, target, skill, xi.effect.CHARM_I, 0, 0, 60) - target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) + if not target:isPC() then + skill:setMsg(xi.msg.basic.SKILL_MISS) + return typeEffect + elseif mob:getFamily() == 316 then -- Pandemonium Warden + duration = 30 + end - return dmg + local msg = xi.mobskills.mobPhysicalStatusEffectMove(mob, target, skill, typeEffect, 0, 0, duration) + if msg == xi.msg.basic.SKILL_ENFEEB_IS then + target:addMod(xi.mod.REGEN_DOWN, 25) + -- apply regen_down on the target, and stack DoT on the effect so it gets removed when charm is removed + local effect = target:getStatusEffect(typeEffect) + effect:addMod(xi.mod.REGEN_DOWN, 25) + mob:charm(target) + mob:resetEnmity(target) + end + + skill:setMsg(msg) + return typeEffect end return mobskillObject diff --git a/scripts/actions/mobskills/cackle.lua b/scripts/actions/mobskills/cackle.lua new file mode 100644 index 00000000000..30adf20155e --- /dev/null +++ b/scripts/actions/mobskills/cackle.lua @@ -0,0 +1,40 @@ +----------------------------------- +-- Cackle +-- Reduces magical attack, magical accuracy, or magical defense of targets in an area of effect. +-- Video evidence that it's only ever one effect applied to all targets in AoE: https://youtu.be/T_Us2Tmlm-E?t=206 +----------------------------------- +local mobskillObject = {} + +local typeEffects = +{ + xi.effect.MAGIC_ATK_DOWN, + xi.effect.MAGIC_ACC_DOWN, + xi.effect.MAGIC_DEF_DOWN, +} + +mobskillObject.onMobSkillCheck = function(target, mob, skill) + -- smaller range to use, but 30 yalm AoE. Effectively cannot out-range the skill + if mob:checkDistance(target) > 10 then + return 1 + end + + mob:setLocalVar('cackle-index', math.random(1, #typeEffects)) + return 0 +end + +mobskillObject.onMobWeaponSkill = function(target, mob, skill) + local power = 40 + local duration = 60 + local returnEffect = xi.effect.NONE + local typeEffect = typeEffects[mob:getLocalVar('cackle-index')] + + skill:setMsg(xi.msg.basic.SKILL_MISS) + if xi.mobskills.mobStatusEffectMove(mob, target, typeEffect, power, 0, duration) == xi.msg.basic.SKILL_ENFEEB_IS then + returnEffect = typeEffect + skill:setMsg(xi.msg.basic.SKILL_ENFEEB_IS) + end + + return returnEffect +end + +return mobskillObject diff --git a/scripts/actions/mobskills/crosswind.lua b/scripts/actions/mobskills/crosswind.lua index 3a6848097da..ead418f33d5 100644 --- a/scripts/actions/mobskills/crosswind.lua +++ b/scripts/actions/mobskills/crosswind.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1746 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/diamondhide.lua b/scripts/actions/mobskills/diamondhide.lua index f081dc8ffb4..a37cb87ee8d 100644 --- a/scripts/actions/mobskills/diamondhide.lua +++ b/scripts/actions/mobskills/diamondhide.lua @@ -12,7 +12,7 @@ end mobskillObject.onMobWeaponSkill = function(target, mob, skill) local power = 600 -- Guesstimated, def not based on mobs lv+hp*tp like was previously in this script.. - skill:setMsg(xi.mobskills.mobBuffMove(mob, xi.effect.STONESKIN, power, 0, 300)) + skill:setMsg(xi.mobskills.mobBuffMove(target, xi.effect.STONESKIN, power, 0, 300)) return xi.effect.STONESKIN end diff --git a/scripts/actions/mobskills/dreadstorm.lua b/scripts/actions/mobskills/dreadstorm.lua index 8fd193aa251..1dc21c4171f 100644 --- a/scripts/actions/mobskills/dreadstorm.lua +++ b/scripts/actions/mobskills/dreadstorm.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1805 then - return 0 - else - return 1 - end - end - return 1 end diff --git a/scripts/actions/mobskills/enervation.lua b/scripts/actions/mobskills/enervation.lua index 37000479dc1..d05c9770a25 100644 --- a/scripts/actions/mobskills/enervation.lua +++ b/scripts/actions/mobskills/enervation.lua @@ -6,16 +6,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1680 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/firespit.lua b/scripts/actions/mobskills/firespit.lua index 3bdadcbb8fa..42b6072f1b5 100644 --- a/scripts/actions/mobskills/firespit.lua +++ b/scripts/actions/mobskills/firespit.lua @@ -6,16 +6,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1639 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/fossilizing_breath.lua b/scripts/actions/mobskills/fossilizing_breath.lua index 02bf92005d5..8c8667bd13f 100644 --- a/scripts/actions/mobskills/fossilizing_breath.lua +++ b/scripts/actions/mobskills/fossilizing_breath.lua @@ -8,17 +8,11 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1805 then - return 0 - else - return 1 - end + if target:isBehind(mob, 48) then + return 1 + else + return 0 end - - return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/fulmination.lua b/scripts/actions/mobskills/fulmination.lua index 11212d9ba18..41002050ef5 100644 --- a/scripts/actions/mobskills/fulmination.lua +++ b/scripts/actions/mobskills/fulmination.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1805 then - return 0 - else - return 1 - end - end - local family = mob:getFamily() local mobhp = mob:getHPP() local result = 1 @@ -27,6 +17,8 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) result = 0 elseif family == 315 and mobhp <= 50 then -- Tyger < 50% result = 0 + elseif family == 316 then + result = 0 end return result diff --git a/scripts/actions/mobskills/gates_of_hades.lua b/scripts/actions/mobskills/gates_of_hades.lua index da8c2f59702..a55ee66721b 100644 --- a/scripts/actions/mobskills/gates_of_hades.lua +++ b/scripts/actions/mobskills/gates_of_hades.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1793 then - return 0 - else - return 1 - end - end - local result = 1 local mobhp = mob:getHPP() diff --git a/scripts/actions/mobskills/hellclap.lua b/scripts/actions/mobskills/hellclap.lua index 5472b1e0f7b..4d322c27b28 100644 --- a/scripts/actions/mobskills/hellclap.lua +++ b/scripts/actions/mobskills/hellclap.lua @@ -5,26 +5,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1839 then - return 0 - else - return 1 - end - end - - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1840 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/hellsnap.lua b/scripts/actions/mobskills/hellsnap.lua index 1b40a8c266a..11cae994f1e 100644 --- a/scripts/actions/mobskills/hellsnap.lua +++ b/scripts/actions/mobskills/hellsnap.lua @@ -5,33 +5,19 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1839 then - return 0 - else - return 1 - end - end - - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1840 then - return 0 - else - return 1 - end - end - return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - xi.mobskills.mobPhysicalStatusEffectMove(mob, target, skill, xi.effect.STUN, 1, 0, 4) + local duration = 5 + local returnEffect = xi.effect.STUN + if mob:getFamily() == 316 and mob:getLocalVar('phase') ~= 21 then + duration = 10 + end + + skill:setMsg(xi.mobskills.mobPhysicalStatusEffectMove(mob, target, skill, returnEffect, 1, 0, duration)) - return xi.effect.STUN + return returnEffect end return mobskillObject diff --git a/scripts/actions/mobskills/homing_missle.lua b/scripts/actions/mobskills/homing_missile.lua similarity index 100% rename from scripts/actions/mobskills/homing_missle.lua rename to scripts/actions/mobskills/homing_missile.lua diff --git a/scripts/actions/mobskills/ill_wind.lua b/scripts/actions/mobskills/ill_wind.lua index f94fbb48d29..86505b4ac2a 100644 --- a/scripts/actions/mobskills/ill_wind.lua +++ b/scripts/actions/mobskills/ill_wind.lua @@ -9,11 +9,7 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 and mob:getModelId() == 1746 then - return 0 - else - return 1 - end + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/lava_spit.lua b/scripts/actions/mobskills/lava_spit.lua index a3af73ddd5d..2c908ecdf14 100644 --- a/scripts/actions/mobskills/lava_spit.lua +++ b/scripts/actions/mobskills/lava_spit.lua @@ -5,16 +5,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1793 then - return 0 - else - return 1 - end - end - if target:isBehind(mob, 48) then return 1 else diff --git a/scripts/actions/mobskills/necrobane.lua b/scripts/actions/mobskills/necrobane.lua index f6365f2f12e..562f40073db 100644 --- a/scripts/actions/mobskills/necrobane.lua +++ b/scripts/actions/mobskills/necrobane.lua @@ -4,26 +4,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1840 then - return 0 - else - return 1 - end - end - - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1839 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/necropurge.lua b/scripts/actions/mobskills/necropurge.lua index 6d893939e92..9e08f544afa 100644 --- a/scripts/actions/mobskills/necropurge.lua +++ b/scripts/actions/mobskills/necropurge.lua @@ -4,26 +4,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1840 then - return 0 - else - return 1 - end - end - - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1839 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/nerve_gas.lua b/scripts/actions/mobskills/nerve_gas.lua index bbbb15f92dd..fb83cc768a4 100644 --- a/scripts/actions/mobskills/nerve_gas.lua +++ b/scripts/actions/mobskills/nerve_gas.lua @@ -9,14 +9,7 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then -- PW - local mobSkin = mob:getModelId() - if mobSkin == 1796 then - return 0 - else - return 1 - end - elseif mob:getFamily() == 313 then -- Tinnin can use at will + if mob:getFamily() == 313 then -- Tinnin can use at will return 0 else if mob:getAnimationSub() == 0 then diff --git a/scripts/actions/mobskills/polar_blast.lua b/scripts/actions/mobskills/polar_blast.lua index 324ae3c81ea..f7156ae4c53 100644 --- a/scripts/actions/mobskills/polar_blast.lua +++ b/scripts/actions/mobskills/polar_blast.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1796 then - return 0 - else - return 1 - end - end - if mob:getAnimationSub() <= 1 then return 0 else diff --git a/scripts/actions/mobskills/polar_bulwark.lua b/scripts/actions/mobskills/polar_bulwark.lua index ea88a5a737f..7e2b3b7e8c2 100644 --- a/scripts/actions/mobskills/polar_bulwark.lua +++ b/scripts/actions/mobskills/polar_bulwark.lua @@ -7,16 +7,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1796 then - return 0 - else - return 1 - end - end - if mob:getAnimationSub() == 0 then return 0 else diff --git a/scripts/actions/mobskills/pyric_blast.lua b/scripts/actions/mobskills/pyric_blast.lua index 995176c1e9a..c5e436fe19d 100644 --- a/scripts/actions/mobskills/pyric_blast.lua +++ b/scripts/actions/mobskills/pyric_blast.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1796 then - return 0 - else - return 1 - end - end - if mob:getAnimationSub() == 0 then return 0 else diff --git a/scripts/actions/mobskills/pyric_bulwark.lua b/scripts/actions/mobskills/pyric_bulwark.lua index 3cf7dcafed6..0107a7f397a 100644 --- a/scripts/actions/mobskills/pyric_bulwark.lua +++ b/scripts/actions/mobskills/pyric_bulwark.lua @@ -7,17 +7,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1796 then - return 0 - else - return 1 - end - end - - -- TODO: Used only when second/left head is alive (animationsub 0 or 1) if mob:getAnimationSub() <= 1 then return 0 else diff --git a/scripts/actions/mobskills/quake_stomp.lua b/scripts/actions/mobskills/quake_stomp.lua index 99b9c4729d6..bf9df5f70a0 100644 --- a/scripts/actions/mobskills/quake_stomp.lua +++ b/scripts/actions/mobskills/quake_stomp.lua @@ -8,16 +8,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1680 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/rock_smash.lua b/scripts/actions/mobskills/rock_smash.lua index c8a0862418a..614db85411e 100644 --- a/scripts/actions/mobskills/rock_smash.lua +++ b/scripts/actions/mobskills/rock_smash.lua @@ -8,16 +8,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1680 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/scorching_lash.lua b/scripts/actions/mobskills/scorching_lash.lua index 0489c73341e..136ab816984 100644 --- a/scripts/actions/mobskills/scorching_lash.lua +++ b/scripts/actions/mobskills/scorching_lash.lua @@ -10,16 +10,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1793 then - return 0 - else - return 1 - end - end - if not target:isBehind(mob, 48) then return 1 else diff --git a/scripts/actions/mobskills/self-destruct.lua b/scripts/actions/mobskills/self-destruct.lua index 5197a98f431..4d9b59f1924 100644 --- a/scripts/actions/mobskills/self-destruct.lua +++ b/scripts/actions/mobskills/self-destruct.lua @@ -4,7 +4,10 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:isMobType(xi.mobskills.mobType.NOTORIOUS) or mob:getHPP() > 75 then + if + (mob:isMobType(xi.mobskills.mobType.NOTORIOUS) or mob:getHPP() > 75) and + mob:getFamily() ~= 91 -- Pandemonium Lamp can always use + then return 1 end diff --git a/scripts/actions/mobskills/serpentine_tail.lua b/scripts/actions/mobskills/serpentine_tail.lua index dced39edc85..832adb23cc9 100644 --- a/scripts/actions/mobskills/serpentine_tail.lua +++ b/scripts/actions/mobskills/serpentine_tail.lua @@ -9,23 +9,12 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1796 then - return 0 - else - return 1 - end - elseif mob:getFamily() == 313 then -- Tinnin - if mob:getAnimationSub() < 2 and target:isBehind(mob, 48) then - return 0 - else - return 1 - end + -- Can always use, only if target is behind and not exclusive like spike flail + if target:isBehind(mob, 96) then + return 0 + else + return 1 end - - return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/somersault_kick.lua b/scripts/actions/mobskills/somersault_kick.lua index 6a5c68d4b39..e9c1087a70a 100644 --- a/scripts/actions/mobskills/somersault_kick.lua +++ b/scripts/actions/mobskills/somersault_kick.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1639 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/tail_slap.lua b/scripts/actions/mobskills/tail_slap.lua index 5ed70cc1141..7518a4d3d1e 100644 --- a/scripts/actions/mobskills/tail_slap.lua +++ b/scripts/actions/mobskills/tail_slap.lua @@ -6,16 +6,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1643 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/tenebrous_mist.lua b/scripts/actions/mobskills/tenebrous_mist.lua index a4e74b866b7..ab1afe7201f 100644 --- a/scripts/actions/mobskills/tenebrous_mist.lua +++ b/scripts/actions/mobskills/tenebrous_mist.lua @@ -9,15 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - if mobSkin == 1805 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/thunderstrike.lua b/scripts/actions/mobskills/thunderstrike.lua index 9609b89433c..a0aa3cb7407 100644 --- a/scripts/actions/mobskills/thunderstrike.lua +++ b/scripts/actions/mobskills/thunderstrike.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1805 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/thundris_shriek.lua b/scripts/actions/mobskills/thundris_shriek.lua index d31bb18ab50..4ed96adc8d1 100644 --- a/scripts/actions/mobskills/thundris_shriek.lua +++ b/scripts/actions/mobskills/thundris_shriek.lua @@ -10,26 +10,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1840 then - return 0 - else - return 1 - end - end - - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1839 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/actions/mobskills/tourbillion.lua b/scripts/actions/mobskills/tourbillion.lua index 699e77dc2b9..87c87e406c9 100644 --- a/scripts/actions/mobskills/tourbillion.lua +++ b/scripts/actions/mobskills/tourbillion.lua @@ -9,16 +9,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 316 then - local mobSkin = mob:getModelId() - - if mobSkin == 1805 then - return 0 - else - return 1 - end - end - --[[TODO: Khimaira should only use this when its wings are up, which is animationsub() == 0. There's no system to put them "down" yet, so it's not really fair to leave it active. Tyger's fair game, though. :)]] diff --git a/scripts/actions/mobskills/wind_shear.lua b/scripts/actions/mobskills/wind_shear.lua index 2813c4beac0..36d55898333 100644 --- a/scripts/actions/mobskills/wind_shear.lua +++ b/scripts/actions/mobskills/wind_shear.lua @@ -10,16 +10,6 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if mob:getFamily() == 91 then - local mobSkin = mob:getModelId() - - if mobSkin == 1746 then - return 0 - else - return 1 - end - end - return 0 end diff --git a/scripts/effects/magic_acc_down.lua b/scripts/effects/magic_acc_down.lua index b759479c050..96ff7e01059 100644 --- a/scripts/effects/magic_acc_down.lua +++ b/scripts/effects/magic_acc_down.lua @@ -4,12 +4,18 @@ local effectObject = {} effectObject.onEffectGain = function(target, effect) + if effect:getPower() > 100 then + effect:setPower(50) + end + + target:addMod(xi.mod.MACC, -effect:getPower()) end effectObject.onEffectTick = function(target, effect) end effectObject.onEffectLose = function(target, effect) + target:delMod(xi.mod.MACC, -effect:getPower()) end return effectObject diff --git a/scripts/effects/magic_atk_down.lua b/scripts/effects/magic_atk_down.lua index 997006d338e..36e37791153 100644 --- a/scripts/effects/magic_atk_down.lua +++ b/scripts/effects/magic_atk_down.lua @@ -4,12 +4,18 @@ local effectObject = {} effectObject.onEffectGain = function(target, effect) + if effect:getPower() > 100 then + effect:setPower(50) + end + + target:addMod(xi.mod.MATT, -effect:getPower()) end effectObject.onEffectTick = function(target, effect) end effectObject.onEffectLose = function(target, effect) + target:delMod(xi.mod.MACC, -effect:getPower()) end return effectObject diff --git a/scripts/enum/effect.lua b/scripts/enum/effect.lua index c00eddd5a5a..f28a55acc12 100644 --- a/scripts/enum/effect.lua +++ b/scripts/enum/effect.lua @@ -675,6 +675,7 @@ xi.effect = HYSTERIA = 804, -- Used for Hysteroanima to stop after readying a weaponskill with no msg. TOMAHAWK = 805, -- Silent status effect inflicted by a Warrior using the "Tomahawk" job ability NUKE_WALL = 806, -- Custom effect for NM type mobs only. + BRAINJACK = 807, -- Charm with a DoT -- 789 -- 807-1022 diff --git a/scripts/zones/Aydeewa_Subterrane/IDs.lua b/scripts/zones/Aydeewa_Subterrane/IDs.lua index 5061345242c..43c1de5f0ec 100644 --- a/scripts/zones/Aydeewa_Subterrane/IDs.lua +++ b/scripts/zones/Aydeewa_Subterrane/IDs.lua @@ -20,6 +20,8 @@ zones[xi.zone.AYDEEWA_SUBTERRANE] = MEMBERS_LEVELS_ARE_RESTRICTED = 7023, -- Your party is unable to participate because certain members' levels are restricted. FISHING_MESSAGE_OFFSET = 7057, -- You can't fish here. MINING_IS_POSSIBLE_HERE = 7328, -- Mining is possible here if you have . + PW_WHO_DARES = 7964, -- Who dares disturb these gates? Pathetic mortal, what foolishness has brought you here? No matter, your fate is now irrevocably sealed. Come now, do not fear. Embrace your death! + PW_END_OF_NOTHING = 7965, -- This is the end...of nothing... Defeating me proves only that you harbor the seeds of evil within your trembling flesh... Someday...they shall come...to fruition... SENSE_OMINOUS_PRESENCE = 8007, -- You sense an ominous presence... BLOOD_STAINS = 8013, -- The ground is smeared with bloodstains... DRAWS_NEAR = 8038, -- Something draws near! diff --git a/scripts/zones/Aydeewa_Subterrane/mobs/Pandemonium_Lamp.lua b/scripts/zones/Aydeewa_Subterrane/mobs/Pandemonium_Lamp.lua new file mode 100644 index 00000000000..69c4e711ef2 --- /dev/null +++ b/scripts/zones/Aydeewa_Subterrane/mobs/Pandemonium_Lamp.lua @@ -0,0 +1,95 @@ +----------------------------------- +-- Area: Aydeewa Subterrane +-- Mob: Pandemonium Warden Pet +----------------------------------- +local ID = zones[xi.zone.AYDEEWA_SUBTERRANE] +----------------------------------- +local entity = {} + +entity.onMobInitialize = function(mob) +end + +entity.onMobMagicPrepare = function(mob, target, spell) + -- Don't use AoE elemental magic, choose random other tier 4 or 5 + if + spell:getID() >= xi.magic.spell.FIRAGA and + spell:getID() <= xi.magic.spell.WATERGA_V + then + local newSpell = xi.magic.spell.FIRE + 5 * math.random(1, 6) - math.random(1, 2) + return newSpell + end +end + +entity.onMobSpawn = function(mob) + mob:setMobMod(xi.mobMod.GIL_MAX, -1) + mob:setMobMod(xi.mobMod.NO_DROPS, 1) + mob:setMobMod(xi.mobMod.EXP_BONUS, -101) + mob:setMod(xi.mod.SLEEPRES, 100) + mob:setMod(xi.mod.LULLABYRES, 100) +end + +entity.onMobEngaged = function(mob, target) + local pw = GetMobByID(ID.mob.PANDEMONIUM_WARDEN) + if not pw:isEngaged() then + pw:engage(target:getTargID()) -- Engage via the zone's local ID of the Lamp's target + end + + -- 6:34 to 6:46 in this video: https://www.youtube.com/watch?v=T_Us2Tmlm-E + -- shows approx 12-second delay on astral flow + mob:timer(12000, function(mobArg) + local abilityID = 0 + local modelID = mobArg:getModelId() + switch (modelID) : caseof + { + [791] = function() -- Carbuncle + abilityID = 919 + end, + [792] = function() -- Fenrir + abilityID = 839 + end, + [793] = function() -- Ifrit + abilityID = 913 + end, + [794] = function() -- Titan + abilityID = 914 + end, + [795] = function() -- Leviathan + abilityID = 915 + end, + [796] = function() -- Garuda + abilityID = 916 + end, + [797] = function() -- Shiva + abilityID = 917 + end, + [798] = function() -- Ramuh + abilityID = 918 + end, + } + if abilityID > 0 then + -- These changes reduce dmg greatly for a well-geared TANK, but will definitely still murder a non-tank + -- for astral flow to scale better with mdef. Resets on mob spawn. + mobArg:setMod(xi.mod.MATT, 0) + -- for astral flow to scale dmg/acc for player INT values + mobArg:setMod(xi.mod.INT, -30) + -- for astral flow to partial resist better when compared to player meva/resistances + mobArg:setMod(xi.mod.MACC, -50) + + mobArg:useMobAbility(abilityID) + + mobArg:timer(2000, function(mobArg2) + mobArg2:setUnkillable(false) + mobArg2:setHP(0) + end) + end + end) +end + +entity.onMobDeath = function(mob, player, isKiller) + -- cleanup quickly since spawning is blocked until mob is fully despawned, but this runs for everyone in the party, so wrap in if-check + if mob:isSpawned() then + DespawnMob(mob:getID()) + end +end + +return entity diff --git a/scripts/zones/Aydeewa_Subterrane/mobs/Pandemonium_Warden.lua b/scripts/zones/Aydeewa_Subterrane/mobs/Pandemonium_Warden.lua index 6859cc56267..08a08d707eb 100644 --- a/scripts/zones/Aydeewa_Subterrane/mobs/Pandemonium_Warden.lua +++ b/scripts/zones/Aydeewa_Subterrane/mobs/Pandemonium_Warden.lua @@ -1,194 +1,528 @@ ----------------------------------- -- Area: Aydeewa Subterrane -- ZNM: Pandemonium Warden +-- +-- !pos 200 33 -140 68 +-- Spawn with Pandemonium key: !additem 2572 +-- Wiki: https://ffxiclopedia.fandom.com/wiki/Pandemonium_Warden +-- Videos: https://youtu.be/oOCCjH8isiA +-- https://www.youtube.com/watch?v=T_Us2Tmlm-E +-- Notes: Lamia uses eagle eye shot, safe to say each phase has the respective 2hr? +--[[ +Outline of fight: +Each phase has a starting HP amount. The even-numbered phases start low enough that low-hp mobskills are immediately available and less TP is required to use a mobskill +Odd-numbered phases are dverger form, and are hard-coded to use cackle, then hellsnap, then change phase. No damage can be inflicted. And pets do not cast spells +Each phase, PW has the job's respective 2-hour ability at 50% of the starting HP +Final phase is the "true form" and every 25% uses astral flow, which resummons all pets as well as 8 avatars. + Everyone except tank should be prepared to get away when this happens, though there's plenty of time to run away unless you get stun locked by pet spells + "All avatars are summoned at once, and with them plus the lamps up, its hard to move your character." + "You will probably get locked in place and die from game mechanics alone." +During phase change: + PW is stunned to interrupt any current action (since it's not dying, just disappearing) + PL are despawned + PW/PL are disappeared and model/animation sub are adjusted + PW reappears and stun is removed + PL are respawned + All PW buffs are wiped + All mobskill LUA were mostly cleared of family-specific restrictions to let this single PW LUA handle everything + phase change sets skill and spell list for PW and PL +If full wipe happens, DoT will keep PW from regen, which will keep him in current form. If he regens past his phase HP, he resets to phase 1 +In each even phase, he has access to the respective mobskills of that mob model: +- Chariots + - Phases 2, 4, 6, 8 + - 10k HP each + - Pets: Archaic Gears +- Gulool Ja Ja + - Phase 10 + - 15000 HP + - Pets: Mamool Ja +- Medusa + - Phase 12 + - 15000 HP + - Pets: Lamiae +- Gurfurlur the Menacing + - Phase 14 + - 15000 HP + - Pets: Trolls +- Khimaira + - Phase 16 + - 20000 HP + - Pets: Puks +- Hydra + - Phase 18 + - 20000 HP + - Pets: Dahaks +- Cerberus + - Phase 20 + - 20000 HP + - Pets: Bombs +- Dvergr + - Phase 21 + - 147000 HP + - Pets: Miniature Dvergr +--]] ----------------------------------- local ID = zones[xi.zone.AYDEEWA_SUBTERRANE] ----------------------------------- local entity = {} -- Pet Arrays, we'll alternate between phases -local petIDs = {} -petIDs[0] = { ID.mob.PANDEMONIUM_WARDEN + 1, ID.mob.PANDEMONIUM_WARDEN + 2, ID.mob.PANDEMONIUM_WARDEN + 3, ID.mob.PANDEMONIUM_WARDEN + 4, ID.mob.PANDEMONIUM_WARDEN + 5, ID.mob.PANDEMONIUM_WARDEN + 6, ID.mob.PANDEMONIUM_WARDEN + 7, ID.mob.PANDEMONIUM_WARDEN + 8 } -petIDs[1] = { ID.mob.PANDEMONIUM_WARDEN + 9, ID.mob.PANDEMONIUM_WARDEN + 10, ID.mob.PANDEMONIUM_WARDEN + 11, ID.mob.PANDEMONIUM_WARDEN + 12, ID.mob.PANDEMONIUM_WARDEN + 13, ID.mob.PANDEMONIUM_WARDEN + 14, ID.mob.PANDEMONIUM_WARDEN + 15, ID.mob.PANDEMONIUM_WARDEN + 16 } - --- Phase Arrays Dverg, Char1, Dverg, Char2, Dverg, Char3, Dverg, Char4, Dverg, Mamo, Dverg, Lamia, Dverg, Troll, Dverg, Cerb, Dverg, Hydra, Dverg, Khim, Dverg --- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -local triggerHPP = { 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1 } -local mobHP = { 10000, 147000, 10000, 147000, 10000, 147000, 10000, 147000, 15000, 147000, 15000, 147000, 15000, 147000, 20000, 147000, 20000, 147000, 20000, 147000 } -local mobModelID = { 1825, 1840, 1825, 1840, 1825, 1840, 1825, 1840, 1863, 1840, 1865, 1840, 1867, 1840, 1793, 1840, 1796, 1840, 1805, 1840 } -local petModelID = { 1823, 1841, 1821, 1841, 1825, 1841, 1824, 1841, 1639, 1841, 1643, 1841, 1680, 1841, 281, 1841, 421, 1841, 1746, 1839 } -local skillID = { 1000, 316, 1001, 316, 1002, 316, 1003, 316, 285, 316, 725, 316, 326, 316, 62, 316, 164, 316, 168, 316 } - --- Avatar Arrays Shiva, Ramuh, Titan, Ifrit, Levia, Garud, Fenri, Carby -local avatarAbilities = { 917, 918, 914, 913, 915, 916, 839, 919 } -local avatarSkins = { 22, 23, 19, 18, 20, 21, 17, 16 } +local petIDs = +{ + [0] = + { + ID.mob.PANDEMONIUM_WARDEN + 1, + ID.mob.PANDEMONIUM_WARDEN + 2, + ID.mob.PANDEMONIUM_WARDEN + 3, + ID.mob.PANDEMONIUM_WARDEN + 4, + ID.mob.PANDEMONIUM_WARDEN + 5, + ID.mob.PANDEMONIUM_WARDEN + 6, + ID.mob.PANDEMONIUM_WARDEN + 7, + ID.mob.PANDEMONIUM_WARDEN + 8 + }, + [1] = + { + ID.mob.PANDEMONIUM_WARDEN + 9, + ID.mob.PANDEMONIUM_WARDEN + 10, + ID.mob.PANDEMONIUM_WARDEN + 11, + ID.mob.PANDEMONIUM_WARDEN + 12, + ID.mob.PANDEMONIUM_WARDEN + 13, + ID.mob.PANDEMONIUM_WARDEN + 14, + ID.mob.PANDEMONIUM_WARDEN + 15, + ID.mob.PANDEMONIUM_WARDEN + 16 + }, +} + +-- Phase Arrays Dverg, Char1, Dverg, Char2, Dverg, Char3, Dverg, Char4, Dverg,GuloolJ, Dverg, Medusa, Dverg,Gurfurl, Dverg, Khim, Dverg, Hydra, Dverg, Cerb, Dverg +-- WAR WAR WAR WAR NIN, RNG, MNK, WAR, WAR, WAR, +-- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 +local mobPhaseHP = { 147000, 10000, 147000, 10000, 147000, 10000, 147000, 10000, 147000, 15000, 147000, 15000, 147000, 15000, 147000, 20000, 147000, 20000, 147000, 20000, 147000 } +local mobModelID = { 1840, 1825, 1840, 1824, 1840, 1822, 1840, 1823, 1840, 1863, 1840, 1865, 1840, 1867, 1840, 1805, 1840, 1796, 1840, 1793, 1840 } +local mobSkillID = { 316, 1000, 316, 1001, 316, 1002, 316, 1003, 316, 285, 316, 725, 316, 326, 316, 168, 316, 164, 316, 62, 316 } +local mobSpecID = { 0, 688, 0, 688, 0, 688, 0, 688, 0, 731, 0, 735, 0, 690, 0, 688, 0, 688, 0, 688, 0 } +local mobSpellID = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } +-- pets corpslight, gears, clight, gears, clight, gears, clight, gears, clight,MamoolJ, clight, Lamiae, clight, Trolls, clight, Puks, clight, Dahaks, clight, Bombs,MiniDverg +local petModelID = { 1841, 1820, 1841, 1820, 1841, 1820, 1841, 1820, 1841, 1639, 1841, 1643, 1841, 1682, 1841, 1746, 1841, 421, 1841, 281, 1839 } +local petSkillID = { 0, 150, 0, 150, 0, 150, 0, 150, 0, 176, 0, 171, 0, 246, 0, 198, 0, 5009, 0, 300, 316 } +local petSpellID = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 2 } +--[[ + Their (pet's) form varies depending on what mob the Warden is currently mimicking: + No phases seem to use AoE elemental magic, and without reworking the core code below if phase ~= 21 + Chariots - Archaic Gears + Melee only + Gulool Ja Ja - Mamool Ja + Some cast blm spells + Medusa - Lamiae + some cast rdm spells + Gurfurlur the Menacing - Trolls + some cast whm, some cast ninjutsu? (to keep logic simple, whm spell list is chosen) + Hydra - Dahaks + Melee only + Khimaira - Puks + Melee only + Cerberus - Bombs + Melee only + Dvergr - Miniature Dvergr + All cast blm spells +]] + +local pwFuncs = {} +pwFuncs.phaseChange = function(mob) + local phase = mob:getLocalVar('phase') + local effects = mob:getStatusEffects() + + -- remove all status effects and stun to interrupt any current action + for i = 1, #effects do + mob:delStatusEffect(effects[i]:getEffectType()) + end + + if phase > 1 then + mob:addStatusEffect(xi.effect.STUN, 1, 0, 10) + end + + mob:setLocalVar('usedSpecial', 0) + + mob:setMod(xi.mod.UDMGPHYS , 0) + mob:setMod(xi.mod.UDMGRANGE , 0) + mob:setMod(xi.mod.UDMGBREATH , 0) + mob:setMod(xi.mod.UDMGMAGIC , 0) + if phase == 21 then -- Prepare for death + mob:hideHP(false) + mob:setUnkillable(false) + end + + -- Change phase + + if phase > 1 then + mob:setStatus(xi.status.DISAPPEAR) + mob:setAutoAttackEnabled(false) + mob:setMagicCastingEnabled(false) + mob:setMobAbilityEnabled(false) + + -- disappear for a bit, then come back with the new mob model + mob:timer(4000, function(mobArg) + mobArg:setStatus(xi.status.UPDATE) + mobArg:setAutoAttackEnabled(true) + mobArg:setMagicCastingEnabled(true) + mobArg:setMobAbilityEnabled(true) + mobArg:delStatusEffectSilent(xi.effect.STUN) + -- ensure we don't lose claim from forced disengage + local target = mobArg:getTarget() + if target then + mobArg:updateClaim(target) + end + end) + + mob:setAnimationSub(0) + mob:setMod(xi.mod.STUNRES, -10) + -- Number of crits to lose a head, re-randoming + mob:setLocalVar('CritToTheFace', math.random(10, 30)) + mob:setLocalVar('crits', 0) + -- dverger phases + if phase % 2 == 1 then + mob:setBehaviour(bit.band(mob:getBehaviour(), bit.bnot(xi.behavior.NO_TURN))) + -- takes no damage during intermediate dverger phases + if phase ~= 21 then + mob:setMod(xi.mod.UDMGPHYS , -100) + mob:setMod(xi.mod.UDMGRANGE , -100) + mob:setMod(xi.mod.UDMGBREATH , -100) + mob:setMod(xi.mod.UDMGMAGIC , -100) + mob:setMod(xi.mod.STUNRES, 100) + end + + mob:timer(5000, function(mobArg) + mobArg:setTP(3000) -- Cackle unless final phase (some other skill will be chosen) + end) + else + if phase == 16 or phase == 18 or phase == 20 then + mob:setBehaviour(bit.bor(mob:getBehaviour(), xi.behavior.NO_TURN)) + else + mob:setBehaviour(bit.band(mob:getBehaviour(), bit.bnot(xi.behavior.NO_TURN))) + end + end + + pwFuncs.despawnPets() + mob:timer(6000, function(mobArg) + pwFuncs.popPets(mobArg) + end) + end + + mob:setTP(0) + + mob:setModelId(mobModelID[phase]) + mob:setHP(mobPhaseHP[phase]) + --mob:setSkillList(mobSkillID[phase]) + mob:setMobMod(xi.mobMod.SKILL_LIST, mobSkillID[phase]) + mob:setSpellList(mobSpellID[phase]) +end + +pwFuncs.despawnPets = function(modulo) + -- Despawn pets + for i = 0, 1 do + if modulo == nil or modulo ~= i then + for j = 1, 8 do + if GetMobByID(petIDs[i][j]):isSpawned() then + DespawnMob(petIDs[i][j]) + end + end + end + end +end + +pwFuncs.handlePet = function(mob, newPet, oldPet, target, modelId, phase) + newPet:disengage() + newPet:setTP(0) + newPet:setHP(newPet:getMaxHP()) + newPet:setMP(newPet:getMaxMP()) + newPet:setModelId(modelId) + if phase ~= nil then + local petSkills = 0 + local petSpells = 0 + if phase > 0 then + petSkills = petSkillID[phase] + petSpells = petSpellID[phase] + end + + if modelId == 1820 then + newPet:setAnimationSub(2) + else + newPet:setAnimationSub(0) + end + + -- spells before spawn + -- newPet:setMobMod(xi.mobMod.SPELL_LIST, petSpells) + newPet:spawn() + newPet:setSpellList(petSpells) + newPet:setMobMod(xi.mobMod.SKILL_LIST, petSkills) + --newPet:setSkillList(petSkills) + + -- Don't autoattack if no mobskills (corpselights don't auto or cast) + if petSkills > 0 then + newPet:setMobAbilityEnabled(true) + newPet:setAutoAttackEnabled(true) + else + newPet:setMobAbilityEnabled(false) + newPet:setAutoAttackEnabled(false) + end + + -- before final phase, not all lamps in a wave use spells + if petSpells > 0 and (phase == 21 or math.random(1, 2) == 1) then + newPet:setMagicCastingEnabled(true) + newPet:setMobMod(xi.mobMod.MAGIC_DELAY, 4) + newPet:setMobMod(xi.mobMod.HP_STANDBACK, 70) + else + newPet:setMagicCastingEnabled(false) + newPet:setMobMod(xi.mobMod.HP_STANDBACK, 0) + end + + newPet:setUnkillable(false) + else + -- astral flow avatars + newPet:spawn() + newPet:setMobMod(xi.mobMod.HP_STANDBACK, 0) + end + + newPet:setPos(mob:getXPos() + 2 * math.random(-2, 2), mob:getYPos(), mob:getZPos() + 2 * math.random(-2, 2)) + newPet:updateEnmity(target) +end + +pwFuncs.popPets = function(mob) + local phase = mob:getLocalVar('phase') + if phase ~= 21 then + pwFuncs.despawnPets(phase % 2) -- messes with astral flow doubled-up pets + end + + local target = mob:getTarget() + + if target ~= nil then + -- Handle pets + for i = 1, 8 do + local newPet = GetMobByID(petIDs[phase % 2][i]) + local oldPet = GetMobByID(petIDs[(phase - 1) % 2][i]) + pwFuncs.handlePet(mob, newPet, oldPet, target, petModelID[phase], phase) + end + end +end entity.onMobSpawn = function(mob) + mob:setMod(xi.mod.STATUSRES, 50) + + mob:setMobMod(xi.mobMod.ALLI_HATE, 30) + mob:setMobMod(xi.mobMod.DRAW_IN, 1) + mob:setMobMod(xi.mobMod.HP_STANDBACK, 0) + mob:setMobMod(xi.mobMod.GIL_MIN, 27000) + mob:setMobMod(xi.mobMod.GIL_MAX, 27000) mob:setMod(xi.mod.DEF, 450) mob:setMod(xi.mod.MEVA, 380) mob:setMod(xi.mod.MDEF, 50) + + mob:setMod(xi.mod.MATT, 100) + mob:setMod(xi.mod.MACC, 150) + -- Make sure model is reset back to start - mob:setModelId(1840) + mob:setModelId(mobModelID[1]) -- Prevent death and hide HP until final phase mob:setUnkillable(true) mob:hideHP(true) - -- Two hours to forced depop + -- Two hours to forced depop, 5 minutes after disengage mob:setLocalVar('PWDespawnTime', os.time() + 7200) + -- "Pandemonium Warden will still despawn normally if left unclaimed for 3 minutes. If a wipe is required then he will still need to be tagged every couple of minutes." + mob:setMobMod(xi.mobMod.IDLE_DESPAWN, 180) + mob:setLocalVar('phase', 1) mob:setLocalVar('astralFlow', 1) + + pwFuncs.phaseChange(mob) + mob:setMagicCastingEnabled(false) end entity.onMobDisengage = function(mob) - -- Make sure model is reset back to start - mob:setModelId(1840) - mob:setMobMod(xi.mobMod.SKILL_LIST, 316) + -- "If all individuals who have developed enmity die, Pandemonium Warden will return to his spawn point, with his train of lamps, and will not be aggressive to any non-combat action" + pwFuncs.despawnPets() + mob:setAggressive(false) + mob:setMagicCastingEnabled(false) +end - -- Prevent death and hide HP until final phase - mob:setUnkillable(true) - mob:hideHP(true) +entity.onMobRoam = function(mob) + local phase = mob:getLocalVar('phase') + -- unsure why this is needed, but PW will regen 10% hp even with DoT unless we also set NO_REST + if mob:getMod(xi.mod.REGEN_DOWN) > 0 then + mob:setMobMod(xi.mobMod.NO_REST, 1) + else + mob:setMobMod(xi.mobMod.NO_REST, 0) + end - -- Reset phases (but not despawn timer) - mob:setLocalVar('phase', 1) - mob:setLocalVar('astralFlow', 1) + -- Reset phases (but not despawn timer) when rested to phase's starting HP + if phase ~= 1 and mob:getHP() >= mobPhaseHP[phase] then + -- Prevent death and hide HP until final phase + mob:setUnkillable(true) + mob:hideHP(true) - -- Despawn pets - for i = 0, 1 do - for j = 1, 8 do - if GetMobByID(petIDs[i][j]):isSpawned() then - DespawnMob(petIDs[i][j]) - end - end + mob:setLocalVar('phase', 1) + mob:setLocalVar('astralFlow', 1) + + pwFuncs.phaseChange(mob) + pwFuncs.despawnPets(mob) end end entity.onMobEngaged = function(mob, target) - -- pop pets - for i = 1, 8 do - local pet = GetMobByID(petIDs[1][i]) - pet:setModelId(1841) - pet:spawn() - pet:updateEnmity(target) + pwFuncs.popPets(mob, target) + + local phase = mob:getLocalVar('phase') + + if phase % 2 == 1 then + mob:setTP(3000) end + + mob:timer(500, function(mobArg) + mobArg:setMagicCastingEnabled(true) -- ensure engages with a TP move always + end) + + mob:setMobAbilityEnabled(true) end -local function handlePet(mob, newPet, oldPet, target, modelId) - if oldPet:isSpawned() then - DespawnMob(oldPet:getID()) +entity.onMobWeaponSkillPrepare = function(mob, target) + local phase = mob:getLocalVar('phase') + if phase ~= 21 and phase % 2 == 1 then + return 2115 end +end - newPet:setModelId(modelId) - newPet:spawn() - newPet:setPos(mob:getXPos() + math.random(-2, 2), mob:getYPos(), mob:getZPos() + math.random(-2, 2)) - newPet:updateEnmity(target) +entity.onMobSkillTarget = function(target, mob, skill) +end + +entity.onMobWeaponSkill = function(target, mob, skill) + local phase = mob:getLocalVar('phase') + if phase ~= 21 and (phase % 2) == 1 and mob:getLocalVar('phaseChange') ~= 1 then + -- transitional Dvergar stage, uses cackle, then hellsnap, then changes again + local hellsnapID = 2113 + if skill:getID() ~= hellsnapID then + mob:timer(5000, function(mobArg) + mobArg:useMobAbility(hellsnapID) -- hellsnap + end) + + mob:timer(8000, function(mobArg) + mobArg:setLocalVar('phaseChange', 1) + end) + end + end + + mob:setLocalVar('timeSinceWS', os.time()) end entity.onMobFight = function(mob, target) -- Init Vars + local mobHP = mob:getHP() local mobHPP = mob:getHPP() local depopTime = mob:getLocalVar('PWDespawnTime') local phase = mob:getLocalVar('phase') local astral = mob:getLocalVar('astralFlow') - local pets = {} - for i = 0, 1 do - pets[i] = {} - for j = 1, 8 do - pets[i][j] = GetMobByID(petIDs[i][j]) - end - end + local phaseSpecialID = mob:getLocalVar('usedSpecial') ~= 1 and mobSpecID[phase] or 0 -- 'special' refers to 2-hour ability -- Check for phase change - if phase < 21 and mobHPP <= triggerHPP[phase] then - if phase == 20 then -- Prepare for death - mob:hideHP(false) - mob:setUnkillable(false) - end + if phase < 21 and mobHP < 1000 and mob:getLocalVar('phaseChange') ~= 1 then + mob:setLocalVar('phaseChange', 1) + end - -- Change phase - mob:setTP(0) - mob:setModelId(mobModelID[phase]) - mob:setHP(mobHP[phase]) - mob:setMobMod(xi.mobMod.SKILL_LIST, skillID[phase]) + if mob:actionQueueEmpty() and mob:getLocalVar('timeSinceWS') < os.time() - 5 then + if mob:getLocalVar('phaseChange') == 1 then + mob:setLocalVar('phaseChange', 0) - -- Handle pets - for i = 1, 8 do - local oldPet = pets[phase % 2][i] - local newPet = pets[(phase - 1) % 2][i] - newPet:updateEnmity(target) - newPet:setMobMod(xi.mobMod.MAGIC_DELAY, 4) - handlePet(mob, newPet, oldPet, target, petModelID[phase]) - end + -- Increment phase + -- phase = mob:getLocalVar('phase') + 1 + mob:setLocalVar('phase', mob:getLocalVar('phase') + 1) - -- Increment phase - mob:setLocalVar('phase', phase + 1) + pwFuncs.phaseChange(mob) + -- Or, check for Astral Flow + elseif phase == 21 and astral < 4 and mobHPP <= (99 - 25 * astral) then + mob:setLocalVar('astralFlow', astral + 1) - -- Or, check for Astral Flow - elseif phase == 21 and astral < 9 and mobHPP <= (100 - 25 * astral) then - for i = 1, 8 do - local oldPet = pets[astral % 2][i] - local newPet = pets[(astral - 1) % 2][i] - if i == 1 then - newPet:updateEnmity(target) - local astralRand = math.random(1, 8) - handlePet(mob, newPet, oldPet, target, avatarSkins[astralRand]) - newPet:useMobAbility(avatarAbilities[astralRand]) - else - handlePet(mob, newPet, oldPet, target, 1839) + -- ensure pets are there + pwFuncs.popPets(mob) + -- 'All avatars are summoned at once, and with them plus the lamps up, its hard to move your character.' + -- 'You will probably get locked in place and die from game mechanics alone.' + for i = 1, 8 do + -- during final phase, pets are always index 1 and astral flows are always index 0 + local newPet = GetMobByID(petIDs[0][i]) + local oldPet = GetMobByID(petIDs[1][i]) + pwFuncs.handlePet(mob, newPet, oldPet, target, 790 + i) + newPet:setUnkillable(true) + newPet:setAutoAttackEnabled(false) + newPet:setMagicCastingEnabled(false) + newPet:setMobAbilityEnabled(false) + end + else + -- use non-dverger 2-hour at 50% hp of current phase + if phaseSpecialID > 0 then + local halfHP = mobPhaseHP[phase] / 2 + if mobHP < halfHP then + mob:useMobAbility(phaseSpecialID) + mob:setLocalVar('usedSpecial', 1) + end end - end - - -- Increment astral - mob:setLocalVar('astralFlow', astral + 1) - -- Or, at least make sure pets weren't drug off - else - --[[ Unused - for i = 1, 8 do - local pet = nil - if phase == 21 then - pet = pets[astral % 2][i] - else - pet = pets[phase % 2][i] + -- make sure pets weren't dragged off... + for i = 0, 1 do + for j = 1, 8 do + local pet = GetMobByID(petIDs[i][j]) + if pet:isAlive() and not pet:isEngaged() then + pet:updateEnmity(target) + end + end end end - ]]-- end -- Check for time limit, too if os.time() > depopTime and mob:actionQueueEmpty() then - for i = 0, 1 do - for j = 1, 8 do - if pets[i][j]:isSpawned() then - DespawnMob(petIDs[i][j]) - end - end - end - DespawnMob(ID.mob.PANDEMONIUM_WARDEN) + -- printf('Timer expired at %i. Despawning Pandemonium Warden.', depopTime) end end entity.onMobDeath = function(mob, player, optParams) - player:addTitle(xi.title.PANDEMONIUM_QUELLER) + if optParams.isKiller or optParams.noKiller then + mob:showText(mob, ID.text.PW_END_OF_NOTHING) + end - -- Despawn pets - for i = 0, 1 do - for j = 1, 8 do - if GetMobByID(petIDs[i][j]):isSpawned() then - DespawnMob(petIDs[i][j]) - end - end + if player ~= nil then + player:addTitle(xi.title.PANDEMONIUM_QUELLER) end + + pwFuncs.despawnPets() end entity.onMobDespawn = function(mob) - -- Despawn pets - for i = 0, 1 do - for j = 1, 8 do - if GetMobByID(petIDs[i][j]):isSpawned() then - DespawnMob(petIDs[i][j]) + pwFuncs.despawnPets() +end + +entity.onCriticalHit = function(mob) + local phase = mob:getLocalVar('phase') + if phase == 18 then -- Hydra phase + local critNum = mob:getLocalVar('crits') + + if critNum + 1 > mob:getLocalVar('CritToTheFace') then -- Lose a head + if mob:getAnimationSub() == 0 then + mob:setAnimationSub(1) + mob:setLocalVar('headTimer', os.time() + math.random(60, 190)) + elseif mob:getAnimationSub() == 1 then + mob:setAnimationSub(2) + mob:setLocalVar('headTimer', os.time() + math.random(60, 190)) + else + -- Meh end + + -- Number of crits to lose a head, re-randoming + mob:setLocalVar('CritToTheFace', math.random(10, 30)) + + critNum = 0 -- reset the crits on the NM + else + critNum = critNum + 1 end + + mob:setLocalVar('crits', critNum) end end diff --git a/scripts/zones/Aydeewa_Subterrane/npcs/qm2.lua b/scripts/zones/Aydeewa_Subterrane/npcs/qm2.lua index 45a9c869329..6bb210e102a 100644 --- a/scripts/zones/Aydeewa_Subterrane/npcs/qm2.lua +++ b/scripts/zones/Aydeewa_Subterrane/npcs/qm2.lua @@ -14,7 +14,8 @@ entity.onTrade = function(player, npc, trade) then -- Trade Pandemonium Key player:confirmTrade() - player:messageSpecial(ID.text.DRAWS_NEAR) + local mob = GetMobByID(ID.mob.PANDEMONIUM_WARDEN) + mob:showText(mob, ID.text.PW_WHO_DARES) end end