From 84c540dcdd0336b536abecc375d430d8ccc356b0 Mon Sep 17 00:00:00 2001 From: Critical <48370698+CriticalXI@users.noreply.github.com> Date: Sun, 28 Dec 2025 16:11:13 -0700 Subject: [PATCH 1/3] [lua, sql] CoP 8-4 Dawn Adjustments --- .../actions/mobskills/auroral_uppercut.lua | 15 +- .../actions/mobskills/bastion_of_twilight.lua | 6 +- .../actions/mobskills/chains_of_apathy.lua | 18 +- .../actions/mobskills/chains_of_arrogance.lua | 18 +- .../actions/mobskills/chains_of_cowardice.lua | 18 +- scripts/actions/mobskills/chains_of_envy.lua | 18 +- scripts/actions/mobskills/chains_of_rage.lua | 18 +- scripts/actions/mobskills/empty_salvation.lua | 16 +- .../mobskills/infernal_deliverance.lua | 5 +- scripts/actions/mobskills/luminous_lance.lua | 35 +- .../actions/mobskills/malevolent_blessing.lua | 5 +- .../actions/mobskills/nullifying_dropkick.lua | 24 +- .../actions/mobskills/pestilent_penance.lua | 5 +- scripts/actions/mobskills/prishe_item_1.lua | 11 - scripts/actions/mobskills/prishe_item_2.lua | 36 -- scripts/actions/mobskills/rejuvenation.lua | 2 +- scripts/actions/mobskills/revelation.lua | 10 +- .../actions/mobskills/seal_of_quiescence.lua | 5 +- .../mobskills/wheel_of_impregnability.lua | 6 +- .../actions/mobskills/winds_of_oblivion.lua | 3 - scripts/actions/spells/black/meteor.lua | 2 + scripts/actions/spells/white/arise.lua | 7 +- scripts/actions/spells/white/raise.lua | 10 +- scripts/actions/spells/white/raise_ii.lua | 10 +- scripts/actions/spells/white/raise_iii.lua | 10 +- .../battlefields/Empyreal_Paradox/dawn.lua | 86 +++-- scripts/enum/behavior.lua | 1 + scripts/enum/mob_pool.lua | 2 + scripts/enum/mob_skill.lua | 27 +- scripts/globals/spells/damage_spell.lua | 2 +- .../zones/Empyreal_Paradox/mobs/Prishe.lua | 287 +++++++++++++--- .../zones/Empyreal_Paradox/mobs/Promathia.lua | 127 ++++++-- .../Empyreal_Paradox/mobs/Promathia_2.lua | 125 ++++--- .../zones/Empyreal_Paradox/mobs/Selhteus.lua | 308 +++++++++++++++++- sql/mob_pool_mods.sql | 12 - sql/mob_pools.sql | 8 +- sql/mob_skills.sql | 28 +- 37 files changed, 888 insertions(+), 438 deletions(-) diff --git a/scripts/actions/mobskills/auroral_uppercut.lua b/scripts/actions/mobskills/auroral_uppercut.lua index c671d8eb83c..8e0cb9c3891 100644 --- a/scripts/actions/mobskills/auroral_uppercut.lua +++ b/scripts/actions/mobskills/auroral_uppercut.lua @@ -1,28 +1,15 @@ ----------------------------------- -- Auroral Uppercut ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if - target:hasStatusEffect(xi.effect.PHYSICAL_SHIELD) or - target:hasStatusEffect(xi.effect.MAGIC_SHIELD) - then - return 1 - end - - mob:showText(mob, ID.text.PRISHE_TEXT + 4) - return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - local damage = mob:getWeaponDmg() - - damage = xi.mobskills.mobMagicalMove(mob, target, skill, damage, xi.element.LIGHT, 1, xi.mobskills.magicalTpBonus.NO_EFFECT) + local damage = xi.mobskills.mobMagicalMove(mob, target, skill, mob:getMainLvl() + 2, xi.element.LIGHT, 2, xi.mobskills.magicalTpBonus.NO_EFFECT) damage = xi.mobskills.mobFinalAdjustments(damage, mob, skill, target, xi.attackType.MAGICAL, xi.damageType.LIGHT, xi.mobskills.shadowBehavior.IGNORE_SHADOWS) target:takeDamage(damage, mob, xi.attackType.MAGICAL, xi.damageType.LIGHT) diff --git a/scripts/actions/mobskills/bastion_of_twilight.lua b/scripts/actions/mobskills/bastion_of_twilight.lua index 51803579051..fbd63dc9405 100644 --- a/scripts/actions/mobskills/bastion_of_twilight.lua +++ b/scripts/actions/mobskills/bastion_of_twilight.lua @@ -2,8 +2,6 @@ -- Bastion of Twilight -- Magic Shield Effect ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} @@ -15,15 +13,15 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) return 1 end - mob:showText(mob, ID.text.PROMATHIA_TEXT + 5) return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) mob:addStatusEffect(xi.effect.MAGIC_SHIELD, 1, 0, 0) - mob:setAnimationSub(2) + skill:setFinalAnimationSub(2) skill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT) + return xi.effect.MAGIC_SHIELD end diff --git a/scripts/actions/mobskills/chains_of_apathy.lua b/scripts/actions/mobskills/chains_of_apathy.lua index 0ced4a8530c..44965786e7b 100644 --- a/scripts/actions/mobskills/chains_of_apathy.lua +++ b/scripts/actions/mobskills/chains_of_apathy.lua @@ -1,27 +1,11 @@ ----------------------------------- -- Chains of Apathy ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - local targets = mob:getEnmityList() - for i, v in pairs(targets) do - if v.entity:isPC() then - local race = v.entity:getRace() - if - (race == xi.race.HUME_M or race == xi.race.HUME_F) and - not v.entity:hasKeyItem(xi.ki.LIGHT_OF_VAHZL) - then - mob:showText(mob, ID.text.PROMATHIA_TEXT) - return 0 - end - end - end - - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/chains_of_arrogance.lua b/scripts/actions/mobskills/chains_of_arrogance.lua index b577bb6953b..c10dc62c5dc 100644 --- a/scripts/actions/mobskills/chains_of_arrogance.lua +++ b/scripts/actions/mobskills/chains_of_arrogance.lua @@ -1,27 +1,11 @@ ----------------------------------- -- Chains of Arrogance ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - local targets = mob:getEnmityList() - for i, v in pairs(targets) do - if v.entity:isPC() then - local race = v.entity:getRace() - if - (race == xi.race.ELVAAN_M or race == xi.race.ELVAAN_F) and - not v.entity:hasKeyItem(xi.ki.LIGHT_OF_MEA) - then - mob:showText(mob, ID.text.PROMATHIA_TEXT + 1) - return 0 - end - end - end - - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/chains_of_cowardice.lua b/scripts/actions/mobskills/chains_of_cowardice.lua index 53029849786..fbd5408202b 100644 --- a/scripts/actions/mobskills/chains_of_cowardice.lua +++ b/scripts/actions/mobskills/chains_of_cowardice.lua @@ -1,27 +1,11 @@ ----------------------------------- -- Chains of Cowardice ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - local targets = mob:getEnmityList() - for i, v in pairs(targets) do - if v.entity:isPC() then - local race = v.entity:getRace() - if - (race == xi.race.TARU_M or race == xi.race.TARU_F) and - not v.entity:hasKeyItem(xi.ki.LIGHT_OF_HOLLA) - then - mob:showText(mob, ID.text.PROMATHIA_TEXT + 2) - return 0 - end - end - end - - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/chains_of_envy.lua b/scripts/actions/mobskills/chains_of_envy.lua index f67a7205e6c..2b1a8324c4e 100644 --- a/scripts/actions/mobskills/chains_of_envy.lua +++ b/scripts/actions/mobskills/chains_of_envy.lua @@ -1,27 +1,11 @@ ----------------------------------- -- Chains of Envy ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - local targets = mob:getEnmityList() - for i, v in pairs(targets) do - if v.entity:isPC() then - local race = v.entity:getRace() - if - race == xi.race.MITHRA and - not v.entity:hasKeyItem(xi.ki.LIGHT_OF_DEM) - then - mob:showText(mob, ID.text.PROMATHIA_TEXT + 3) - return 0 - end - end - end - - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/chains_of_rage.lua b/scripts/actions/mobskills/chains_of_rage.lua index e8a9932a3bb..3385f061436 100644 --- a/scripts/actions/mobskills/chains_of_rage.lua +++ b/scripts/actions/mobskills/chains_of_rage.lua @@ -1,27 +1,11 @@ ----------------------------------- -- Chains of Rage ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - local targets = mob:getEnmityList() - for i, v in pairs(targets) do - if v.entity:isPC() then - local race = v.entity:getRace() - if - race == xi.race.GALKA and - not v.entity:hasKeyItem(xi.ki.LIGHT_OF_ALTAIEU) - then - mob:showText(mob, ID.text.PROMATHIA_TEXT + 4) - return 0 - end - end - end - - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/empty_salvation.lua b/scripts/actions/mobskills/empty_salvation.lua index 27cda9f2c33..21e4af3c602 100644 --- a/scripts/actions/mobskills/empty_salvation.lua +++ b/scripts/actions/mobskills/empty_salvation.lua @@ -10,18 +10,18 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - local damage = mob:getWeaponDmg() * 2 - - damage = xi.mobskills.mobMagicalMove(mob, target, skill, damage, xi.element.DARK, 1, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) + local damage = xi.mobskills.mobMagicalMove(mob, target, skill, mob:getMainLvl() + 2, xi.element.DARK, 2, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) damage = xi.mobskills.mobFinalAdjustments(damage, mob, skill, target, xi.attackType.MAGICAL, xi.damageType.DARK, xi.mobskills.shadowBehavior.NUMSHADOWS_3) - -- Dispel 3 status effects - target:dispelStatusEffect(xi.effectFlag.DISPELABLE) - target:dispelStatusEffect(xi.effectFlag.DISPELABLE) - target:dispelStatusEffect(xi.effectFlag.DISPELABLE) - target:takeDamage(damage, mob, xi.attackType.MAGICAL, xi.damageType.DARK) + -- Dispel 3 status effects + for i = 1, 3 do + if not target:dispelStatusEffect(xi.effectFlag.DISPELABLE) then + break + end + end + return damage end diff --git a/scripts/actions/mobskills/infernal_deliverance.lua b/scripts/actions/mobskills/infernal_deliverance.lua index 1fe4b078969..c870267744f 100644 --- a/scripts/actions/mobskills/infernal_deliverance.lua +++ b/scripts/actions/mobskills/infernal_deliverance.lua @@ -12,13 +12,14 @@ end mobskillObject.onMobWeaponSkill = function(target, mob, skill) local numhits = 1 local accmod = 1 - local ftp = 2.5 + 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.dmg, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) + target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.STUN, 0, 0, 10) - target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) return dmg end diff --git a/scripts/actions/mobskills/luminous_lance.lua b/scripts/actions/mobskills/luminous_lance.lua index 0a1494d6f95..aff764c6f4a 100644 --- a/scripts/actions/mobskills/luminous_lance.lua +++ b/scripts/actions/mobskills/luminous_lance.lua @@ -1,47 +1,28 @@ ----------------------------------- -- Luminous Lance ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - local lanceTime = mob:getLocalVar('lanceTime') - local lanceOut = mob:getLocalVar('lanceOut') - - if - not (target:hasStatusEffect(xi.effect.PHYSICAL_SHIELD) and target:hasStatusEffect(xi.effect.MAGIC_SHIELD)) and - lanceTime + 60 < mob:getBattleTime() and - target:getCurrentAction() ~= xi.action.category.MOBABILITY_USING and - lanceOut == 1 - then - return 0 - end - - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - mob:showText(mob, ID.text.SELHTEUS_TEXT + 1) - local numhits = 1 local accmod = 1 - local dmgmod = 1.6 - + local dmgmod = 3 local info = xi.mobskills.mobRangedMove(mob, target, skill, numhits, accmod, dmgmod, xi.mobskills.magicalTpBonus.NO_EFFECT) - local dmg = xi.mobskills.mobFinalAdjustments(info.dmg, mob, skill, target, xi.attackType.RANGED, xi.damageType.PIERCING, info.hitslanded) - mob:entityAnimationPacket('ids0') - mob:setLocalVar('lanceTime', mob:getBattleTime()) - mob:setLocalVar('lanceOut', 0) - target:setAnimationSub(3) + target:takeDamage(dmg, mob, xi.attackType.RANGED, xi.damageType.PIERCING) - -- Cannot be resisted - target:addStatusEffect(xi.effect.TERROR, 0, 0, 20) + if dmg > 0 then + -- Cannot be resisted + target:setAnimationSub(3) + target:addStatusEffect(xi.effect.TERROR, 0, 0, 30) + end - target:takeDamage(dmg, mob, xi.attackType.RANGED, xi.damageType.PIERCING) return dmg end diff --git a/scripts/actions/mobskills/malevolent_blessing.lua b/scripts/actions/mobskills/malevolent_blessing.lua index 6ad0583213c..3268c1e06fd 100644 --- a/scripts/actions/mobskills/malevolent_blessing.lua +++ b/scripts/actions/mobskills/malevolent_blessing.lua @@ -9,12 +9,11 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - local damage = mob:getWeaponDmg() * 3 - - damage = xi.mobskills.mobMagicalMove(mob, target, skill, damage, xi.element.DARK, 1.25, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) + local damage = xi.mobskills.mobMagicalMove(mob, target, skill, mob:getMainLvl() + 2, xi.element.DARK, 3, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) damage = xi.mobskills.mobFinalAdjustments(damage, mob, skill, target, xi.attackType.MAGICAL, xi.damageType.DARK, xi.mobskills.shadowBehavior.NUMSHADOWS_2) target:takeDamage(damage, mob, xi.attackType.MAGICAL, xi.damageType.DARK) + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.CURSE_I, 35, 0, 45) return damage diff --git a/scripts/actions/mobskills/nullifying_dropkick.lua b/scripts/actions/mobskills/nullifying_dropkick.lua index 5f487635df2..9e163334151 100644 --- a/scripts/actions/mobskills/nullifying_dropkick.lua +++ b/scripts/actions/mobskills/nullifying_dropkick.lua @@ -1,35 +1,21 @@ ----------------------------------- -- Nullifying Dropkick ------------------------------------ -local ID = zones[xi.zone.EMPYREAL_PARADOX] +-- Description: Removes Physical and Magical Shields from Promathia. ----------------------------------- ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if - target:hasStatusEffect(xi.effect.PHYSICAL_SHIELD) or - target:hasStatusEffect(xi.effect.MAGIC_SHIELD) - then - mob:showText(mob, ID.text.PRISHE_TEXT + 5) - return 0 - end - - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - local numhits = 1 - local accmod = 1 - local ftp = 2.0 -- fTP and fTP scaling unknown. TODO: capture ftp - local info = xi.mobskills.mobPhysicalMove(mob, target, skill, numhits, accmod, ftp, xi.mobskills.physicalTpBonus.NO_EFFECT, 0, 0, 0) - local dmg = xi.mobskills.mobFinalAdjustments(info.dmg, mob, skill, target, xi.attackType.PHYSICAL, xi.damageType.BLUNT, info.hitslanded) - target:delStatusEffect(xi.effect.PHYSICAL_SHIELD) target:delStatusEffect(xi.effect.MAGIC_SHIELD) - target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.BLUNT) - return dmg + skill:setMsg(xi.msg.basic.NONE) + + return 0 end return mobskillObject diff --git a/scripts/actions/mobskills/pestilent_penance.lua b/scripts/actions/mobskills/pestilent_penance.lua index 17e049d7347..3939c663eed 100644 --- a/scripts/actions/mobskills/pestilent_penance.lua +++ b/scripts/actions/mobskills/pestilent_penance.lua @@ -9,12 +9,11 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - local damage = mob:getWeaponDmg() * 3 - - damage = xi.mobskills.mobMagicalMove(mob, target, skill, damage, xi.element.DARK, 1, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) + local damage = xi.mobskills.mobMagicalMove(mob, target, skill, mob:getMainLvl() + 2, xi.element.DARK, 3, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) damage = xi.mobskills.mobFinalAdjustments(damage, mob, skill, target, xi.attackType.MAGICAL, xi.damageType.DARK, xi.mobskills.shadowBehavior.NUMSHADOWS_2) target:takeDamage(damage, mob, xi.attackType.MAGICAL, xi.damageType.DARK) + xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.PLAGUE, 10, 0, 120) return damage diff --git a/scripts/actions/mobskills/prishe_item_1.lua b/scripts/actions/mobskills/prishe_item_1.lua index 7f04e705e59..f7170f9aa64 100644 --- a/scripts/actions/mobskills/prishe_item_1.lua +++ b/scripts/actions/mobskills/prishe_item_1.lua @@ -1,8 +1,6 @@ ----------------------------------- -- Prishe Item 1 ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} @@ -12,15 +10,6 @@ end mobskillObject.onMobWeaponSkill = function(target, mob, skill) skill:setMsg(xi.msg.basic.NONE) - if mob:getTarget() and mob:getTarget():getFamily() == 478 then - -- using Ambrosia! - target:addStatusEffect(xi.effect.FOOD, 0, 0, 14400, 0, 0, 0, xi.effectSourceType.FOOD, xi.item.BOWL_OF_AMBROSIA, mob:getID()) - mob:messageText(mob, ID.text.PRISHE_TEXT + 8, false) - else - -- using Daedalus Wing! - mob:addTP(1000) - mob:messageText(mob, ID.text.PRISHE_TEXT + 9, false) - end return 0 end diff --git a/scripts/actions/mobskills/prishe_item_2.lua b/scripts/actions/mobskills/prishe_item_2.lua index 0c6adaf5c75..a1c47e7ad11 100644 --- a/scripts/actions/mobskills/prishe_item_2.lua +++ b/scripts/actions/mobskills/prishe_item_2.lua @@ -1,51 +1,15 @@ ----------------------------------- -- Prishe Item 2 ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if - target:hasStatusEffect(xi.effect.PHYSICAL_SHIELD) or - target:hasStatusEffect(xi.effect.MAGIC_SHIELD) - then - return 1 - elseif - mob:hasStatusEffect(xi.effect.PLAGUE) or - mob:hasStatusEffect(xi.effect.CURSE_I) or - mob:hasStatusEffect(xi.effect.MUTE) - then - return 0 - elseif math.random(1, 100) <= 25 then - return 1 - end - return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) skill:setMsg(xi.msg.basic.NONE) - if - mob:hasStatusEffect(xi.effect.PLAGUE) or - mob:hasStatusEffect(xi.effect.CURSE_I) or - mob:hasStatusEffect(xi.effect.MUTE) - then - -- use Remedy! - mob:messageText(mob, ID.text.PRISHE_TEXT + 12, false) - mob:delStatusEffect(xi.effect.PLAGUE) - mob:delStatusEffect(xi.effect.CURSE_I) - mob:delStatusEffect(xi.effect.MUTE) - elseif math.random(1, 100) <= 50 then - -- Carnal Incense! - mob:messageText(mob, ID.text.PRISHE_TEXT + 10, false) - mob:addStatusEffect(xi.effect.PHYSICAL_SHIELD, 1, 0, 30) - else - -- Spiritual Incense! - mob:messageText(mob, ID.text.PRISHE_TEXT + 11, false) - mob:addStatusEffect(xi.effect.MAGIC_SHIELD, 1, 0, 30) - end return 0 end diff --git a/scripts/actions/mobskills/rejuvenation.lua b/scripts/actions/mobskills/rejuvenation.lua index a8395a7e1dc..52121cbca6d 100644 --- a/scripts/actions/mobskills/rejuvenation.lua +++ b/scripts/actions/mobskills/rejuvenation.lua @@ -5,7 +5,7 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - return 1 + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) diff --git a/scripts/actions/mobskills/revelation.lua b/scripts/actions/mobskills/revelation.lua index 9acc37b266c..bd50a1845fd 100644 --- a/scripts/actions/mobskills/revelation.lua +++ b/scripts/actions/mobskills/revelation.lua @@ -5,17 +5,11 @@ local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - if target:getFamily() == 478 and mob:getLocalVar('lanceOut') == 0 then - return 0 - else - return 1 - end + return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - local damage = mob:getWeaponDmg() - - damage = xi.mobskills.mobMagicalMove(mob, target, skill, damage, xi.element.LIGHT, 1.5, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) + local damage = xi.mobskills.mobMagicalMove(mob, target, skill, mob:getMainLvl() + 2, xi.element.LIGHT, 2, xi.mobskills.magicalTpBonus.MAB_BONUS, 1) damage = xi.mobskills.mobFinalAdjustments(damage, mob, skill, target, xi.attackType.MAGICAL, xi.damageType.LIGHT, xi.mobskills.shadowBehavior.IGNORE_SHADOWS) target:takeDamage(damage, mob, xi.attackType.MAGICAL, xi.damageType.LIGHT) diff --git a/scripts/actions/mobskills/seal_of_quiescence.lua b/scripts/actions/mobskills/seal_of_quiescence.lua index 9b17c82ab8f..5b67a7f1586 100644 --- a/scripts/actions/mobskills/seal_of_quiescence.lua +++ b/scripts/actions/mobskills/seal_of_quiescence.lua @@ -1,18 +1,15 @@ ----------------------------------- -- Seal of Quiescence ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - mob:showText(mob, ID.text.PROMATHIA_TEXT + 6) return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) - skill:setMsg(xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.MUTE, 30, 0, 75)) + skill:setMsg(xi.mobskills.mobStatusEffectMove(mob, target, xi.effect.SILENCE, 30, 0, 75)) return xi.effect.MUTE end diff --git a/scripts/actions/mobskills/wheel_of_impregnability.lua b/scripts/actions/mobskills/wheel_of_impregnability.lua index 12704e421f3..f3d19037a58 100644 --- a/scripts/actions/mobskills/wheel_of_impregnability.lua +++ b/scripts/actions/mobskills/wheel_of_impregnability.lua @@ -1,8 +1,6 @@ ----------------------------------- -- Wheel of Impregnability ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} @@ -14,15 +12,15 @@ mobskillObject.onMobSkillCheck = function(target, mob, skill) return 1 end - mob:showText(mob, ID.text.PROMATHIA_TEXT + 5) return 0 end mobskillObject.onMobWeaponSkill = function(target, mob, skill) mob:addStatusEffect(xi.effect.PHYSICAL_SHIELD, 1, 0, 0) - mob:setAnimationSub(1) + skill:setFinalAnimationSub(1) skill:setMsg(xi.msg.basic.SKILL_GAIN_EFFECT) + return xi.effect.PHYSICAL_SHIELD end diff --git a/scripts/actions/mobskills/winds_of_oblivion.lua b/scripts/actions/mobskills/winds_of_oblivion.lua index 9d0176a3236..587e1e039ff 100644 --- a/scripts/actions/mobskills/winds_of_oblivion.lua +++ b/scripts/actions/mobskills/winds_of_oblivion.lua @@ -1,13 +1,10 @@ ----------------------------------- -- Winds of Oblivion ----------------------------------- -local ID = zones[xi.zone.EMPYREAL_PARADOX] ------------------------------------ ---@type TMobSkill local mobskillObject = {} mobskillObject.onMobSkillCheck = function(target, mob, skill) - mob:showText(mob, ID.text.PROMATHIA_TEXT + 6) return 0 end diff --git a/scripts/actions/spells/black/meteor.lua b/scripts/actions/spells/black/meteor.lua index 42bd7ed0c9e..a163d4bde9d 100644 --- a/scripts/actions/spells/black/meteor.lua +++ b/scripts/actions/spells/black/meteor.lua @@ -32,6 +32,8 @@ spellObject.onSpellCast = function(caster, target, spell) -- TODO: Account for all mitigation sources. -- TODO: Account for rage. damage = caster:getMainLvl() * 15.5 + elseif caster:getFamily() == 134 then -- Promathia family + damage = caster:getMainLvl() * 7 else damage = ((100 + caster:getMod(xi.mod.MATT)) / (100 + target:getMod(xi.mod.MDEF))) * (caster:getStat(xi.mod.INT) + (caster:getMaxSkillLevel(caster:getMainLvl(), xi.job.BLM, xi.skill.ELEMENTAL_MAGIC)) / 6) * 9.4 end diff --git a/scripts/actions/spells/white/arise.lua b/scripts/actions/spells/white/arise.lua index ee36f9a8d2b..09780175179 100644 --- a/scripts/actions/spells/white/arise.lua +++ b/scripts/actions/spells/white/arise.lua @@ -19,12 +19,11 @@ spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(4) else - if target:getName() == 'Prishe' then - -- CoP 8-4 Prishe - target:setLocalVar('Raise', 1) - target:entityAnimationPacket(xi.animationString.SPECIAL_00) + -- CoP 8-4 Prishe + if target:getPool() == xi.mobPools.PRISHE then target:addHP(target:getMaxHP()) target:addMP(target:getMaxMP()) + target:setLocalVar('raise', 1) end end diff --git a/scripts/actions/spells/white/raise.lua b/scripts/actions/spells/white/raise.lua index 4683246a3f9..983e4b71243 100644 --- a/scripts/actions/spells/white/raise.lua +++ b/scripts/actions/spells/white/raise.lua @@ -30,10 +30,12 @@ spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(1) else - -- NPC ally "raise" behavior (instant revive style) - target:addHP(target:getMaxHP()) - target:addMP(target:getMaxMP()) - target:entityAnimationPacket(xi.animationString.SPECIAL_00) + -- CoP 8-4 Prishe + if target:getPool() == xi.mobPools.PRISHE then + target:addHP(target:getMaxHP()) + target:addMP(target:getMaxMP()) + target:setLocalVar('raise', 1) + end end spell:setMsg(xi.msg.basic.MAGIC_CASTS_ON) diff --git a/scripts/actions/spells/white/raise_ii.lua b/scripts/actions/spells/white/raise_ii.lua index 81bb4ed5208..c8793334d6a 100644 --- a/scripts/actions/spells/white/raise_ii.lua +++ b/scripts/actions/spells/white/raise_ii.lua @@ -30,10 +30,12 @@ spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(2) else - -- NPC ally "raise" behavior (instant revive style) - target:addHP(target:getMaxHP()) - target:addMP(target:getMaxMP()) - target:entityAnimationPacket(xi.animationString.SPECIAL_00) + -- CoP 8-4 Prishe + if target:getPool() == xi.mobPools.PRISHE then + target:addHP(target:getMaxHP()) + target:addMP(target:getMaxMP()) + target:setLocalVar('raise', 1) + end end spell:setMsg(xi.msg.basic.MAGIC_CASTS_ON) diff --git a/scripts/actions/spells/white/raise_iii.lua b/scripts/actions/spells/white/raise_iii.lua index 0b88dafe882..b0176ee1e70 100644 --- a/scripts/actions/spells/white/raise_iii.lua +++ b/scripts/actions/spells/white/raise_iii.lua @@ -30,10 +30,12 @@ spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(3) else - -- NPC ally "raise" behavior (instant revive style) - target:addHP(target:getMaxHP()) - target:addMP(target:getMaxMP()) - target:entityAnimationPacket(xi.animationString.SPECIAL_00) + -- CoP 8-4 Prishe + if target:getPool() == xi.mobPools.PRISHE then + target:addHP(target:getMaxHP()) + target:addMP(target:getMaxMP()) + target:setLocalVar('raise', 1) + end end spell:setMsg(xi.msg.basic.MAGIC_CASTS_ON) diff --git a/scripts/battlefields/Empyreal_Paradox/dawn.lua b/scripts/battlefields/Empyreal_Paradox/dawn.lua index 1ab2ec10c76..959132009c4 100644 --- a/scripts/battlefields/Empyreal_Paradox/dawn.lua +++ b/scripts/battlefields/Empyreal_Paradox/dawn.lua @@ -24,27 +24,49 @@ local content = BattlefieldMission:new({ mission = xi.mission.id.cop.DAWN, requiredVar = 'Mission[6][840]Status', requiredValue = 1, + hasWipeGrace = false, grantXP = 2000, title = xi.title.AVERTER_OF_THE_APOCALYPSE, }) -function content:setupBattlefield(battlefield) - battlefield:setLocalVar('instantKick', 1) - local baseID = empyrealParadoxID.mob.PROMATHIA + (battlefield:getArea() - 1) * 2 - local pos = GetMobByID(baseID):getSpawnPos() +local playerCoords = +{ + [1] = { x = -520.000, y = -120.000, z = 493.778, r = 190 }, + [2] = { x = 520.000, y = 0.000, z = 493.778, r = 190 }, + [3] = { x = -520.000, y = 120.000, z = -545.538, r = 190 }, +} + +local prisheCoords = +{ + [1] = { x = -526.678, y = -120.000, z = 509.175, r = 192 }, + [2] = { x = 513.225, y = 0.000, z = 509.710, r = 192 }, + [3] = { x = -527.256, y = 120.000, z = -533.638, r = 192 }, +} - -- TODO: Get table of spawn positions for Allies and set exactly. Rot value - -- is not accurate. lookAt is used to workaround at this time. +local selhteusCoords = +{ + [1] = { x = -513.769, y = -120.000, z = 509.175, r = 170 }, + [2] = { x = 531.881, y = 0.000, z = 510.375, r = 170 }, + [3] = { x = -506.980, y = 120.000, z = -533.638, r = 170 }, +} - local prishe = battlefield:insertEntity(11, true, true) - prishe:setSpawn(pos.x - 6, pos.y, pos.z - 21.5, 192) - prishe:lookAt(pos) +function content:setupBattlefield(battlefield) + local battlefieldArea = battlefield:getArea() + local initiatorId, _ = battlefield:getInitiator() + local initiator = GetPlayerByID(initiatorId) + if initiator then + battlefield:setLocalVar('initRace', initiator:getRace()) + end + + local prishe = battlefield:insertEntity(11, true, true) + local prishePos = prisheCoords[battlefieldArea] + prishe:setSpawn(prishePos.x, prishePos.y, prishePos.z, prishePos.r) prishe:spawn() - local selhteus = battlefield:insertEntity(12, true, true) - selhteus:setSpawn(pos.x + 10, pos.y, pos.z - 17.5, 172) - prishe:lookAt(pos) + local selhteus = battlefield:insertEntity(12, true, true) + local selhteusPos = selhteusCoords[battlefieldArea] + selhteus:setSpawn(selhteusPos.x, selhteusPos.y, selhteusPos.z, selhteusPos.r) selhteus:spawn() end @@ -52,23 +74,17 @@ function content:onEventFinishBattlefield(player, csid, option, npc) local battlefield = player:getBattlefield() local battlefieldArea = battlefield:getArea() local phaseTwoMobId = empyrealParadoxID.mob.PROMATHIA + (battlefieldArea - 1) * 2 + 1 + local playerPos = playerCoords[battlefieldArea] + + player:setPos(playerPos.x, playerPos.y, playerPos.z, playerPos.r) -- Bail out if anyone else got here first if GetMobByID(phaseTwoMobId):isSpawned() then return end - -- Set up the Arena for Phase 2 + -- Spawn Promathia phase 2 SpawnMob(phaseTwoMobId) - - -- Reset allies - local bcnmAllies = battlefield:getAllies() - for _, ally in pairs(bcnmAllies) do - ally:resetLocalVars() - local spawn = ally:getSpawnPos() - - ally:setPos(spawn.x, spawn.y, spawn.z, spawn.rot) - end end function content:onEventFinishWin(player, csid, option, npc) @@ -86,10 +102,32 @@ content.groups = }, allDeath = function(battlefield, mob) - local players = battlefield:getPlayers() + local battlefieldArea = battlefield:getArea() + + -- Reposition Prishe and Selhteus + local bcnmAllies = battlefield:getAllies() + for _, ally in pairs(bcnmAllies) do + if ally:isMob() then + ally:resetLocalVars() + + if ally:getPool() == xi.mobPools.PRISHE then + local prishePos = prisheCoords[battlefieldArea] + ally:setPos(prishePos.x, prishePos.y, prishePos.z, prishePos.r) + + -- Reset Prishe's engage wait time + ally:setLocalVar('[helperNpc]engageWaitTime', 180) + ally:setLocalVar('engageWait', GetSystemTime() + 180) + + elseif ally:getPool() == xi.mobPools.SELHTEUS then + local selhteusPos = selhteusCoords[battlefieldArea] + ally:setPos(selhteusPos.x, selhteusPos.y, selhteusPos.z, selhteusPos.r) + end + end + end + local players = battlefield:getPlayers() for _, player in pairs(players) do - player:startEvent(32004, battlefield:getArea()) + player:startEvent(32004, battlefieldArea) end end, }, diff --git a/scripts/enum/behavior.lua b/scripts/enum/behavior.lua index c23e60f0013..94b6b1cab7f 100644 --- a/scripts/enum/behavior.lua +++ b/scripts/enum/behavior.lua @@ -10,6 +10,7 @@ xi.behavior = NO_DESPAWN = 0x001, -- mob does not despawn on death STANDBACK = 0x002, -- mob will standback forever RAISABLE = 0x004, -- mob can be raised via Raise spells + NOHELP = 0x008, -- mob cannot be targeted by helpful magic from players (cure, protect, etc) AGGRO_AMBUSH = 0x200, -- mob aggroes by ambush NO_TURN = 0x400, -- mob does not turn to face target } diff --git a/scripts/enum/mob_pool.lua b/scripts/enum/mob_pool.lua index e205b241b81..9bd87187bc2 100644 --- a/scripts/enum/mob_pool.lua +++ b/scripts/enum/mob_pool.lua @@ -29,6 +29,7 @@ xi.mobPool = PEPPER = 3116, -- BCNM20 Charming Trio, Absorbing Kiss PHOEDME = 3132, -- BCNM20 Charming Trio, Deep Kiss PLATOON_SCORPION = 3157, -- KS30 Platoon Scorpion Wild Rage behavior + PRISHE = 3199, -- Prishe (CoP 8-4 Dawn) PROCREATOR = 3202, -- Fission (Number of Adds) PROGENERATOR = 3204, -- Fission (Number of Adds) PROPAGATOR = 3206, -- Fission (Number of Adds) @@ -42,6 +43,7 @@ xi.mobPool = WREAKER = 4382, -- Wreaker (CoP 1-3 Spire Battle) QNAERN_WHM = 4651, -- Qn'Aern WHM benediction check AMNAF_PSYCHEFLAYER = 5310, -- Reset enmity on sleepga + SELHTEUS = 5417, -- Selhteus (CoP 8-4 Dawn) FAHRAFAHR_THE_BLOODIED = 6750, -- Reset Enmity on Drop Hammer HPEMDE_NO_DIVING = 7033, -- Hpemde that don't dive, such as those in the north end of Al'Taieu EOZDEI_LEFT = 7095, -- Eo'zdei left rotation diff --git a/scripts/enum/mob_skill.lua b/scripts/enum/mob_skill.lua index 515afb63307..510e22c76fe 100644 --- a/scripts/enum/mob_skill.lua +++ b/scripts/enum/mob_skill.lua @@ -538,7 +538,32 @@ xi.mobSkill = REACTOR_OVERHEAT = 1468, REACTOR_OVERLOAD = 1469, - DEADALUS_WING_COP_PRISHE = 1487, -- Dwing COP 8-4 Dawn + HUNDRED_FISTS_PRISHE = 1485, + BENEDICTION_PRISHE = 1486, + ITEM_1_PRISHE = 1487, + ITEM_2_PRISHE = 1488, + NULLIFYING_DROPKICK_1 = 1489, + AURORAL_UPPERCUT_1 = 1490, + CHAINS_OF_APATHY = 1491, + CHAINS_OF_ARROGANCE = 1492, + CHAINS_OF_COWARDICE = 1493, + CHAINS_OF_RAGE = 1494, + CHAINS_OF_ENVY = 1495, + MALEVOLENT_BLESSING_1 = 1496, + PESTILENT_PENANCE_1 = 1497, + EMPTY_SALVATION_1 = 1498, + INFERNAL_DELIVERANCE_1 = 1499, + MALEVOLENT_BLESSING_2 = 1500, + PESTILENT_PENANCE_2 = 1501, + EMPTY_SALVATION_2 = 1502, + INFERNAL_DELIVERANCE_2 = 1503, + WHEEL_OF_IMPREGNABILITY = 1504, + BASTION_OF_TWILIGHT = 1505, + WINDS_OF_OBLIVION = 1506, + SEAL_OF_QUIESCENCE = 1507, + LUMINOUS_LANCE_1 = 1508, + REJUVENATION_1 = 1509, + REVELATION_1 = 1510, HOWLING_MOON_3 = 1520, -- Unknown usage. diff --git a/scripts/globals/spells/damage_spell.lua b/scripts/globals/spells/damage_spell.lua index da0ec37414f..c1c68c7fc69 100644 --- a/scripts/globals/spells/damage_spell.lua +++ b/scripts/globals/spells/damage_spell.lua @@ -88,7 +88,7 @@ local pTable = [xi.magic.spell.FLOOD ] = { xi.mod.INT, 0, 552, 2, 700, 657, 2, 2, 2, 2, 2, 2, 2 }, [xi.magic.spell.FLOOD_II ] = { xi.mod.INT, 10, 710, 2, 800, 780, 2, 2, 2, 2, 2, 2, 2 }, [xi.magic.spell.IMPACT ] = { xi.mod.INT, 0, 932, 2.3, 932, 525, 0, 0, 0, 0, 0, 0, 0 }, -- I value unknown. Guesstimate used. - [xi.magic.spell.COMET ] = { xi.mod.INT, 0, 964, 2.3, 1000, 850, 4, 3.75, 3.5, 3, 2, 1, 1 }, -- I value unknown. Guesstimate used. + [xi.magic.spell.COMET ] = { xi.mod.INT, 0, 552, 2, 700, 700, 2, 2, 2, 2, 2, 2, 2 }, -- I value unknown. Guesstimate used. [xi.magic.spell.DEATH ] = { 0, 0, 32, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0 }, -- Dia as nuke. diff --git a/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua b/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua index 32ce9725730..2aa30b0511e 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua @@ -4,81 +4,274 @@ -- Chains of Promathia 8-4 BCNM Fight ----------------------------------- local ID = zones[xi.zone.EMPYREAL_PARADOX] +mixins = +{ + require('scripts/mixins/helper_npc'), + require('scripts/mixins/job_special'), +} ----------------------------------- ---@type TMobEntity local entity = {} -entity.onMobInitialize = function(mob) - mob:addMod(xi.mod.REGAIN, 30) +-- Helper NPC configuration +local helperConfig = +{ + engageWaitTime = 180, -- 3 minutes + isAggroable = true, + targetMobs = function(mob) + local battlefieldArea = mob:getBattlefield():getArea() + local areaOffset = (battlefieldArea - 1) * 2 + return + { + ID.mob.PROMATHIA + areaOffset, -- Promathia + ID.mob.PROMATHIA + areaOffset + 1, -- Promathia v2 + } + end, +} + +-- Prishe Item Usage +-- Uses item on engage and on a 2 1/2 minute timer +local itemActions = +{ + -- 1. Daedulus Wing: Usable at any time + { + condition = function(mob, phase) + return true + end, + + action = function(mob) + mob:messageText(mob, ID.text.PRISHE_TEXT + 9, false) + mob:useMobAbility(xi.mobSkill.ITEM_1_PRISHE) + mob:setLocalVar('daedulus', 1) + end, + }, + + -- 2. Bowl of Ambrosia: Usable in Phase 1 + { + condition = function(mob, phase) + return phase == 0 + end, + + action = function(mob) + mob:addStatusEffect(xi.effect.FOOD, 0, 0, 150, 0, 0, 0, xi.effectSourceType.FOOD, 4511, mob:getID()) + mob:messageText(mob, ID.text.PRISHE_TEXT + 8, false) + mob:useMobAbility(xi.mobSkill.ITEM_1_PRISHE) + end, + }, + + -- 3. Carnal Incense (Physical Immunity): Only usable in phase 2 + { + condition = function(mob, phase) + return phase == 1 + end, + + action = function(mob) + mob:addStatusEffect(xi.effect.PHYSICAL_SHIELD, 1, 0, 30) + mob:messageText(mob, ID.text.PRISHE_TEXT + 10, false) + mob:useMobAbility(xi.mobSkill.ITEM_2_PRISHE) + end, + }, + + -- 4. Spiritual Incense (Magical Immunity): Only usable in phase 2 + { + condition = function(mob, phase) + return phase == 1 + end, + + action = function(mob) + mob:addStatusEffect(xi.effect.MAGIC_SHIELD, 1, 0, 30) + mob:messageText(mob, ID.text.PRISHE_TEXT + 11, false) + mob:useMobAbility(xi.mobSkill.ITEM_2_PRISHE) + end, + }, + + -- 5. Remedy: Only usable in phase 2 if she has a status effect to be removed + { + condition = function(mob, phase) + return phase == 1 and + (mob:hasStatusEffect(xi.effect.PLAGUE) or + mob:hasStatusEffect(xi.effect.CURSE_I) or + mob:hasStatusEffect(xi.effect.SILENCE)) + end, + + action = function(mob) + mob:messageText(mob, ID.text.PRISHE_TEXT + 12, false) + mob:delStatusEffect(xi.effect.PLAGUE) + mob:delStatusEffect(xi.effect.CURSE_I) + mob:delStatusEffect(xi.effect.SILENCE) + mob:useMobAbility(xi.mobSkill.ITEM_2_PRISHE) + end, + }, +} + +local useItem = function(mob, phase) + local battlefield = mob:getBattlefield() + if battlefield then + battlefield:setLocalVar('prisheItemTimer', GetSystemTime() + 150) + end + + local validItems = {} + for _, item in ipairs(itemActions) do + if item.condition(mob, phase) then + table.insert(validItems, item) + end + end + + if #validItems > 0 then + local choice = validItems[math.random(1, #validItems)] + choice.action(mob) + end end -entity.onMobRoam = function(mob) - local promathia = ID.mob.PROMATHIA + (mob:getBattlefield():getArea() - 1) * 2 - local wait = mob:getLocalVar('wait') - local ready = mob:getLocalVar('ready') +entity.onMobSpawn = function(mob) + xi.mix.helperNpc.config(mob, helperConfig) + + mob:setMobMod(xi.mobMod.NO_REST, 1) + mob:setMobMod(xi.mobMod.MAGIC_DELAY, 5) + mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 150) - if ready == 0 and wait > 240 then - local promathiaMob = GetMobByID(promathia) + xi.mix.jobSpecial.config(mob, + { + specials = + { + { id = xi.jsa.HUNDRED_FISTS, hpp = 50 }, + { id = xi.jsa.BENEDICTION, hpp = 10 }, + }, + }) + + mob:addListener('WEAPONSKILL_STATE_ENTER', 'PRISHE_SKILL_MSG', function(mobArg, skillID) + local message = + { + [xi.mobSkill.AURORAL_UPPERCUT_1 ] = ID.text.PRISHE_TEXT + 4, + [xi.mobSkill.NULLIFYING_DROPKICK_1] = ID.text.PRISHE_TEXT + 5, + [xi.jsa.HUNDRED_FISTS ] = ID.text.PRISHE_TEXT + 6, + [xi.jsa.BENEDICTION ] = ID.text.PRISHE_TEXT + 7, + } + + if message[skillID] then + mobArg:messageText(mobArg, message[skillID]) + end + end) + + mob:addListener('WEAPONSKILL_STATE_EXIT', 'PRISHE_DAEDALUS', function(mobArg, skillID) + -- Handle Daedulus Wing TP restoration if - promathiaMob and - promathiaMob:getCurrentAction() ~= xi.action.category.NONE + skillID == xi.mobSkill.ITEM_1_PRISHE and + mobArg:getLocalVar('daedulus') == 1 then - mob:entityAnimationPacket('prov') - mob:messageText(mob, ID.text.PRISHE_TEXT) - else - mob:entityAnimationPacket('prov') - mob:messageText(mob, ID.text.PRISHE_TEXT + 1) - promathia = promathia + 1 + mobArg:setTP(3000) + mobArg:setLocalVar('daedulus', 0) end - mob:setLocalVar('ready', promathia) - mob:setLocalVar('wait', 0) - elseif ready > 0 then - local readyMob = GetMobByID(ready) - if readyMob then - mob:addEnmity(readyMob, 0, 1) - mob:updateEnmity(readyMob) + -- Reset animation sub after Nullifying Dropkick completes + if skillID == xi.mobSkill.NULLIFYING_DROPKICK_1 then + local target = mobArg:getTarget() + if target then + target:setAnimationSub(0) + end end - else - mob:setLocalVar('wait', wait + 3) - end + end) end entity.onMobEngage = function(mob, target) - -- Logic to only allow Daedalus Wing to be cast once + mob:setMod(xi.mod.REGAIN, 50) +end + +entity.onMobFight = function(mob, target) local battlefield = mob:getBattlefield() if not battlefield then return end - if battlefield:getLocalVar('usedWing') ~= 1 then - battlefield:setLocalVar('usedWing', 1) - mob:useMobAbility(xi.mobSkill.DEADALUS_WING_COP_PRISHE) + -- Handle post-raise behavior + if mob:getLocalVar('raise') == 1 then + mob:setLocalVar('raise', 0) + mob:entityAnimationPacket(xi.animationString.SPECIAL_00) + mob:stun(2000) + mob:timer(2000, function(prishe) + prishe:messageText(prishe, ID.text.PRISHE_TEXT + 3) + prishe:setLocalVar('deathProcessed', 0) + end) end - mob:addStatusEffectEx(xi.effect.SILENCE, 0, 0, 0, 5) + -- React to Promathia taking first damage + local reactPhase = battlefield:getLocalVar('prisheReact') + if reactPhase > 0 then + local messageOffset = reactPhase - 1 -- Phase 1 uses +0, Phase 2 uses +1 + battlefield:setLocalVar('prisheReact', 0) + mob:setMobMod(xi.mobMod.NO_MOVE, 1) + mob:setAutoAttackEnabled(false) + mob:entityAnimationPacket('prov') + mob:showText(mob, ID.text.PRISHE_TEXT + messageOffset) + + mob:timer(3000, function(prishe) + prishe:setMobMod(xi.mobMod.NO_MOVE, 0) + prishe:setAutoAttackEnabled(true) + prishe:setLocalVar('useItem', 1) + end) + end + + -- Enough time has passed to use next item + local itemTimer = battlefield:getLocalVar('prisheItemTimer') + local phase = battlefield:getLocalVar('phase') + if + (itemTimer > 0 or mob:getLocalVar('useItem') == 1) and + GetSystemTime() >= itemTimer + then + useItem(mob, phase) + end end -entity.onMobFight = function(mob, target) - if mob:getLocalVar('Raise') == 1 then - mob:messageText(mob, ID.text.PRISHE_TEXT + 3) - mob:setLocalVar('Raise', 0) - mob:stun(3000) - elseif mob:getHPP() < 70 and mob:getLocalVar('HF') == 0 then - mob:useMobAbility(xi.jsa.HUNDRED_FISTS_PRISHE) - mob:messageText(mob, ID.text.PRISHE_TEXT + 6) - mob:setLocalVar('HF', 1) - elseif mob:getHPP() < 30 and mob:getLocalVar('Bene') == 0 then - mob:useMobAbility(xi.jsa.BENEDICTION_PRISHE) - mob:messageText(mob, ID.text.PRISHE_TEXT + 7) - mob:setLocalVar('Bene', 1) +entity.onMobMobskillChoose = function(mob, target) + if + target:hasStatusEffect(xi.effect.PHYSICAL_SHIELD) or + target:hasStatusEffect(xi.effect.MAGIC_SHIELD) + then + return xi.mobSkill.NULLIFYING_DROPKICK_1 + else + return xi.mobSkill.AURORAL_UPPERCUT_1 end +end - -- mob:setStatus(0) +entity.onMobSpellChoose = function(mob, target, spellId) + local battlefield = mob:getBattlefield() + if not battlefield then + return + end + + local spellList = + { + [1] = { xi.magic.spell.BANISH_III, target, false, xi.action.type.DAMAGE_TARGET, nil, 100 }, + [2] = { xi.magic.spell.BANISHGA_III, target, false, xi.action.type.DAMAGE_TARGET, nil, 100 }, + [3] = { xi.magic.spell.HOLY, target, false, xi.action.type.DAMAGE_TARGET, nil, 100 }, + [4] = { xi.magic.spell.CURE_IV, mob, true, xi.action.type.HEALING_TARGET, 33, 100 }, + } + + local groupTable = battlefield:getPlayers() + table.insert(groupTable, mob) -- Include self for Cure IV + + return xi.combat.behavior.chooseAction(mob, target, groupTable, spellList) +end + +entity.onMobDisengage = function(mob) + local battlefield = mob:getBattlefield() + if battlefield then + battlefield:setLocalVar('prisheItemTimer', 0) + end +end + +entity.onMobDeath = function(mob, target, optParams) + -- Prevents repeated messages if Prishe stays dead for some time + if mob:getLocalVar('deathProcessed') == 0 then + mob:messageText(mob, ID.text.PRISHE_TEXT + 2) + + mob:setLocalVar('deathProcessed', 1) + end end -entity.onMobDeath = function(mob, player, optParams) - mob:messageText(mob, ID.text.PRISHE_TEXT + 2) +entity.onMobDespawn = function(mob) + mob:removeListener('PRISHE_SKILL_MSG') + mob:removeListener('PRISHE_DAEDALUS') end return entity diff --git a/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua b/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua index 5e94f8fd5bc..d424a2cdb4d 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua @@ -8,50 +8,133 @@ local ID = zones[xi.zone.EMPYREAL_PARADOX] ---@type TMobEntity local entity = {} +local raceToChains = +{ + [xi.race.HUME_M ] = { skill = xi.mobSkill.CHAINS_OF_APATHY, message = ID.text.PROMATHIA_TEXT }, + [xi.race.HUME_F ] = { skill = xi.mobSkill.CHAINS_OF_APATHY, message = ID.text.PROMATHIA_TEXT }, + [xi.race.ELVAAN_M] = { skill = xi.mobSkill.CHAINS_OF_ARROGANCE, message = ID.text.PROMATHIA_TEXT + 1 }, + [xi.race.ELVAAN_F] = { skill = xi.mobSkill.CHAINS_OF_ARROGANCE, message = ID.text.PROMATHIA_TEXT + 1 }, + [xi.race.TARU_M ] = { skill = xi.mobSkill.CHAINS_OF_COWARDICE, message = ID.text.PROMATHIA_TEXT + 2 }, + [xi.race.TARU_F ] = { skill = xi.mobSkill.CHAINS_OF_COWARDICE, message = ID.text.PROMATHIA_TEXT + 2 }, + [xi.race.GALKA ] = { skill = xi.mobSkill.CHAINS_OF_RAGE, message = ID.text.PROMATHIA_TEXT + 3 }, + [xi.race.MITHRA ] = { skill = xi.mobSkill.CHAINS_OF_ENVY, message = ID.text.PROMATHIA_TEXT + 4 }, +} + entity.onMobInitialize = function(mob) + mob:addImmunity(xi.immunity.LIGHT_SLEEP) + mob:addImmunity(xi.immunity.DARK_SLEEP) + mob:addImmunity(xi.immunity.SILENCE) + mob:addImmunity(xi.immunity.PARALYZE) + mob:addImmunity(xi.immunity.BIND) + mob:addImmunity(xi.immunity.BLIND) + mob:addImmunity(xi.immunity.STUN) mob:addMod(xi.mod.REGAIN, 75) mob:addMod(xi.mod.UFASTCAST, 50) + mob:setMod(xi.mod.DEF, 250) + mob:setMod(xi.mod.MDEF, 30) + mob:setMod(xi.mod.DOUBLE_ATTACK, 25) + mob:setMod(xi.mod.DESPAWN_TIME_REDUCTION, 10) + mob:addMobMod(xi.mobMod.SIGHT_RANGE, 15) + mob:addMobMod(xi.mobMod.SOUND_RANGE, 15) mob:setMobMod(xi.mobMod.MAGIC_COOL, 15) end -entity.onMobEngage = function(mob, target) - local bcnmAllies = mob:getBattlefield():getAllies() - for _, v in pairs(bcnmAllies) do - if v:getName() == 'Prishe' then - if not v:getTarget() then - v:entityAnimationPacket('prov') - v:showText(v, ID.text.PRISHE_TEXT) - v:setLocalVar('ready', mob:getID()) - end - else - v:addEnmity(mob, 0, 1) - v:updateEnmity(mob) - end - end +entity.onMobSpawn = function(mob) + mob:setLocalVar('nextBreakpoint', 90) + mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 250) end entity.onMobFight = function(mob, target) - if mob:getAnimationSub() == 3 and not mob:hasStatusEffect(xi.effect.STUN) then + local battlefield = mob:getBattlefield() + if not battlefield then + return + end + + -- Trigger Prishe reaction on first instance of damage done to Promathia + if + mob:getLocalVar('damageTaken') == 0 and + mob:getHPP() < 100 + then + battlefield:setLocalVar('prisheReact', 1) + mob:setLocalVar('damageTaken', 1) + end + + -- Reset animation after lance wears off + if + mob:getAnimationSub() == 3 and + not mob:hasStatusEffect(xi.effect.TERROR) + then mob:setAnimationSub(0) mob:stun(1500) end - local bcnmAllies = mob:getBattlefield():getAllies() - for _, v in pairs(bcnmAllies) do - if not v:getTarget() then - v:addEnmity(mob, 0, 1) - v:updateEnmity(mob) - end + -- HP breakpoints: Force Chains use at every 10% HP + -- Determine which Chains based on initiator race + local nextBreakpoint = mob:getLocalVar('nextBreakpoint') + if mob:getHPP() < nextBreakpoint then + local initRace = battlefield:getLocalVar('initRace') + mob:useMobAbility(raceToChains[initRace].skill) + + mob:setLocalVar('nextBreakpoint', nextBreakpoint - 10) end end entity.onSpellPrecast = function(mob, spell) - if spell:getID() == 219 then + if spell:getID() == xi.magic.spell.COMET then spell:setMPCost(1) end end +entity.onMobMobskillChoose = function(mob, target) + local battlefield = mob:getBattlefield() + if not battlefield then + return + end + + -- High chance to use Chains move + -- Determine which Chains based on initiator race + local initRace = battlefield:getLocalVar('initRace') + if math.random(1, 100) <= 50 then + return raceToChains[initRace].skill + end + + local tpList = + { + xi.mobSkill.MALEVOLENT_BLESSING_1, + xi.mobSkill.PESTILENT_PENANCE_1, + xi.mobSkill.EMPTY_SALVATION_1, + xi.mobSkill.INFERNAL_DELIVERANCE_1, + } + + -- Otherwise choose from TP moves + return tpList[math.random(#tpList)] +end + +entity.onMobWeaponSkill = function(target, mob, skill) + local battlefield = mob:getBattlefield() + if not battlefield then + return + end + + -- Send appropriate message when using Chains move + local skillID = skill:getID() + local initRace = battlefield:getLocalVar('initRace') + if + skillID == raceToChains[initRace].skill and + mob:getTarget() == target -- Prevents multiple messages due to AOE + then + mob:messageText(mob, raceToChains[initRace].message) + end +end + entity.onMobDeath = function(mob, player, optParams) + local battlefield = mob:getBattlefield() + if + battlefield and + (optParams.isKiller or optParams.noKiller) + then + battlefield:setLocalVar('phase', 1) + end end return entity diff --git a/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua b/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua index 70b08c29d67..e864406c821 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua @@ -9,83 +9,114 @@ local ID = zones[xi.zone.EMPYREAL_PARADOX] local entity = {} entity.onMobInitialize = function(mob) - mob:addMod(xi.mod.REGAIN, 75) - mob:addMod(xi.mod.UFASTCAST, 50) + mob:addImmunity(xi.immunity.LIGHT_SLEEP) + mob:addImmunity(xi.immunity.DARK_SLEEP) + mob:addImmunity(xi.immunity.SILENCE) + mob:addImmunity(xi.immunity.PARALYZE) + mob:addImmunity(xi.immunity.BIND) + mob:addImmunity(xi.immunity.BLIND) + mob:addImmunity(xi.immunity.STUN) + mob:setMod(xi.mod.REGAIN, 75) + mob:setMod(xi.mod.UFASTCAST, 50) + mob:setMod(xi.mod.DEF, 250) + mob:setMod(xi.mod.MDEF, 30) + mob:setMod(xi.mod.DOUBLE_ATTACK, 25) + mob:addMobMod(xi.mobMod.SIGHT_RANGE, 15) + mob:addMobMod(xi.mobMod.SOUND_RANGE, 15) mob:setMobMod(xi.mobMod.MAGIC_COOL, 15) end entity.onMobSpawn = function(mob) + mob:setLocalVar('nextBreakpoint', 90) + mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 350) + + mob:addListener('WEAPONSKILL_STATE_ENTER', 'PROMY_SKILL_MSG', function(mobArg, skillID) + if + skillID == xi.mobSkill.WHEEL_OF_IMPREGNABILITY or + skillID == xi.mobSkill.BASTION_OF_TWILIGHT + then + mob:messageText(mob, ID.text.PROMATHIA_TEXT + 5) + elseif + skillID == xi.mobSkill.WINDS_OF_OBLIVION or + skillID == xi.mobSkill.SEAL_OF_QUIESCENCE + then + mob:messageText(mob, ID.text.PROMATHIA_TEXT + 6) + end + end) +end + +entity.onMobFight = function(mob, target) local battlefield = mob:getBattlefield() - if not battlefield then + if xi.combat.behavior.isEntityBusy(mob) or not battlefield then return end - if GetMobByID(ID.mob.PROMATHIA + (battlefield:getArea() - 1) * 2):isDead() then - battlefield:setLocalVar('phaseChange', 0) - end -end - -entity.onMobEngage = function(mob, target) - local bcnmAllies = mob:getBattlefield():getAllies() - for i, v in pairs(bcnmAllies) do - if v:getName() == 'Prishe' then - if not v:getTarget() then - v:entityAnimationPacket('prov') - v:showText(v, ID.text.PRISHE_TEXT + 1) - v:setLocalVar('ready', mob:getID()) - end - else - v:addEnmity(mob, 0, 1) - v:updateEnmity(mob) - end + -- Trigger Prishe reaction on first instance of damage done to Promathia + if mob:getLocalVar('damageTaken') == 0 and mob:getHPP() < 100 then + battlefield:setLocalVar('prisheReact', 2) + mob:setLocalVar('damageTaken', 1) end -end -entity.onMobFight = function(mob, target) - if mob:getAnimationSub() == 3 and not mob:hasStatusEffect(xi.effect.STUN) then - mob:setAnimationSub(0) - mob:stun(1500) - elseif - mob:getAnimationSub() == 2 and - not mob:hasStatusEffect(xi.effect.MAGIC_SHIELD) - then - mob:setAnimationSub(0) - elseif - mob:getAnimationSub() == 1 and - not mob:hasStatusEffect(xi.effect.PHYSICAL_SHIELD) + -- Reset animation after lance wears off + if + mob:getAnimationSub() == 3 and + not mob:hasStatusEffect(xi.effect.TERROR) then mob:setAnimationSub(0) + mob:stun(1500) end - local bcnmAllies = mob:getBattlefield():getAllies() - for i, v in pairs(bcnmAllies) do - if not v:getTarget() then - v:addEnmity(mob, 0, 1) - v:updateEnmity(mob) - end + -- HP breakpoints: Go physical or magic immune every 10% HP + local nextBreakpoint = mob:getLocalVar('nextBreakpoint') + if + mob:getAnimationSub() == 0 and + mob:getHPP() < nextBreakpoint + then + local pickImmune = math.random(1, 100) <= 50 and xi.mobSkill.WHEEL_OF_IMPREGNABILITY or xi.mobSkill.BASTION_OF_TWILIGHT + mob:useMobAbility(pickImmune) + mob:setLocalVar('nextBreakpoint', nextBreakpoint - 10) end end entity.onSpellPrecast = function(mob, spell) - if spell:getID() == 218 then + if spell:getID() == xi.magic.spell.METEOR then spell:setAoE(xi.magic.aoe.RADIAL) - spell:setRadius(30) + spell:setRadius(25) spell:setAnimation(280) spell:setMPCost(1) - elseif spell:getID() == 219 then + elseif spell:getID() == xi.magic.spell.COMET then spell:setMPCost(1) end end -entity.onMagicCastingCheck = function(mob, target, spell) +entity.onMobSpellChoose = function(mob, target, spellId) if math.random(1, 100) <= 25 then - return 219 + return xi.magic.spell.COMET else - return 218 + return xi.magic.spell.METEOR end end -entity.onMobDeath = function(mob, player, optParams) +entity.onMobMobskillChoose = function(mob, target) + local tpList = {} + if + mob:hasStatusEffect(xi.effect.PHYSICAL_SHIELD) or + mob:hasStatusEffect(xi.effect.MAGIC_SHIELD) + then + table.insert(tpList, xi.mobSkill.WINDS_OF_OBLIVION) + table.insert(tpList, xi.mobSkill.SEAL_OF_QUIESCENCE) + else + table.insert(tpList, xi.mobSkill.MALEVOLENT_BLESSING_2) + table.insert(tpList, xi.mobSkill.PESTILENT_PENANCE_2) + table.insert(tpList, xi.mobSkill.EMPTY_SALVATION_2) + table.insert(tpList, xi.mobSkill.INFERNAL_DELIVERANCE_2) + end + + return tpList[math.random(#tpList)] +end + +entity.onMobDespawn = function(mob) + mob:removeListener('PROMY_SKILL_MSG') end return entity diff --git a/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua b/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua index 70e4a477c5c..525607e96e5 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua @@ -4,39 +4,311 @@ -- Chains of Promathia 8-4 BCNM Fight ----------------------------------- local ID = zones[xi.zone.EMPYREAL_PARADOX] +mixins = { require('scripts/mixins/helper_npc') } ----------------------------------- ---@type TMobEntity local entity = {} +-- Circular grid coordinates for each arena for Selhteus to navigate +local pathNodes = +{ + [1] = + { + -- Center + { x = -520.0000, y = -120.0000, z = 521.8342 }, + + -- Inner ring + { x = -520.0000, y = -120.0000, z = 529.8342 }, + { x = -505.6569, y = -120.0000, z = 537.3912 }, + { x = -520.0000, y = -120.0000, z = 545.8342 }, + { x = -534.3431, y = -120.0000, z = 537.3912 }, + { x = -520.0000, y = -120.0000, z = 513.8342 }, + { x = -505.6569, y = -120.0000, z = 506.2772 }, + { x = -534.3431, y = -120.0000, z = 506.2772 }, + + -- Middle ring + { x = -520.0000, y = -120.0000, z = 537.8342 }, + { x = -510.2426, y = -120.0000, z = 540.7912 }, + { x = -500.4852, y = -120.0000, z = 538.0912 }, + { x = -491.9140, y = -120.0000, z = 532.3582 }, + { x = -495.6889, y = -120.0000, z = 520.0472 }, + { x = -491.9140, y = -120.0000, z = 511.3102 }, + { x = -500.4852, y = -120.0000, z = 505.5772 }, + { x = -510.2426, y = -120.0000, z = 502.8772 }, + { x = -520.0000, y = -120.0000, z = 505.8342 }, + { x = -529.7574, y = -120.0000, z = 502.8772 }, + { x = -539.5148, y = -120.0000, z = 505.5772 }, + { x = -548.0860, y = -120.0000, z = 511.3102 }, + { x = -543.8796, y = -120.0000, z = 521.7178 }, + { x = -548.0860, y = -120.0000, z = 532.3582 }, + { x = -539.5148, y = -120.0000, z = 538.0912 }, + { x = -529.7574, y = -120.0000, z = 540.7912 }, + + -- Outer ring + { x = -520.0000, y = -120.0000, z = 545.8342 }, + { x = -508.5410, y = -120.0000, z = 542.6254 }, + { x = -500.0000, y = -120.0000, z = 535.4736 }, + { x = -493.4590, y = -120.0000, z = 525.5430 }, + { x = -495.6889, y = -120.0000, z = 520.0472 }, + { x = -493.4590, y = -120.0000, z = 518.1254 }, + { x = -500.0000, y = -120.0000, z = 508.1948 }, + { x = -508.5410, y = -120.0000, z = 501.0430 }, + { x = -520.0000, y = -120.0000, z = 497.8342 }, + { x = -531.4590, y = -120.0000, z = 501.0430 }, + { x = -540.0000, y = -120.0000, z = 508.1948 }, + { x = -546.5410, y = -120.0000, z = 518.1254 }, + { x = -543.8796, y = -120.0000, z = 521.7178 }, + { x = -546.5410, y = -120.0000, z = 525.5430 }, + { x = -540.0000, y = -120.0000, z = 535.4736 }, + { x = -531.4590, y = -120.0000, z = 542.6254 }, + }, + + [2] = + { + -- Center + { x = 520.1450, y = 0.0000, z = 517.1620 }, + + -- Inner ring + { x = 520.1450, y = 0.0000, z = 525.1620 }, + { x = 505.8019, y = 0.0000, z = 532.7190 }, + { x = 520.1450, y = 0.0000, z = 541.1620 }, + { x = 534.4881, y = 0.0000, z = 532.7190 }, + { x = 520.1450, y = 0.0000, z = 509.1620 }, + { x = 505.8019, y = 0.0000, z = 501.6050 }, + { x = 534.4881, y = 0.0000, z = 501.6050 }, + + -- Middle ring + { x = 519.5360, y = 0.0000, z = 545.3560 }, + { x = 509.7786, y = 0.0000, z = 542.1472 }, + { x = 500.0212, y = 0.0000, z = 537.1100 }, + { x = 491.4500, y = 0.0000, z = 529.7050 }, + { x = 544.9990, y = 0.0000, z = 519.6650 }, + { x = 491.4500, y = 0.0000, z = 511.1650 }, + { x = 500.0212, y = 0.0000, z = 497.1140 }, + { x = 509.7786, y = 0.0000, z = 492.0768 }, + { x = 520.2430, y = 0.0000, z = 493.1810 }, + { x = 530.5074, y = 0.0000, z = 492.1940 }, + { x = 539.2688, y = 0.0000, z = 497.1140 }, + { x = 548.6400, y = 0.0000, z = 509.3390 }, + { x = 494.1020, y = 0.0000, z = 520.4900 }, + { x = 548.6400, y = 0.0000, z = 529.7050 }, + { x = 539.2688, y = 0.0000, z = 537.1100 }, + { x = 530.5074, y = 0.0000, z = 542.1472 }, + + -- Outer ring + { x = 519.5360, y = 0.0000, z = 545.3560 }, + { x = 508.0770, y = 0.0000, z = 542.1472 }, + { x = 499.5290, y = 0.0000, z = 535.0054 }, + { x = 492.9880, y = 0.0000, z = 525.0748 }, + { x = 544.9990, y = 0.0000, z = 519.6650 }, + { x = 492.9880, y = 0.0000, z = 513.2492 }, + { x = 499.5290, y = 0.0000, z = 503.3186 }, + { x = 508.0770, y = 0.0000, z = 496.1768 }, + { x = 520.2430, y = 0.0000, z = 493.1810 }, + { x = 531.9130, y = 0.0000, z = 496.1768 }, + { x = 540.4610, y = 0.0000, z = 503.3186 }, + { x = 547.0020, y = 0.0000, z = 513.2492 }, + { x = 494.1020, y = 0.0000, z = 520.4900 }, + { x = 547.0020, y = 0.0000, z = 525.0748 }, + { x = 540.4610, y = 0.0000, z = 535.0054 }, + { x = 531.9130, y = 0.0000, z = 542.1472 }, + }, + + [3] = + { + -- Center + { x = -519.5570, y = 120.0000, z = -516.6000 }, + + -- Inner ring + { x = -519.5570, y = 120.0000, z = -508.6000 }, + { x = -505.2139, y = 120.0000, z = -501.0430 }, + { x = -519.5570, y = 120.0000, z = -491.1600 }, + { x = -533.9001, y = 120.0000, z = -501.0430 }, + { x = -519.5570, y = 120.0000, z = -524.6000 }, + { x = -505.2139, y = 120.0000, z = -532.1570 }, + { x = -533.9001, y = 120.0000, z = -532.1570 }, + + -- Middle ring + { x = -519.9580, y = 120.0000, z = -495.6430 }, + { x = -510.2006, y = 120.0000, z = -498.6000 }, + { x = -500.4432, y = 120.0000, z = -504.4330 }, + { x = -491.8720, y = 120.0000, z = -514.1660 }, + { x = -493.6080, y = 120.0000, z = -519.8430 }, + { x = -491.8720, y = 120.0000, z = -527.8800 }, + { x = -500.4432, y = 120.0000, z = -537.0130 }, + { x = -510.2006, y = 120.0000, z = -539.5030 }, + { x = -519.9030, y = 120.0000, z = -545.4980 }, + { x = -529.6604, y = 120.0000, z = -539.5030 }, + { x = -539.4178, y = 120.0000, z = -537.0130 }, + { x = -547.9890, y = 120.0000, z = -527.8800 }, + { x = -544.8860, y = 120.0000, z = -520.8420 }, + { x = -547.9890, y = 120.0000, z = -514.1660 }, + { x = -539.4178, y = 120.0000, z = -504.4330 }, + { x = -529.6604, y = 120.0000, z = -498.6000 }, + + -- Outer ring + { x = -519.9580, y = 120.0000, z = -495.6430 }, + { x = -508.4990, y = 120.0000, z = -498.8518 }, + { x = -499.9510, y = 120.0000, z = -505.9936 }, + { x = -493.4100, y = 120.0000, z = -516.0242 }, + { x = -493.6080, y = 120.0000, z = -519.8430 }, + { x = -493.4100, y = 120.0000, z = -527.7618 }, + { x = -499.9510, y = 120.0000, z = -537.7924 }, + { x = -508.4990, y = 120.0000, z = -544.9342 }, + { x = -519.9030, y = 120.0000, z = -545.4980 }, + { x = -531.3070, y = 120.0000, z = -544.9342 }, + { x = -539.8550, y = 120.0000, z = -537.7924 }, + { x = -546.3960, y = 120.0000, z = -527.7618 }, + { x = -544.8860, y = 120.0000, z = -520.8420 }, + { x = -546.3960, y = 120.0000, z = -516.0242 }, + { x = -539.8550, y = 120.0000, z = -505.9936 }, + { x = -531.3070, y = 120.0000, z = -498.8518 }, + }, +} + +-- Helper NPC configuration +local helperConfig = +{ + targetMobs = function(mob) + local battlefieldArea = mob:getBattlefield():getArea() + local areaOffset = (battlefieldArea - 1) * 2 + return + { + ID.mob.PROMATHIA + areaOffset, -- Promathia + ID.mob.PROMATHIA + areaOffset + 1, -- Promathia v2 + } + end, +} + +-- Function to navigate Selhteus to the closest node at least 16' from Promathia +local findClosestNode = function(mob, target, area) + local closestNode = nil + local minDist = 0 + + for _, node in ipairs(pathNodes[area]) do + local distFromProm = utils.distance(target:getPos(), node) + local distFromMob = utils.distance(mob:getPos(), node) + + if distFromProm >= 16 then + if distFromMob < minDist or minDist == 0 then + minDist = distFromMob + closestNode = node + end + end + end + + if closestNode then + mob:pathTo(closestNode.x, closestNode.y, closestNode.z) + end +end + entity.onMobInitialize = function(mob) - mob:addMod(xi.mod.REGAIN, 50) + mob:setBehavior(bit.bor(mob:getBehavior(), xi.behavior.NOHELP)) -- Disallow spells cast on Selhteus mob:addMod(xi.mod.CURE_POTENCY_RCVD, -100) + mob:setMobMod(xi.mobMod.NO_REST, 1) mob:setAutoAttackEnabled(false) end +entity.onMobSpawn = function(mob) + xi.mix.helperNpc.config(mob, helperConfig) + mob:setMobAbilityEnabled(false) + + mob:addListener('WEAPONSKILL_STATE_ENTER', 'SELH_SKILL_MSG', function(mobArg, skillID) + if skillID == xi.mobSkill.LUMINOUS_LANCE_1 then + mobArg:messageText(mobArg, ID.text.SELHTEUS_TEXT + 1) + elseif skillID == xi.mobSkill.REJUVENATION_1 then + mobArg:messageText(mobArg, ID.text.SELHTEUS_TEXT + 2) + end + end) +end + +entity.onMobEngage = function(mob, target) + mob:setMod(xi.mod.REGAIN, 50) + mob:setLocalVar('positionTimer', GetSystemTime() + 30) + mob:setLocalVar('lanceTime', GetSystemTime() + 90) +end + entity.onMobFight = function(mob, target) - if target:getTarget():getID() ~= mob:getID() then - local targetPos = target:getPos() - local radians = (256 - targetPos.rot) * (math.pi / 128) - mob:pathTo(targetPos.x + math.cos(radians) * 16, targetPos.y, targetPos.z + math.sin(radians) * 16) - end - - local lanceTime = mob:getLocalVar('lanceTime') - local lanceOut = mob:getLocalVar('lanceOut') - local rejuv = mob:getLocalVar('rejuv') - if mob:getHPP() < 30 and rejuv == 0 and target:getFamily() == 478 then - mob:messageText(mob, ID.text.SELHTEUS_TEXT + 2) - mob:useMobAbility(1509) + local battlefield = mob:getBattlefield() + if not battlefield then + return + end + + if xi.combat.behavior.isEntityBusy(mob) then + return + end + + -- Ensures he is at least 16' from Promathia + local positionTimer = mob:getLocalVar('positionTimer') + local area = battlefield:getArea() + if + mob:checkDistance(target) < 15 and + positionTimer < GetSystemTime() + then + findClosestNode(mob, target, area) + mob:setLocalVar('positionTimer', GetSystemTime() + 30) + end + + -- Uses Rejuvenation when below 10% HP in 2nd phase + local phase = battlefield:getLocalVar('phase') + local hasRejuved = mob:getLocalVar('rejuv') == 1 + if + mob:getHPP() < 10 and + not hasRejuved and + phase == 1 + then + mob:useMobAbility(xi.mobSkill.REJUVENATION_1) mob:setLocalVar('rejuv', 1) - elseif lanceTime + 50 < mob:getBattleTime() and lanceOut == 0 then - mob:entityAnimationPacket(xi.animationString.SPECIAL_00) - mob:setLocalVar('lanceOut', 1) + end + + -- Lance is ready, animate summoning lance and allow Luminous Lance usage + local lanceReady = mob:getLocalVar('lanceReady') + if lanceReady == 0 then + local lanceTime = mob:getLocalVar('lanceTime') + if lanceTime <= GetSystemTime() then + mob:setLocalVar('lanceReady', 1) + mob:entityAnimationPacket(xi.animationString.SPECIAL_00) + mob:setMobAbilityEnabled(false) + mob:timer(3000, function(selhteus) + selhteus:setLocalVar('lanceReady', 2) + end) + end + end + + -- If lance is ready, and target is not busy, use Luminous Lance and allow Revelation usage + if + lanceReady == 2 and + target:getAnimationSub() == 0 and + not xi.combat.behavior.isEntityBusy(target) + then + mob:useMobAbility(xi.mobSkill.LUMINOUS_LANCE_1) + mob:setLocalVar('lanceTime', GetSystemTime() + 90) + mob:setLocalVar('lanceReady', 0) + mob:timer(3000, function(selhteus) + mob:entityAnimationPacket('ids0') + mob:setMobAbilityEnabled(true) + end) + end + + -- Uses TP if above 1000 + if phase == 2 and mob:getTP() >= 1000 then + mob:useMobAbility() end end +entity.onMobMobskillChoose = function(mob, target) + return xi.mobSkill.REVELATION_1 +end + entity.onMobDeath = function(mob, player, optParams) - mob:messageText(mob, ID.text.SELHTEUS_TEXT) - mob:getBattlefield():lose() + if optParams.isKiller or optParams.noKiller then + mob:messageText(mob, ID.text.SELHTEUS_TEXT) + mob:getBattlefield():lose() + end +end + +entity.onMobDespawn = function(mob) + mob:removeListener('SELH_SKILL_MSG') end return entity diff --git a/sql/mob_pool_mods.sql b/sql/mob_pool_mods.sql index e786e6ec020..93582a1edf7 100644 --- a/sql/mob_pool_mods.sql +++ b/sql/mob_pool_mods.sql @@ -253,12 +253,6 @@ INSERT INTO `mob_pool_mods` VALUES (3099,23,23,1); -- IMMUNITY: 23 -- Polar Hare INSERT INTO `mob_pool_mods` VALUES (3168,28,10,1); -- EXP_BONUS: 10 --- Promathia -INSERT INTO `mob_pool_mods` VALUES (3205,1,250,0); -- DEF: 250 -INSERT INTO `mob_pool_mods` VALUES (3205,29,30,0); -- MDEF: 30 -INSERT INTO `mob_pool_mods` VALUES (3205,288,25,0); -- DOUBLE_ATTACK: 25 -INSERT INTO `mob_pool_mods` VALUES (3205,366,25,0); -- MAIN_DMG_RATING: 25 - -- Proto-Omega INSERT INTO `mob_pool_mods` VALUES (3208,370,20,0); -- REGEN: 20 @@ -513,12 +507,6 @@ INSERT INTO `mob_pool_mods` VALUES (4837,62,1,1); -- NO_STANDBACK: 1 INSERT INTO `mob_pool_mods` VALUES (4932,30,1017,1); -- SPECIAL_SKILL: 1017 INSERT INTO `mob_pool_mods` VALUES (4932,33,50,1); -- SPECIAL_COOL: 50 --- Promathia -INSERT INTO `mob_pool_mods` VALUES (5106,1,250,0); -- DEF: 250 -INSERT INTO `mob_pool_mods` VALUES (5106,29,30,0); -- MDEF: 30 -INSERT INTO `mob_pool_mods` VALUES (5106,288,25,0); -- DOUBLE_ATTACK: 25 -INSERT INTO `mob_pool_mods` VALUES (5106,366,10,0); -- MAIN_DMG_RATING: 10 - -- Maat Nin INSERT INTO `mob_pool_mods` VALUES (5403,62,1,1); -- NO_STANDBACK: 1 diff --git a/sql/mob_pools.sql b/sql/mob_pools.sql index a73f7e5a298..f5c16b84fdd 100644 --- a/sql/mob_pools.sql +++ b/sql/mob_pools.sql @@ -3254,13 +3254,13 @@ INSERT INTO `mob_pools` VALUES (3195,'Princeps_XIII-LXXXIX','Princeps_XIII-LXXXI INSERT INTO `mob_pools` VALUES (3196,'Princess_Jelly','Princess_Jelly',229,0x0000240100000000000000000000000000000000,4,4,7,240,100,0,1,0,1,16,6144,0,429,641,0,0,2,0,0,229,229,0,20); INSERT INTO `mob_pools` VALUES (3197,'Princess_Pudding','Princess_Pudding',112,0x0000070700000000000000000000000000000000,4,4,12,240,100,0,1,0,0,2,0,0,0,0,0,0,2,0,0,112,112,NULL,NULL); INSERT INTO `mob_pools` VALUES (3198,'Prince_Seere','Prince_Seere',358,0x0000F40200000000000000000000000000000000,3,3,3,240,100,0,1,1,1,2,0,32,352,159,0,0,20,0,0,358,358,3,19); -INSERT INTO `mob_pools` VALUES (3199,'Prishe','Prishe',476,0x0000B70400000000000000000000000000000000,2,3,1,480,100,5,0,0,0,16,0,40,0,25,0,0,129,2,0,476,476,NULL,NULL); +INSERT INTO `mob_pools` VALUES (3199,'Prishe','Prishe',476,0x0000B70400000000000000000000000000000000,2,3,1,480,100,5,0,0,0,16,0,40,0,25,0,0,129,2,0,476,476,0,17); INSERT INTO `mob_pools` VALUES (3200,'Processionaire','Processionaire',79,0x00008C0100000000000000000000000000000000,1,1,11,240,100,0,1,0,1,0,0,0,150,133,0,0,0,0,0,79,79,2,17); INSERT INTO `mob_pools` VALUES (3201,'Proconsul_XII','Proconsul_XII',25,0x0000110500000000000000000000000000000000,7,7,3,265,100,0,1,0,1,2,0,0,238,135,0,0,4,0,0,25,25,3,16); INSERT INTO `mob_pools` VALUES (3202,'Procreator','Procreator',138,0x00006D0400000000000000000000000000000000,1,5,3,190,100,0,0,1,0,18,6144,0,6,133,0,0,0,0,0,138,138,2,13); INSERT INTO `mob_pools` VALUES (3203,'Procrustes','Procrustes',126,0x0000120100000000000000000000000000000000,4,4,12,240,100,0,1,1,1,2,0,0,0,3,0,0,2,0,0,126,126,NULL,NULL); INSERT INTO `mob_pools` VALUES (3204,'Progenerator','Progenerator',138,0x00006D0400000000000000000000000000000000,1,5,3,190,100,0,1,1,0,16,6148,0,0,6,0,0,0,0,0,138,138,3,16); -INSERT INTO `mob_pools` VALUES (3205,'Promathia','Promathia',134,0x0000300400000000000000000000000000000000,7,7,3,240,100,0,0,0,0,18,6172,32,3979,155,0,0,127,0,0,134,134,1,20); +INSERT INTO `mob_pools` VALUES (3205,'Promathia','Promathia',134,0x0000300400000000000000000000000000000000,7,7,3,290,100,0,0,0,0,18,6172,32,3979,155,0,0,127,0,0,134,134,1,20); INSERT INTO `mob_pools` VALUES (3206,'Propagator','Propagator',138,0x00006D0400000000000000000000000000000000,1,5,3,240,100,0,1,1,0,2,0,0,2629,135,13,0,0,0,0,138,138,3,16); INSERT INTO `mob_pools` VALUES (3207,'Proteus','Proteus',218,0x0000610100000000000000000000000000000000,2,2,7,360,100,0,1,0,0,2,0,32,236,157,0,0,0,0,0,218,218,2,20); INSERT INTO `mob_pools` VALUES (3208,'Proto-Omega','Proto-Omega',54,0x0000370400000000000000000000000000000000,2,1,6,240,100,1024,1,1,0,18,0,32,2773,157,1,0,0,0,0,727,518,NULL,NULL); @@ -5161,7 +5161,7 @@ INSERT INTO `mob_pools` VALUES (5102,'Maca_Maca','Maca_Maca',178,0x00002D0100000 INSERT INTO `mob_pools` VALUES (5103,'Qohanyk','Qohanyk',186,0x00007E0100000000000000000000000000000000,1,1,7,200,100,0,1,0,0,0,0,0,555,129,4,0,0,0,0,186,186,0,27); INSERT INTO `mob_pools` VALUES (5104,'Sinewy_Matamata','Sinewy_Matamata',348,0x0000000A00000000000000000000000000000000,1,1,6,240,100,0,1,0,0,0,0,0,0,129,8,0,0,0,0,275,275,0,21); INSERT INTO `mob_pools` VALUES (5105,'Hoarfrost_Gefyrst','Hoarfrost_Gefyrst',103,0x0000670800000000000000000000000000000000,4,5,12,240,100,0,1,0,0,0,0,0,764,129,0,0,14,0,0,103,103,0,15); -INSERT INTO `mob_pools` VALUES (5106,'Promathia','Promathia',134,0x0000310400000000000000000000000000000000,7,7,3,240,100,0,0,0,0,18,6172,32,3979,155,0,0,128,0,0,478,134,1,20); +INSERT INTO `mob_pools` VALUES (5106,'Promathia','Promathia',134,0x0000310400000000000000000000000000000000,7,7,3,290,100,0,0,0,0,18,6172,32,3979,155,0,0,128,0,0,478,134,1,20); INSERT INTO `mob_pools` VALUES (5107,'Nightmare_Funguar','Nightmare_Funguar',116,0x0000780100000000000000000000000000000000,1,1,11,240,100,0,1,0,1,0,0,0,0,129,0,0,0,0,0,116,116,0,12); INSERT INTO `mob_pools` VALUES (5108,'Nightmare_Treant','Nightmare_Treant',245,0x0000840100000000000000000000000000000000,1,1,7,240,100,0,1,0,1,0,0,0,4406,129,0,0,0,0,0,245,245,0,32); INSERT INTO `mob_pools` VALUES (5109,'Nightmare_Flytrap','Nightmare_Flytrap',114,0x0000410500000000000000000000000000000000,1,1,8,200,100,0,1,0,1,0,0,0,563,131,8,0,0,0,0,114,114,1,12); @@ -5472,7 +5472,7 @@ INSERT INTO `mob_pools` VALUES (5413,'Maat_brd','Maat',335,0x0000FA0200000000000 INSERT INTO `mob_pools` VALUES (5414,'Spriggan_rng','Spriggan_rng',221,0x0000220200000000000000000000000000000000,11,11,5,360,100,0,1,0,1,0,0,0,7,131,0,0,0,0,0,221,221,1,20); INSERT INTO `mob_pools` VALUES (5415,'Spriggan_blm','Spriggan_blm',221,0x00001A0200000000000000000000000000000000,4,4,5,360,100,0,1,0,1,0,0,0,7,131,0,0,2,0,0,221,221,1,20); INSERT INTO `mob_pools` VALUES (5416,'Spriggan_thf','Spriggan_thf',221,0x00001E0200000000000000000000000000000000,6,6,5,360,100,0,1,0,1,0,0,0,7,131,0,0,0,0,0,221,221,1,20); -INSERT INTO `mob_pools` VALUES (5417,'Selhteus','Selh\'teus',477,0x0000F20400000000000000000000000000000000,1,1,1,240,100,10,0,0,0,16,0,40,0,153,0,0,0,2,0,477,477,NULL,NULL); +INSERT INTO `mob_pools` VALUES (5417,'Selhteus','Selh\'teus',477,0x0000F20400000000000000000000000000000000,1,1,1,240,100,10,0,0,0,16,0,40,0,153,0,0,0,2,0,477,477,0,12); INSERT INTO `mob_pools` VALUES (5418,'Bedrock_Barry','Bedrock_Barry',258,0x0000A80100000000000000000000000000000000,4,5,7,240,100,0,0,0,0,2,0,32,297,153,0,0,130,0,0,396,396,0,11); INSERT INTO `mob_pools` VALUES (5419,'Tococo','Tococo',55,0x0000BC0100000000000000000000000000000000,1,1,2,240,100,0,0,0,1,2,6808,0,721,129,0,0,0,0,0,55,55,0,17); INSERT INTO `mob_pools` VALUES (5420,'QuVho_Deathhurler','QuVho_Deathhurler',202,0x0000860200000000000000000000000000000000,7,1,2,265,100,0,1,0,1,2,0,32,872,155,0,0,0,0,0,397,397,NULL,NULL); diff --git a/sql/mob_skills.sql b/sql/mob_skills.sql index f915c038531..803edc58904 100644 --- a/sql/mob_skills.sql +++ b/sql/mob_skills.sql @@ -1513,27 +1513,27 @@ INSERT INTO `mob_skills` VALUES (1485,1091,'hundred_fists',0,0.0,7.0,2000,0,1,0, INSERT INTO `mob_skills` VALUES (1486,1092,'benediction',1,0.0,7.0,2000,0,1,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1487,1093,'prishe_item_1',0,0.0,7.0,2000,0,1,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1488,1094,'prishe_item_2',0,0.0,7.0,2000,0,1,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1489,1095,'nullifying_dropkick',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1490,1096,'auroral_uppercut',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1489,1095,'nullifying_dropkick',0,0.0,7.0,2000,100,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1490,1096,'auroral_uppercut',0,0.0,7.0,2000,100,4,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1491,1100,'chains_of_apathy',1,0.0,30.0,2000,1500,4,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1492,1101,'chains_of_arrogance',1,0.0,30.0,2000,1500,4,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1493,1102,'chains_of_cowardice',1,0.0,30.0,2000,1500,4,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1494,1103,'chains_of_rage',1,0.0,30.0,2000,1500,4,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1495,1104,'chains_of_envy',1,0.0,30.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1496,1105,'malevolent_blessing',4,0.0,10.0,2000,1500,4,0,0,7,0,0,0); -INSERT INTO `mob_skills` VALUES (1497,1106,'pestilent_penance',4,0.0,10.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1498,1107,'empty_salvation',1,0.0,25.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1499,1108,'infernal_deliverance',1,0.0,15.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1500,1109,'malevolent_blessing',4,0.0,10.0,2000,1500,4,0,0,7,0,0,0); -INSERT INTO `mob_skills` VALUES (1501,1110,'pestilent_penance',4,0.0,10.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1502,1111,'empty_salvation',1,0.0,25.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1503,1112,'infernal_deliverance',1,0.0,15.0,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1496,1105,'malevolent_blessing',0,0.0,16.0,2000,1500,4,0,0,7,0,0,0); +INSERT INTO `mob_skills` VALUES (1497,1106,'pestilent_penance',4,17.0,17.0,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1498,1107,'empty_salvation',1,0.0,23.5,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1499,1108,'infernal_deliverance',1,0.0,25.0,2000,1500,4,0,0,4,0,0,0); +INSERT INTO `mob_skills` VALUES (1500,1109,'malevolent_blessing',0,0.0,16.0,2000,1500,4,0,0,7,0,0,0); +INSERT INTO `mob_skills` VALUES (1501,1110,'pestilent_penance',4,17.0,17.0,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1502,1111,'empty_salvation',1,0.0,23.5,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1503,1112,'infernal_deliverance',1,0.0,25.0,2000,1500,4,0,0,4,0,0,0); INSERT INTO `mob_skills` VALUES (1504,1113,'wheel_of_impregnability',0,0.0,7.0,2000,1500,1,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1505,1114,'bastion_of_twilight',0,0.0,7.0,2000,1500,1,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1506,1115,'winds_of_oblivion',1,0.0,15.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1507,1116,'seal_of_quiescence',1,0.0,15.0,2000,1500,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1508,1099,'luminous_lance',0,0.0,20.0,2000,0,4,0,0,0,0,0,0); -INSERT INTO `mob_skills` VALUES (1509,1097,'rejuvenation',1,0.0,10.0,2000,0,1,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1506,1115,'winds_of_oblivion',1,0.0,16.0,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1507,1116,'seal_of_quiescence',1,0.0,16.0,2000,1500,4,0,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1508,1099,'luminous_lance',0,0.0,20.0,2000,0,4,4,0,0,0,0,0); +INSERT INTO `mob_skills` VALUES (1509,1097,'rejuvenation',1,0.0,20.0,2000,0,1,0,0,0,0,0,0); INSERT INTO `mob_skills` VALUES (1510,1098,'revelation',0,0.0,20.0,2000,0,4,0,0,0,0,0,0); -- INSERT INTO `mob_skills` VALUES (1511,1255,'slam_dunk',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); -- INSERT INTO `mob_skills` VALUES (1512,1256,'howl',0,0.0,7.0,2000,1500,4,0,0,0,0,0,0); From 3234599e65006a922d39ce99c045ab20e25b9875 Mon Sep 17 00:00:00 2001 From: sruon Date: Mon, 29 Dec 2025 16:44:15 -0700 Subject: [PATCH 2/3] Prishe raise handling --- scripts/actions/spells/white/arise.lua | 23 +++++++----- scripts/actions/spells/white/raise.lua | 7 ---- scripts/actions/spells/white/raise_ii.lua | 7 ---- scripts/actions/spells/white/raise_iii.lua | 7 ---- .../zones/Empyreal_Paradox/mobs/Prishe.lua | 37 ++++++++++--------- src/map/ai/states/death_state.cpp | 8 ++++ 6 files changed, 41 insertions(+), 48 deletions(-) diff --git a/scripts/actions/spells/white/arise.lua b/scripts/actions/spells/white/arise.lua index 09780175179..9648f0ab33f 100644 --- a/scripts/actions/spells/white/arise.lua +++ b/scripts/actions/spells/white/arise.lua @@ -5,9 +5,20 @@ local spellObject = {} spellObject.onMagicCastingCheck = function(caster, target, spell) + -- Can't cast on living targets + if target:isAlive() then + return xi.msg.basic.MAGIC_CANNOT_BE_CAST + end + + -- Only PCs should ever be blocked by the Raise/Tractor menu state + if target:isPC() and target:hasRaiseTractorMenu() then + return xi.msg.basic.MAGIC_CANNOT_BE_CAST + end + + -- Non-PC targets must be explicitly raisable (behavior bit) if - target:isAlive() or -- Can't cast on alive targets. - target:hasRaiseTractorMenu() -- Raise and tractor menus both block the casting. + not target:isPC() and + bit.band(target:getBehavior(), xi.behavior.RAISABLE) == 0 then return xi.msg.basic.MAGIC_CANNOT_BE_CAST end @@ -18,17 +29,9 @@ end spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(4) - else - -- CoP 8-4 Prishe - if target:getPool() == xi.mobPools.PRISHE then - target:addHP(target:getMaxHP()) - target:addMP(target:getMaxMP()) - target:setLocalVar('raise', 1) - end end spell:setMsg(xi.msg.basic.MAGIC_CASTS_ON) - return 4 end diff --git a/scripts/actions/spells/white/raise.lua b/scripts/actions/spells/white/raise.lua index 983e4b71243..7388dcfb268 100644 --- a/scripts/actions/spells/white/raise.lua +++ b/scripts/actions/spells/white/raise.lua @@ -29,13 +29,6 @@ end spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(1) - else - -- CoP 8-4 Prishe - if target:getPool() == xi.mobPools.PRISHE then - target:addHP(target:getMaxHP()) - target:addMP(target:getMaxMP()) - target:setLocalVar('raise', 1) - end end spell:setMsg(xi.msg.basic.MAGIC_CASTS_ON) diff --git a/scripts/actions/spells/white/raise_ii.lua b/scripts/actions/spells/white/raise_ii.lua index c8793334d6a..1fdbf87e4da 100644 --- a/scripts/actions/spells/white/raise_ii.lua +++ b/scripts/actions/spells/white/raise_ii.lua @@ -29,13 +29,6 @@ end spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(2) - else - -- CoP 8-4 Prishe - if target:getPool() == xi.mobPools.PRISHE then - target:addHP(target:getMaxHP()) - target:addMP(target:getMaxMP()) - target:setLocalVar('raise', 1) - end end spell:setMsg(xi.msg.basic.MAGIC_CASTS_ON) diff --git a/scripts/actions/spells/white/raise_iii.lua b/scripts/actions/spells/white/raise_iii.lua index b0176ee1e70..0f3d6b82f3e 100644 --- a/scripts/actions/spells/white/raise_iii.lua +++ b/scripts/actions/spells/white/raise_iii.lua @@ -29,13 +29,6 @@ end spellObject.onSpellCast = function(caster, target, spell) if target:isPC() then target:sendRaise(3) - else - -- CoP 8-4 Prishe - if target:getPool() == xi.mobPools.PRISHE then - target:addHP(target:getMaxHP()) - target:addMP(target:getMaxMP()) - target:setLocalVar('raise', 1) - end end spell:setMsg(xi.msg.basic.MAGIC_CASTS_ON) diff --git a/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua b/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua index 2aa30b0511e..dd356bcfc32 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua @@ -171,6 +171,24 @@ entity.onMobSpawn = function(mob) end end end) + + mob:addListener('MAGIC_TAKE', 'PRISHE_RAISE', function(prishe, caster, spell, action) + if spell:getSpellFamily() ~= xi.magic.spellFamily.RAISE then + return + end + + -- Prishe waits a few seconds before getting up + prishe:timer(5000, function(prisheArg) + prisheArg:entityAnimationPacket(xi.animationString.SPECIAL_00) + -- Give a few seconds for the special raise animation to play + prisheArg:timer(4000, function(prisheArg2) + prisheArg2:setHP(prisheArg2:getMaxHP()) + prisheArg2:resetAI() -- Exit the DEATH state + prisheArg2:setAnimation(xi.animation.NONE) + prisheArg2:messageText(prisheArg2, ID.text.PRISHE_TEXT + 3) + end) + end) + end) end entity.onMobEngage = function(mob, target) @@ -183,17 +201,6 @@ entity.onMobFight = function(mob, target) return end - -- Handle post-raise behavior - if mob:getLocalVar('raise') == 1 then - mob:setLocalVar('raise', 0) - mob:entityAnimationPacket(xi.animationString.SPECIAL_00) - mob:stun(2000) - mob:timer(2000, function(prishe) - prishe:messageText(prishe, ID.text.PRISHE_TEXT + 3) - prishe:setLocalVar('deathProcessed', 0) - end) - end - -- React to Promathia taking first damage local reactPhase = battlefield:getLocalVar('prisheReact') if reactPhase > 0 then @@ -261,17 +268,13 @@ entity.onMobDisengage = function(mob) end entity.onMobDeath = function(mob, target, optParams) - -- Prevents repeated messages if Prishe stays dead for some time - if mob:getLocalVar('deathProcessed') == 0 then - mob:messageText(mob, ID.text.PRISHE_TEXT + 2) - - mob:setLocalVar('deathProcessed', 1) - end + mob:messageText(mob, ID.text.PRISHE_TEXT + 2) end entity.onMobDespawn = function(mob) mob:removeListener('PRISHE_SKILL_MSG') mob:removeListener('PRISHE_DAEDALUS') + mob:removeListener('PRISHE_RAISE') end return entity diff --git a/src/map/ai/states/death_state.cpp b/src/map/ai/states/death_state.cpp index d9c26e9e7ac..3024a4df1bd 100644 --- a/src/map/ai/states/death_state.cpp +++ b/src/map/ai/states/death_state.cpp @@ -24,6 +24,7 @@ #include "ai/ai_container.h" #include "entities/battleentity.h" #include "entities/charentity.h" +#include "entities/mobentity.h" #include "packets/s2c/0x0f9_res.h" #include "status_effect.h" #include "status_effect_container.h" @@ -64,6 +65,13 @@ bool CDeathState::Update(timer::time_point tick) auto time = GetEntryTime() + m_deathTime - std::chrono::seconds(m_PEntity->getMod(Mod::DESPAWN_TIME_REDUCTION)); if (tick > time) { + auto* PMob = dynamic_cast(m_PEntity); + // RAISABLE mobs should stay in death state indefinitely until raised + if (PMob && (PMob->m_Behavior & BEHAVIOR_RAISABLE)) + { + return false; + } + Complete(); m_PEntity->OnDeathTimer(); } From 12b44e26d8f6f847d5c8782d57de15466441747a Mon Sep 17 00:00:00 2001 From: Critical <48370698+CriticalXI@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:22:34 -0700 Subject: [PATCH 3/3] [c++] NO_ASSIST behavior adjustment --- .../battlefields/Empyreal_Paradox/dawn.lua | 4 +-- scripts/enum/behavior.lua | 2 +- scripts/enum/mob_pool.lua | 4 +-- .../zones/Empyreal_Paradox/mobs/Prishe.lua | 11 ++++---- .../zones/Empyreal_Paradox/mobs/Promathia.lua | 25 ++++++++++--------- .../Empyreal_Paradox/mobs/Promathia_2.lua | 10 ++++---- .../zones/Empyreal_Paradox/mobs/Selhteus.lua | 2 +- src/map/entities/charentity.cpp | 12 ++++++++- src/map/entities/mobentity.cpp | 4 +-- src/map/entities/mobentity.h | 2 +- 10 files changed, 43 insertions(+), 33 deletions(-) diff --git a/scripts/battlefields/Empyreal_Paradox/dawn.lua b/scripts/battlefields/Empyreal_Paradox/dawn.lua index 959132009c4..d4de4e5f20d 100644 --- a/scripts/battlefields/Empyreal_Paradox/dawn.lua +++ b/scripts/battlefields/Empyreal_Paradox/dawn.lua @@ -110,7 +110,7 @@ content.groups = if ally:isMob() then ally:resetLocalVars() - if ally:getPool() == xi.mobPools.PRISHE then + if ally:getPool() == xi.mobPools.PRISHE_DAWN then local prishePos = prisheCoords[battlefieldArea] ally:setPos(prishePos.x, prishePos.y, prishePos.z, prishePos.r) @@ -118,7 +118,7 @@ content.groups = ally:setLocalVar('[helperNpc]engageWaitTime', 180) ally:setLocalVar('engageWait', GetSystemTime() + 180) - elseif ally:getPool() == xi.mobPools.SELHTEUS then + elseif ally:getPool() == xi.mobPools.SELHTEUS_DAWN then local selhteusPos = selhteusCoords[battlefieldArea] ally:setPos(selhteusPos.x, selhteusPos.y, selhteusPos.z, selhteusPos.r) end diff --git a/scripts/enum/behavior.lua b/scripts/enum/behavior.lua index 94b6b1cab7f..60a08c8fa1d 100644 --- a/scripts/enum/behavior.lua +++ b/scripts/enum/behavior.lua @@ -10,7 +10,7 @@ xi.behavior = NO_DESPAWN = 0x001, -- mob does not despawn on death STANDBACK = 0x002, -- mob will standback forever RAISABLE = 0x004, -- mob can be raised via Raise spells - NOHELP = 0x008, -- mob cannot be targeted by helpful magic from players (cure, protect, etc) + NO_ASSIST = 0x008, -- mob cannot be targeted by helpful magic from players (cure, protect, etc) AGGRO_AMBUSH = 0x200, -- mob aggroes by ambush NO_TURN = 0x400, -- mob does not turn to face target } diff --git a/scripts/enum/mob_pool.lua b/scripts/enum/mob_pool.lua index 9bd87187bc2..024d53b77ce 100644 --- a/scripts/enum/mob_pool.lua +++ b/scripts/enum/mob_pool.lua @@ -29,7 +29,7 @@ xi.mobPool = PEPPER = 3116, -- BCNM20 Charming Trio, Absorbing Kiss PHOEDME = 3132, -- BCNM20 Charming Trio, Deep Kiss PLATOON_SCORPION = 3157, -- KS30 Platoon Scorpion Wild Rage behavior - PRISHE = 3199, -- Prishe (CoP 8-4 Dawn) + PRISHE_DAWN = 3199, -- Prishe (CoP 8-4 Dawn) PROCREATOR = 3202, -- Fission (Number of Adds) PROGENERATOR = 3204, -- Fission (Number of Adds) PROPAGATOR = 3206, -- Fission (Number of Adds) @@ -43,7 +43,7 @@ xi.mobPool = WREAKER = 4382, -- Wreaker (CoP 1-3 Spire Battle) QNAERN_WHM = 4651, -- Qn'Aern WHM benediction check AMNAF_PSYCHEFLAYER = 5310, -- Reset enmity on sleepga - SELHTEUS = 5417, -- Selhteus (CoP 8-4 Dawn) + SELHTEUS_DAWN = 5417, -- Selhteus (CoP 8-4 Dawn) FAHRAFAHR_THE_BLOODIED = 6750, -- Reset Enmity on Drop Hammer HPEMDE_NO_DIVING = 7033, -- Hpemde that don't dive, such as those in the north end of Al'Taieu EOZDEI_LEFT = 7095, -- Eo'zdei left rotation diff --git a/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua b/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua index dd356bcfc32..a934a80a264 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Prishe.lua @@ -104,7 +104,7 @@ local itemActions = }, } -local useItem = function(mob, phase) +local function useItem(mob, phase) local battlefield = mob:getBattlefield() if battlefield then battlefield:setLocalVar('prisheItemTimer', GetSystemTime() + 150) @@ -248,14 +248,13 @@ entity.onMobSpellChoose = function(mob, target, spellId) local spellList = { - [1] = { xi.magic.spell.BANISH_III, target, false, xi.action.type.DAMAGE_TARGET, nil, 100 }, - [2] = { xi.magic.spell.BANISHGA_III, target, false, xi.action.type.DAMAGE_TARGET, nil, 100 }, - [3] = { xi.magic.spell.HOLY, target, false, xi.action.type.DAMAGE_TARGET, nil, 100 }, - [4] = { xi.magic.spell.CURE_IV, mob, true, xi.action.type.HEALING_TARGET, 33, 100 }, + [1] = { xi.magic.spell.BANISH_III, target, false, xi.action.type.DAMAGE_TARGET, nil, 0, 100 }, + [2] = { xi.magic.spell.BANISHGA_III, target, false, xi.action.type.DAMAGE_TARGET, nil, 0, 100 }, + [3] = { xi.magic.spell.HOLY, target, false, xi.action.type.DAMAGE_TARGET, nil, 0, 100 }, + [4] = { xi.magic.spell.CURE_IV, mob, true, xi.action.type.HEALING_TARGET, 33, 0, 100 }, } local groupTable = battlefield:getPlayers() - table.insert(groupTable, mob) -- Include self for Cure IV return xi.combat.behavior.chooseAction(mob, target, groupTable, spellList) end diff --git a/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua b/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua index d424a2cdb4d..4ac74299f5e 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Promathia.lua @@ -28,20 +28,20 @@ entity.onMobInitialize = function(mob) mob:addImmunity(xi.immunity.BIND) mob:addImmunity(xi.immunity.BLIND) mob:addImmunity(xi.immunity.STUN) - mob:addMod(xi.mod.REGAIN, 75) - mob:addMod(xi.mod.UFASTCAST, 50) - mob:setMod(xi.mod.DEF, 250) - mob:setMod(xi.mod.MDEF, 30) - mob:setMod(xi.mod.DOUBLE_ATTACK, 25) + mob:setMod(xi.mod.REGAIN, 75) + mob:setMod(xi.mod.UFASTCAST, 50) mob:setMod(xi.mod.DESPAWN_TIME_REDUCTION, 10) - mob:addMobMod(xi.mobMod.SIGHT_RANGE, 15) - mob:addMobMod(xi.mobMod.SOUND_RANGE, 15) + mob:setMobMod(xi.mobMod.SIGHT_RANGE, 15) + mob:setMobMod(xi.mobMod.SOUND_RANGE, 15) mob:setMobMod(xi.mobMod.MAGIC_COOL, 15) end entity.onMobSpawn = function(mob) mob:setLocalVar('nextBreakpoint', 90) mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 250) + mob:setMod(xi.mod.DEF, 250) + mob:setMod(xi.mod.MDEF, 30) + mob:setMod(xi.mod.DOUBLE_ATTACK, 25) end entity.onMobFight = function(mob, target) @@ -128,11 +128,12 @@ entity.onMobWeaponSkill = function(target, mob, skill) end entity.onMobDeath = function(mob, player, optParams) - local battlefield = mob:getBattlefield() - if - battlefield and - (optParams.isKiller or optParams.noKiller) - then + if optParams.isKiller or optParams.noKiller then + local battlefield = mob:getBattlefield() + if not battlefield then + return + end + battlefield:setLocalVar('phase', 1) end end diff --git a/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua b/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua index e864406c821..255a1e306e8 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Promathia_2.lua @@ -18,17 +18,17 @@ entity.onMobInitialize = function(mob) mob:addImmunity(xi.immunity.STUN) mob:setMod(xi.mod.REGAIN, 75) mob:setMod(xi.mod.UFASTCAST, 50) - mob:setMod(xi.mod.DEF, 250) - mob:setMod(xi.mod.MDEF, 30) - mob:setMod(xi.mod.DOUBLE_ATTACK, 25) - mob:addMobMod(xi.mobMod.SIGHT_RANGE, 15) - mob:addMobMod(xi.mobMod.SOUND_RANGE, 15) + mob:setMobMod(xi.mobMod.SIGHT_RANGE, 15) + mob:setMobMod(xi.mobMod.SOUND_RANGE, 15) mob:setMobMod(xi.mobMod.MAGIC_COOL, 15) end entity.onMobSpawn = function(mob) mob:setLocalVar('nextBreakpoint', 90) mob:setMobMod(xi.mobMod.BASE_DAMAGE_MULTIPLIER, 350) + mob:setMod(xi.mod.DEF, 250) + mob:setMod(xi.mod.MDEF, 30) + mob:setMod(xi.mod.DOUBLE_ATTACK, 25) mob:addListener('WEAPONSKILL_STATE_ENTER', 'PROMY_SKILL_MSG', function(mobArg, skillID) if diff --git a/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua b/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua index 525607e96e5..c90838d2b96 100644 --- a/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua +++ b/scripts/zones/Empyreal_Paradox/mobs/Selhteus.lua @@ -203,7 +203,7 @@ local findClosestNode = function(mob, target, area) end entity.onMobInitialize = function(mob) - mob:setBehavior(bit.bor(mob:getBehavior(), xi.behavior.NOHELP)) -- Disallow spells cast on Selhteus + mob:setBehavior(bit.bor(mob:getBehavior(), xi.behavior.NO_ASSIST)) -- Disallow spells cast on Selhteus mob:addMod(xi.mod.CURE_POTENCY_RCVD, -100) mob:setMobMod(xi.mobMod.NO_REST, 1) mob:setAutoAttackEnabled(false) diff --git a/src/map/entities/charentity.cpp b/src/map/entities/charentity.cpp index 3f8b4e3d653..8c62dada929 100644 --- a/src/map/entities/charentity.cpp +++ b/src/map/entities/charentity.cpp @@ -2535,7 +2535,17 @@ CBattleEntity* CCharEntity::IsValidTarget(uint16 targid, uint16 validTargetFlags } else { - errMsg = std::make_unique(this, this, 0, 0, MsgBasic::CANNOT_ATTACK_TARGET); + // Check if target is a BEHAVIOR_NO_ASSIST mob with player allegiance + auto* PEntity = GetEntity(targid, TYPE_MOB | TYPE_PC | TYPE_PET | TYPE_TRUST); + if (PEntity && PEntity->objtype == TYPE_MOB && static_cast(PEntity)->allegiance == ALLEGIANCE_TYPE::PLAYER && + (static_cast(PEntity)->m_Behavior & BEHAVIOR_NO_ASSIST)) + { + errMsg = std::make_unique(this, this, 0, 0, MsgBasic::CANNOT_ON_THAT_TARG); + } + else + { + errMsg = std::make_unique(this, this, 0, 0, MsgBasic::CANNOT_ATTACK_TARGET); + } } return nullptr; } diff --git a/src/map/entities/mobentity.cpp b/src/map/entities/mobentity.cpp index 89e20c99428..e026450e928 100644 --- a/src/map/entities/mobentity.cpp +++ b/src/map/entities/mobentity.cpp @@ -602,14 +602,14 @@ bool CMobEntity::ValidTarget(CBattleEntity* PInitiator, uint16 targetFlags) return true; } - if ((targetFlags & TARGET_PLAYER) && allegiance == PInitiator->allegiance && !isCharmed) + if ((targetFlags & TARGET_PLAYER) && allegiance == PInitiator->allegiance && !(m_Behavior & BEHAVIOR_NO_ASSIST) && !isCharmed) { return true; } if (targetFlags & TARGET_NPC) { - if (allegiance == PInitiator->allegiance && !(m_Behavior & BEHAVIOR_NOHELP) && !isCharmed) + if (allegiance == PInitiator->allegiance && !(m_Behavior & BEHAVIOR_NO_ASSIST) && !isCharmed) { return true; } diff --git a/src/map/entities/mobentity.h b/src/map/entities/mobentity.h index 6caaaca193e..22c98944442 100644 --- a/src/map/entities/mobentity.h +++ b/src/map/entities/mobentity.h @@ -99,7 +99,7 @@ enum BEHAVIOR : uint16 BEHAVIOR_NO_DESPAWN = 0x001, // mob does not despawn on death BEHAVIOR_STANDBACK = 0x002, // mob will standback forever BEHAVIOR_RAISABLE = 0x004, // mob can be raised via Raise spells - BEHAVIOR_NOHELP = 0x008, // mob can not be targeted by helpful magic from players (cure, protect, etc) + BEHAVIOR_NO_ASSIST = 0x008, // mob can not be targeted by helpful magic from players (cure, protect, etc) BEHAVIOR_AGGRO_AMBUSH = 0x200, // mob aggroes by ambush BEHAVIOR_NO_TURN = 0x400 // mob does not turn to face target };