From 0cd9023c07a433bab68bbdf8419645a130f124d9 Mon Sep 17 00:00:00 2001 From: ThrisStraizo <45871917+ThrisStraizo@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:54:55 -0700 Subject: [PATCH] Behind the Smile quest implementation This PR implements the quest Behind the Smile and the mechanics for the mob fight with Bullheaded Grosvez. --- scripts/enum/item.lua | 1 + scripts/enum/mob_skill.lua | 3 + scripts/globals/quests.lua | 2 +- .../quests/otherAreas/Behind_the_Smile.lua | 120 ++++++++++++++++++ .../Carpenters_Landing/DefaultActions.lua | 11 +- scripts/zones/Carpenters_Landing/IDs.lua | 10 +- .../mobs/Bullheaded_Grosvez.lua | 109 ++++++++++++++++ sql/mob_groups.sql | 2 +- sql/mob_pools.sql | 2 +- sql/mob_spawn_points.sql | 2 +- sql/npc_list.sql | 2 +- 11 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 scripts/quests/otherAreas/Behind_the_Smile.lua create mode 100644 scripts/zones/Carpenters_Landing/mobs/Bullheaded_Grosvez.lua diff --git a/scripts/enum/item.lua b/scripts/enum/item.lua index e4623aff679..4ff563fba7a 100644 --- a/scripts/enum/item.lua +++ b/scripts/enum/item.lua @@ -5547,6 +5547,7 @@ xi.item = KYAHAN = 12968, COTTON_KYAHAN = 12969, SOIL_KYAHAN = 12970, + MANNEQUIN_PUMPS = 12973, MERCENARYS_KYAHAN = 12975, GAITERS = 12976, COTTON_GAITERS = 12977, diff --git a/scripts/enum/mob_skill.lua b/scripts/enum/mob_skill.lua index a91dd4a406b..5aa5080251a 100644 --- a/scripts/enum/mob_skill.lua +++ b/scripts/enum/mob_skill.lua @@ -268,6 +268,9 @@ xi.mobSkill = HYPOTHERMAL_COMBUSTION_2 = 602, -- Snoll Tzar COUNTERSTANCE_1 = 603, -- Geush Urvan + SHOULDER_TACKLE_1 = 606, + SLAM_DUNK_1 = 607, + NETHER_BLAST_1 = 610, RUINOUS_OMEN_1 = 616, diff --git a/scripts/globals/quests.lua b/scripts/globals/quests.lua index 96378ce47ae..27d775b971f 100644 --- a/scripts/globals/quests.lua +++ b/scripts/globals/quests.lua @@ -519,7 +519,7 @@ xi.quest.id = PETALS_FOR_PARELBRIAUX = 74, -- + Converted ELDERLY_PURSUITS = 75, -- + Converted IN_THE_NAME_OF_SCIENCE = 76, -- + - BEHIND_THE_SMILE = 77, + BEHIND_THE_SMILE = 77, -- + Converted KNOCKING_ON_FORBIDDEN_DOORS = 78, -- + Converted CONFESSIONS_OF_A_BELLMAKER = 79, -- + Converted IN_SEARCH_OF_THE_TRUTH = 80, -- + Converted diff --git a/scripts/quests/otherAreas/Behind_the_Smile.lua b/scripts/quests/otherAreas/Behind_the_Smile.lua new file mode 100644 index 00000000000..9ce6be8286b --- /dev/null +++ b/scripts/quests/otherAreas/Behind_the_Smile.lua @@ -0,0 +1,120 @@ +----------------------------------- +-- Behind the Smile +----------------------------------- +-- Log ID: 4, Quest ID: 77 +-- Enaremand : !pos 96.514 -41 51.613 +----------------------------------- +local carpentersLandingID = zones[xi.zone.CARPENTERS_LANDING] +----------------------------------- + +local quest = Quest:new(xi.questLog.OTHER_AREAS, xi.quest.id.otherAreas.BEHIND_THE_SMILE) + +quest.reward = +{ + item = xi.item.MANNEQUIN_PUMPS, +} + +quest.sections = +{ + -- START: Talk to Enaremand (J-7) on the upper level in Tavnazian Safehold + -- QUEST AVAILABLE + { + check = function(player, status, vars) + return status == xi.questStatus.QUEST_AVAILABLE and + player:hasCompletedQuest(xi.questLog.OTHER_AREAS, xi.quest.id.otherAreas.ITS_RAINING_MANNEQUINS) + end, + + [xi.zone.TAVNAZIAN_SAFEHOLD] = + { + ['Enaremand'] = quest:progressEvent(533), + + onEventFinish = + { + [533] = function(player, csid, option, npc) + quest:begin(player) + end, + }, + }, + }, + + -- QUEST ACCEPTED + { + check = function(player, status, vars) + return status == xi.questStatus.QUEST_ACCEPTED + end, + + [xi.zone.TAVNAZIAN_SAFEHOLD] = + { + ['Enaremand'] = + { + onTrigger = function(player, npc) + if player:hasKeyItem(xi.ki.RED_OIL) then + return quest:progressEvent(534) + else + return quest:event(541) + end + end, + }, + + onEventFinish = + { + [534] = function(player, csid, option, npc) + if quest:complete(player) then + player:delKeyItem(xi.ki.RED_OIL) + end + end, + }, + }, + + [xi.zone.MHAURA] = + { + ['Fyi_Chalmwoh'] = + { + onTrigger = function(player, npc) + if quest:getVar(player, 'Prog') == 0 then + return quest:progressEvent(320) + end + end, + }, + + onEventFinish = + { + [320] = function(player, csid, option, npc) + quest:setVar(player, 'Prog', 1) + end, + }, + }, + + [xi.zone.CARPENTERS_LANDING] = + { + ['qm_behind_the_smile'] = + { + onTrigger = function(player, npc) + local questProgress = quest:getVar(player, 'Prog') + if + questProgress == 1 and + not GetMobByID(carpentersLandingID.mob.BULLHEADED_GROSVEZ):isSpawned() + then + SpawnMob(carpentersLandingID.mob.BULLHEADED_GROSVEZ):updateClaim(player) + return quest:messageSpecial(carpentersLandingID.text.STENCH_OF_DECAY) + elseif questProgress == 2 then + quest:setVar(player, 'Prog', 3) + npcUtil.giveKeyItem(player, xi.ki.RED_OIL) + return quest:noAction() + end + end, + }, + + ['Bullheaded_Grosvez'] = + { + onMobDeath = function(mob, player, optParams) + if quest:getVar(player, 'Prog') == 1 then + quest:setVar(player, 'Prog', 2) + end + end, + }, + } + }, +} + +return quest diff --git a/scripts/zones/Carpenters_Landing/DefaultActions.lua b/scripts/zones/Carpenters_Landing/DefaultActions.lua index aaa2fdcd76a..44df5fcbdc8 100644 --- a/scripts/zones/Carpenters_Landing/DefaultActions.lua +++ b/scripts/zones/Carpenters_Landing/DefaultActions.lua @@ -1,9 +1,10 @@ local ID = zones[xi.zone.CARPENTERS_LANDING] return { - ['Anguenet'] = { event = 21 }, - ['Guilloud'] = { event = 1 }, - ['Lourdaude'] = { event = 26 }, - ['qm_cryptonberries'] = { messageSpecial = ID.text.CRYPTONBERRY_FALLEN_TREE }, - ['qm_para'] = { messageSpecial = ID.text.NOTHING_OUT_OF_ORDINARY }, + ['Anguenet'] = { event = 21 }, + ['Guilloud'] = { event = 1 }, + ['Lourdaude'] = { event = 26 }, + ['qm_behind_the_smile'] = { messageSpecial = ID.text.NOTHING_OUT_OF_ORDINARY }, + ['qm_cryptonberries'] = { messageSpecial = ID.text.CRYPTONBERRY_FALLEN_TREE }, + ['qm_para'] = { messageSpecial = ID.text.NOTHING_OUT_OF_ORDINARY }, } diff --git a/scripts/zones/Carpenters_Landing/IDs.lua b/scripts/zones/Carpenters_Landing/IDs.lua index 756a170acad..f1a2c91a26b 100644 --- a/scripts/zones/Carpenters_Landing/IDs.lua +++ b/scripts/zones/Carpenters_Landing/IDs.lua @@ -37,6 +37,7 @@ zones[xi.zone.CARPENTERS_LANDING] = CRYPTONBERRY_ASSASSIN_2HR = 7492, -- ..Take up thy lanternsss. The truth we shall illuminate. CRYPTONBERRY_EXECUTOR_2HR = 7493, -- Through this we ssseek our just reward... POLISH_MUSHROOM_SPORE = 7494, -- You polish the with the glowing mushroom spores! + CATCH_HIS_BREATH = 7496, -- Bullheaded Grosvez pauses to catch his breath. MYCOPHILE_MUSHROOM = 7510, -- There is a rotten mushroom here. There are 3 openings in its cap. HERCULES_TREE_NOTHING_YET = 7512, -- There is nothing here yet. Check again in the morning. UNITY_WANTED_BATTLE_INTERACT = 7555, -- Those who have accepted % must pay # Unity accolades to participate. The content for this Wanted battle is #. [Ready to begin?/You do not have the appropriate object set, so your rewards will be limited.] @@ -44,13 +45,14 @@ zones[xi.zone.CARPENTERS_LANDING] = }, mob = { - ORCTRAP = GetFirstID('Orctrap'), - TEMPEST_TIGON = GetFirstID('Tempest_Tigon'), - OVERGROWN_IVY = GetFirstID('Overgrown_Ivy'), + BULLHEADED_GROSVEZ = GetFirstID('Bullheaded_Grosvez'), CRYPTONBERRY_EXECUTOR = GetFirstID('Cryptonberry_Executor'), - MYCOPHILE = GetFirstID('Mycophile'), HERCULES_BEETLE = GetFirstID('Hercules_Beetle'), + MYCOPHILE = GetFirstID('Mycophile'), + ORCTRAP = GetFirstID('Orctrap'), + OVERGROWN_IVY = GetFirstID('Overgrown_Ivy'), PARA = GetFirstID('Para'), + TEMPEST_TIGON = GetFirstID('Tempest_Tigon'), }, npc = { diff --git a/scripts/zones/Carpenters_Landing/mobs/Bullheaded_Grosvez.lua b/scripts/zones/Carpenters_Landing/mobs/Bullheaded_Grosvez.lua new file mode 100644 index 00000000000..715a199b175 --- /dev/null +++ b/scripts/zones/Carpenters_Landing/mobs/Bullheaded_Grosvez.lua @@ -0,0 +1,109 @@ +----------------------------------- +-- Area: Carpenters Landing +-- NM: Bullheaded Grosvez +-- Quest: Behind the Smile +----------------------------------- +local ID = zones[xi.zone.CARPENTERS_LANDING] +----------------------------------- +mixins = { require('scripts/mixins/job_special'), } +----------------------------------- +---@type TMobEntity +local entity = {} + +entity.onMobInitialize = function(mob) + mob:setMobMod(xi.mobMod.IDLE_DESPAWN, 180) + mob:addImmunity(xi.immunity.DARK_SLEEP) + mob:addImmunity(xi.immunity.LIGHT_SLEEP) +end + +entity.onMobSpawn = function(mob) + mob:addListener('EFFECT_LOSE', 'HUNDRED_FISTS_EFFECT_LOSE', function(mobArg, effect) + if effect:getEffectType() == xi.effect.HUNDRED_FISTS then + mobArg:messageText(mobArg, ID.text.CATCH_HIS_BREATH, false) + mobArg:setAutoAttackEnabled(false) + mobArg:setLocalVar('takeABreather', GetSystemTime() + 15) + + -- Reset enmity if needed. + local target = mobArg:getTarget() + if target then + mobArg:resetEnmity(target) + end + end + end) + + xi.mix.jobSpecial.config(mob, { + specials = + { + { id = xi.mobSkill.HUNDRED_FISTS_1 }, + { id = xi.mobSkill.HUNDRED_FISTS_1, cooldown = 390, hpp = math.random(75, 85) }, + }, + }) +end + +entity.onMobFight = function(mob, target) + if mob:getLocalVar('takeABreather') < GetSystemTime() then + mob:setAutoAttackEnabled(true) + end +end + +entity.onMobMobskillChoose = function(mob, target, skillId) + local skillList = + { + xi.mobSkill.SLAM_DUNK_1, + xi.mobSkill.SHOULDER_TACKLE_1 + } + + return skillList[math.random(1, #skillList)] +end + +entity.onMobWeaponSkill = function(mob, target, skill, action) + local skillId = skill:getID() + + if skillId == xi.mobSkill.SLAM_DUNK_1 then + local slamDunkRepeats = mob:getLocalVar('slamDunkRepeats') + + if slamDunkRepeats < 2 then + mob:useMobAbility(xi.mobSkill.SLAM_DUNK_1) + mob:setLocalVar('slamDunkRepeats', slamDunkRepeats + 1) + else + mob:setLocalVar('slamDunkRepeats', 0) + mob:timer(2500, function(mobArg) + mobArg:messageText(mobArg, ID.text.CATCH_HIS_BREATH, false) + mobArg:setLocalVar('takeABreather', GetSystemTime() + 15) + mobArg:setAutoAttackEnabled(false) + + local targetArg = mobArg:getTarget() + if targetArg then + mobArg:resetEnmity(targetArg) + end + end) + end + end + + if skillId == xi.mobSkill.SHOULDER_TACKLE_1 then + local shoulderTackleRepeats = mob:getLocalVar('shoulderTackleRepeats') + + if shoulderTackleRepeats < 2 then + mob:useMobAbility(xi.mobSkill.SHOULDER_TACKLE_1) + mob:setLocalVar('shoulderTackleRepeats', shoulderTackleRepeats + 1) + else + mob:setLocalVar('shoulderTackleRepeats', 0) + mob:timer(2500, function(mobArg) + mobArg:messageText(mobArg, ID.text.CATCH_HIS_BREATH, false) + mobArg:setLocalVar('takeABreather', GetSystemTime() + 15) + mobArg:setAutoAttackEnabled(false) + + local targetArg = mobArg:getTarget() + if targetArg then + mobArg:resetEnmity(targetArg) + end + end) + end + end +end + +entity.onMobDespawn = function(mob) + mob:removeListener('HUNDRED_FISTS_EFFECT_LOSE') +end + +return entity diff --git a/sql/mob_groups.sql b/sql/mob_groups.sql index dd0c75f15be..81f8a75bb37 100644 --- a/sql/mob_groups.sql +++ b/sql/mob_groups.sql @@ -115,7 +115,7 @@ INSERT INTO `mob_groups` VALUES (45,4834,2,'Cryptonberry_Assassin',0,128,0,4500, INSERT INTO `mob_groups` VALUES (46,3971,2,'Tonberrys_Elemental',0,128,0,0,0,0,NULL); INSERT INTO `mob_groups` VALUES (47,3970,2,'Tonberrys_Avatar',0,128,0,0,0,0,NULL); INSERT INTO `mob_groups` VALUES (48,3097,2,'Para',0,128,0,2800,0,0,NULL); -INSERT INTO `mob_groups` VALUES (49,576,2,'Bullheaded_Grosvez',0,128,0,0,0,0,NULL); +INSERT INTO `mob_groups` VALUES (49,576,2,'Bullheaded_Grosvez',0,128,0,5000,0,0,NULL); INSERT INTO `mob_groups` VALUES (50,2784,2,'Mycophile',0,128,1761,2500,0,0,NULL); INSERT INTO `mob_groups` VALUES (51,1934,2,'Hercules_Beetle',0,128,1303,2238,0,0,NULL); INSERT INTO `mob_groups` VALUES (52,6809,2,'Orcfeltrap',0,128,0,0,0,0,NULL); diff --git a/sql/mob_pools.sql b/sql/mob_pools.sql index 550a8109a81..745f527c078 100644 --- a/sql/mob_pools.sql +++ b/sql/mob_pools.sql @@ -631,7 +631,7 @@ INSERT INTO `mob_pools` VALUES (572,'Bukhis','Bukhis',240,0x00005609000000000000 INSERT INTO `mob_pools` VALUES (573,'Bukki','Bukki',165,0x0000BE0600000000000000000000000000000000,4,4,7,240,100,0,0,0,0,2,0,0,0,3,0,0,2,0,0,165,165,1,12); INSERT INTO `mob_pools` VALUES (574,'Bullbeggar','Bullbeggar',188,0x0000A00100000000000000000000000000000000,1,1,7,200,100,0,1,0,1,0,0,0,139,133,0,0,0,0,0,188,188,2,21); INSERT INTO `mob_pools` VALUES (575,'Bulldog_Bats','Bulldog_Bats',47,0x0000040100000000000000000000000000000000,1,1,11,240,100,0,1,0,0,0,0,0,0,3,0,0,0,0,0,47,47,1,16); -INSERT INTO `mob_pools` VALUES (576,'Bullheaded_Grosvez','Bullheaded_Grosvez',189,0x0000700200000000000000000000000000000000,2,2,1,480,100,0,1,0,1,0,0,0,0,3,0,0,0,0,0,334,334,2,17); +INSERT INTO `mob_pools` VALUES (576,'Bullheaded_Grosvez','Bullheaded_Grosvez',189,0x0000700200000000000000000000000000000000,2,2,1,360,100,0,1,0,1,0,0,0,0,3,0,0,0,0,0,334,334,2,17); INSERT INTO `mob_pools` VALUES (577,'Bull_Bugard','Bull_Bugard',58,0x0000470500000000000000000000000000000000,1,1,3,300,100,0,1,1,1,0,0,0,8,129,0,0,0,0,0,58,58,1,12); INSERT INTO `mob_pools` VALUES (578,'Bull_Dhalmel','Bull_Dhalmel',80,0x00004C0100000000000000000000000000000000,1,1,12,240,100,0,0,0,1,0,0,0,419,131,0,0,0,0,0,80,80,1,37); INSERT INTO `mob_pools` VALUES (579,'Bull_[Herd1]','Bull_[Herd1]',226,0x00004D0500000000000000000000000000000000,1,1,0,0,100,0,0,0,0,0,0,0,0,133,0,0,0,0,0,226,226,NULL,NULL); diff --git a/sql/mob_spawn_points.sql b/sql/mob_spawn_points.sql index fb7354e703d..28dba42f61d 100644 --- a/sql/mob_spawn_points.sql +++ b/sql/mob_spawn_points.sql @@ -364,7 +364,7 @@ INSERT INTO `mob_spawn_points` VALUES (16785717,0,'Para','Para',48,51,51,-412.54 INSERT INTO `mob_spawn_points` VALUES (16785718,0,'Para','Para',48,51,51,-412.864,-0.094,-359.222,26); INSERT INTO `mob_spawn_points` VALUES (16785719,0,'Para','Para',48,51,51,-413.243,0.000,-361.204,254); INSERT INTO `mob_spawn_points` VALUES (16785720,0,'Para','Para',48,51,51,-416.734,-0.465,-365.799,54); -INSERT INTO `mob_spawn_points` VALUES (16785721,0,'Bullheaded_Grosvez','Bullheaded Grosvez',49,40,40,37.861,-8.684,-568.912,198); +INSERT INTO `mob_spawn_points` VALUES (16785721,0,'Bullheaded_Grosvez','Bullheaded Grosvez',49,45,45,37.861,-8.684,-568.912,198); INSERT INTO `mob_spawn_points` VALUES (16785722,0,'Mycophile','Mycophile',50,35,35,145.500,-9.483,-699.000,74); INSERT INTO `mob_spawn_points` VALUES (16785723,0,'Hercules_Beetle','Hercules Beetle',51,34,34,-238.000,-5.950,-93.500,180); INSERT INTO `mob_spawn_points` VALUES (16785724,0,'Orcfeltrap','Orcfeltrap',52,0,0,118.940,-6.433,-437.238,14); diff --git a/sql/npc_list.sql b/sql/npc_list.sql index 2b438d886d5..2fceace2c1d 100644 --- a/sql/npc_list.sql +++ b/sql/npc_list.sql @@ -130,7 +130,7 @@ INSERT INTO `npc_list` VALUES (16785778,'Cofisephe','Cofisephe',177,210.327,-2.8 INSERT INTO `npc_list` VALUES (16785779,'Coupulie','Coupulie',131,-313.585,-2.628,490.944,1,40,40,0,1,0,0,27,0x0100000414102620083003400350006000700000,32,'COP',1); INSERT INTO `npc_list` VALUES (16785780,'Echanie','Echanie',139,-148.424,-2.948,45.675,1,50,50,0,1,0,0,27,0x010003041410172000300A400350006000700000,32,'COP',1); INSERT INTO `npc_list` VALUES (16785781,'qm_para','???',0,-416.126,0.000,-363.468,1,50,50,0,0,0,0,3,0x0000320000000000000000000000000000000000,0,'COP',0); -INSERT INTO `npc_list` VALUES (16785782,'qm','???',240,39.877,-7.397,-565.422,1,50,50,0,0,0,0,3,0x0000340000000000000000000000000000000000,0,'COP',0); +INSERT INTO `npc_list` VALUES (16785782,'qm_behind_the_smile','???',240,39.877,-7.397,-565.422,1,50,50,0,0,0,0,3,0x0000340000000000000000000000000000000000,0,'COP',0); INSERT INTO `npc_list` VALUES (16785783,'relic','',126,-99.577,-0.016,-514.720,32769,50,50,0,0,0,0,3,0x0000340000000000000000000000000000000000,0,'COP',1); INSERT INTO `npc_list` VALUES (16785784,'Perseus','Perseus',0,0.000,0.000,0.000,0,40,40,0,0,0,6,2075,0x010001014D104D204D304D404D50166133700000,32,'COP',1); INSERT INTO `npc_list` VALUES (16785785,'qm','???',0,0.000,0.000,0.000,0,50,50,0,0,0,2,2051,0x0000320000000000000000000000000000000000,0,'COP',0);