diff --git a/scripts/actions/abilities/pets/chronoshift.lua b/scripts/actions/abilities/pets/chronoshift.lua index b72ce87dfcf..d0bdfe037ee 100644 --- a/scripts/actions/abilities/pets/chronoshift.lua +++ b/scripts/actions/abilities/pets/chronoshift.lua @@ -24,12 +24,6 @@ abilityObject.onPetAbility = function(target, pet, petskill, summoner, action) petskill:setMsg(xi.msg.basic.NO_EFFECT) end - pet:timer(5000, function() - if summoner then - summoner:despawnPet() - end - end) - return effectCount end diff --git a/scripts/actions/abilities/pets/zantetsuken.lua b/scripts/actions/abilities/pets/zantetsuken.lua index 769191f5e73..b6db0fe309d 100644 --- a/scripts/actions/abilities/pets/zantetsuken.lua +++ b/scripts/actions/abilities/pets/zantetsuken.lua @@ -11,13 +11,19 @@ local abilityObject = {} abilityObject.onAbilityCheck = function(player, target, ability) - return xi.job_utils.summoner.canUseBloodPact(player, player:getPet(), target, ability) + return 0, 0 end abilityObject.onPetAbility = function(target, pet, petskill, summoner, action) + if summoner == nil then + return 0 + end + local returnParam = 0 local power = summoner:getMP() / utils.clamp(summoner:getMaxMP(), 1, 9999) + summoner:delStatusEffect(xi.effect.ASTRAL_FLOW) + if target:isNM() then local params = {} @@ -37,14 +43,16 @@ abilityObject.onPetAbility = function(target, pet, petskill, summoner, action) returnParam = info.damage else - -- Insta-kill: Highly innacurate against regular monsters. - local chance = 50 * power / utils.clamp(petskill:getTotalTargets(), 1, 50) + -- Zantetsuken chance to kill starts at MP/MaxMP so at full MP the kill chance is 100% + -- This is then divided by the number of targets. So with 4 targets, each one has a 25% + -- to die if the summoner has full MP. I am guessing at the 1% floor with 100 targets. + -- I have never tested this against more than 5-6 targets on retail. + local chance = 100 * power / utils.clamp(petskill:getTotalTargets(), 1, 100) if math.randomInt(1, 100) <= chance and target:getAnimation() ~= 33 then - petskill:setMsg(xi.msg.basic.SKILL_ENFEEB_IS) target:takeDamage(target:getHP(), pet, xi.attackType.MAGICAL, xi.damageType.DARK) returnParam = xi.effect.KO diff --git a/scripts/globals/pets.lua b/scripts/globals/pets.lua index bb5d4296013..2c7f74fb9e3 100644 --- a/scripts/globals/pets.lua +++ b/scripts/globals/pets.lua @@ -116,26 +116,39 @@ xi.pet.spawnPet = function(caster, petID, state, target) end) end elseif petID == xi.petId.ODIN then - if target then - caster:petAttack(target) - --pet:timer(5000, function() - -- pet:usePetAbility(xi.jobAbility.ZANTETSUKEN, target) - --end) + local pet = caster:getPet() + if pet then + pet:timer(5000, function() + if target and target:isAlive() then + pet:usePetAbility(xi.jobAbility.ZANTETSUKEN, target) + end + end) + -- Retail Odin despawns 15 seconds after appearing even if he fails to use Zantetsuken + pet:timer(15000, function() + caster:despawnPet() + end) end elseif petID == xi.petId.ATOMOS then - if target then - -- Use Deconstruction on the target 3 seconds after spawning. - local pet = caster:getPet() - if pet then - -- Timed sequence after spawning, wait -> Deconstruction -> wait -> Chronoshift (despawn pet after complete) - pet:timer(3000, function() + local pet = caster:getPet() + if pet then + -- Timed sequence after spawning, wait -> Deconstruction -> wait -> Chronoshift -> despawn + -- Full cycle takes 5s -> 5s -> 5s from summon to despawn + charge times which ends up at about 17s + -- On retail the behavior appears to be that Atomos will always do Deconstruction if the target is in range + -- If the target moves out of range, Atomos still does Chronoshift as long as the summoner is within 30 yalms + -- If the target and the summoner are both out of range then Atomos just waits and despawns + pet:timer(5000, function() + if target and target:isAlive() then pet:usePetAbility(xi.jobAbility.DECONSTRUCTION, target) - end) - - pet:timer(10000, function() - pet:usePetAbility(xi.jobAbility.CHRONOSHIFT, pet) - end) - end + end + end) + -- 11 seconds here gives time for the animation to consistently line up to retail + pet:timer(11000, function() + pet:usePetAbility(xi.jobAbility.CHRONOSHIFT, pet) + end) + -- Ensure Atomos always despawns + pet:timer(17000, function() + caster:despawnPet() + end) end end end diff --git a/sql/abilities.sql b/sql/abilities.sql index dfb2ba3e662..c09a73eb9f8 100644 --- a/sql/abilities.sql +++ b/sql/abilities.sql @@ -523,7 +523,7 @@ INSERT INTO `abilities` VALUES (666,'pavor_nocturnus',15,98,4,60,174,0,0,94,2000 INSERT INTO `abilities` VALUES (667,'blindside',15,99,4,60,173,0,0,94,2000,0,6,10,0,0,1,60,0,0,'SOA'); INSERT INTO `abilities` VALUES (668,'deconstruction',15,75,4,0,0,0,0,0,0,0,6,20,0,0,0,0,0,0,NULL); INSERT INTO `abilities` VALUES (669,'chronoshift',15,75,4,0,0,0,0,0,0,0,6,0,0,14,0,0,0,0,NULL); --- INSERT INTO `abilities` VALUES (670,'zantetsuken',22,1,1,0,300,0,0,???,2000,0,6,10,0,10,450,900,0,0,NULL); +-- INSERT INTO `abilities` VALUES (670,'zantetsuken',22,1,1,0,300,0,0,151,2000,0,6,10,0,10,450,900,0,0,NULL); INSERT INTO `abilities` VALUES (671,'perfect_defense',15,75,1,0,300,0,0,152,2000,0,6,0,0,10,0,0,0,2,NULL); INSERT INTO `abilities` VALUES (672,'foot_kick',9,25,257,1,102,0,0,0,2000,0,6,3,0,0,1,60,0,0,NULL); INSERT INTO `abilities` VALUES (673,'dust_cloud',9,25,257,1,102,0,0,0,2000,0,6,3,0,10,1,60,0,0,NULL); diff --git a/sql/pet_skills.sql b/sql/pet_skills.sql index 5410be2924d..5907566da97 100644 --- a/sql/pet_skills.sql +++ b/sql/pet_skills.sql @@ -187,7 +187,7 @@ INSERT INTO `pet_skills` VALUES (666,0,155,'pavor_nocturnus',0,0,12,2000,1000,4, INSERT INTO `pet_skills` VALUES (667,0,173,'blindside',0,0,10,2000,1000,4,317,@SKILLFLAG_BLOODPACT_RAGE,0,13,0,@SC_GRAVITATION,@SC_TRANSFIXION,0); -- No LUA yet INSERT INTO `pet_skills` VALUES (668,0,171,'deconstruction',0,0,21,2000,1000,4,323,@SKILLFLAG_BLOODPACT_RAGE,0,13,0,0,0,0); INSERT INTO `pet_skills` VALUES (669,0,172,'chronoshift',1,14,0,2000,1000,3,283,@SKILLFLAG_BLOODPACT_WARD,0,13,0,0,0,0); --- 670: Zantetsuken 10y (Radius 10y) +INSERT INTO `pet_skills` VALUES (670,0,151,'zantetsuken',4,15,15.0,2000,1000,4,277,@SKILLFLAG_ASTRAL_FLOW | @SKILLFLAG_BLOODPACT_RAGE,0,13,0,0,0,0); -- Conal 15y, anim/msg from capture INSERT INTO `pet_skills` VALUES (671,0,152,'perfect_defense',1,10,0,2000,1000,3,316,@SKILLFLAG_ASTRAL_FLOW | @SKILLFLAG_BLOODPACT_WARD,0,13,0,0,0,0); -- Jug Pet Ready Moves INSERT INTO `pet_skills` VALUES (672,3840,1,'foot_kick',0,0,3,2000,1500,4,185,@SKILLFLAG_NONE,0,11,0,@SC_REVERBERATION,0,0); diff --git a/src/map/ai/states/petskill_state.cpp b/src/map/ai/states/petskill_state.cpp index 116e9d653c4..dfa47c9e567 100644 --- a/src/map/ai/states/petskill_state.cpp +++ b/src/map/ai/states/petskill_state.cpp @@ -168,7 +168,7 @@ bool CPetSkillState::Update(timer::time_point tick) PSummoner->StatusEffectContainer->GetStatusEffect(xi::StatusEffect::AvatarsFavor)->SetPower(power > 11 ? power : 11); } - if (PTarget && m_PEntity->getPetType() == PET_TYPE::AVATAR && (m_PEntity->petID() != PETID_ALEXANDER && m_PEntity->petID() != PETID_ATOMOS)) + if (PTarget && m_PEntity->getPetType() == PET_TYPE::AVATAR && (m_PEntity->petID() != PETID_ALEXANDER && m_PEntity->petID() != PETID_ATOMOS && m_PEntity->petID() != PETID_ODIN)) { auto* PBattleTarget = dynamic_cast(PTarget); if (PBattleTarget &&