From 2ed5f5044db61558716b2888c3623114b50b51f5 Mon Sep 17 00:00:00 2001 From: Flibe-XI Date: Sun, 25 Jan 2026 12:12:45 -0500 Subject: [PATCH] Added core structure of brigands chart quest --- scripts/enum/animation_string.lua | 3 + scripts/enum/item.lua | 5 + scripts/globals/brigands_chart.lua | 183 ++++++++++++++++++ .../Buburimu_Peninsula/DefaultActions.lua | 1 + scripts/zones/Buburimu_Peninsula/IDs.lua | 19 +- .../Buburimu_Peninsula/npcs/Jade_Etui.lua | 13 ++ scripts/zones/Buburimu_Peninsula/npcs/qm1.lua | 24 +++ sql/npc_list.sql | 4 +- 8 files changed, 247 insertions(+), 5 deletions(-) create mode 100644 scripts/globals/brigands_chart.lua create mode 100644 scripts/zones/Buburimu_Peninsula/npcs/Jade_Etui.lua create mode 100644 scripts/zones/Buburimu_Peninsula/npcs/qm1.lua diff --git a/scripts/enum/animation_string.lua b/scripts/enum/animation_string.lua index 04cd5a196fe..dcfd61f592d 100644 --- a/scripts/enum/animation_string.lua +++ b/scripts/enum/animation_string.lua @@ -142,4 +142,7 @@ xi.animationString = SYNERGY_LIGHT_FEWELL = 'ef97', SYNERGY_DARK_FEWELL = 'ef98', SYNERGY_STEAM = 'efa0', + + -- QM animations + SHIMMER = 'efon', } diff --git a/scripts/enum/item.lua b/scripts/enum/item.lua index a68f9aa5191..1eb17158abf 100644 --- a/scripts/enum/item.lua +++ b/scripts/enum/item.lua @@ -24,10 +24,12 @@ xi.item = KADOMATSU = 87, WASTEBASKET = 89, RUSTY_BUCKET = 90, + BLUE_PITCHER = 91, TARUTARU_STOOL = 92, WATER_CASK = 93, OKADOMATSU = 100, TARUTARU_FOLDING_SCREEN = 104, + SKY_POT = 105, RED_JAR = 106, WATER_JUG = 107, WHITE_JAR = 110, @@ -56,6 +58,7 @@ xi.item = JEUNOAN_FLAG = 184, COPY_OF_AUTUMNS_END_IN_GUSTABERG = 188, COPY_OF_ACOLYTES_GRIEF = 189, + COPY_OF_LINES_AND_SPACE = 190, FISHING_HOLE_MAP = 191, MELODIUS_EGG = 196, CLOCKWORK_EGG = 197, @@ -66,6 +69,7 @@ xi.item = BRASS_FLOWERPOT = 217, EARTHEN_FLOWERPOT = 218, CERAMIC_FLOWERPOT = 219, + WOODEN_FLOWERPOT = 220, HUME_M_MANNEQUIN = 256, HUME_F_MANNEQUIN = 257, ELVAAN_M_MANNEQUIN = 258, @@ -6691,6 +6695,7 @@ xi.item = MINERVAS_RING = 15550, ALBATROSS_RING = 15552, PELICAN_RING = 15554, + PENGUIN_RING = 15556, DIABOLOSS_RING = 15557, MIGHTY_RING = 15558, VISION_RING = 15559, diff --git a/scripts/globals/brigands_chart.lua b/scripts/globals/brigands_chart.lua new file mode 100644 index 00000000000..db4c735e5c4 --- /dev/null +++ b/scripts/globals/brigands_chart.lua @@ -0,0 +1,183 @@ +----------------------------------- +-- Brigand's Chart Quest (hidden) +----------------------------------- +-- !additem 1873 -- Brigand's Chart +-- qm1 : !pos -87.5 20 -330.9 118 +----------------------------------- +local ID = zones[xi.zone.BUBURIMU_PENINSULA] +----------------------------------- + +xi = xi or {} +xi.brigandsChart = xi.brigandsChart or {} + +local function removeFromEvent(player) + if not player then + return + end + + player:delStatusEffect(xi.effect.LEVEL_RESTRICTION) + player:changeMusic(0, 0) + player:changeMusic(1, 0) + player:changeMusic(2, 101) + player:changeMusic(3, 102) +end + +local function resetEvent() + local qm1 = GetNPCByID(ID.npc.BRIGAND_CHART_QM) + local npcHume = GetNPCByID(ID.npc.BRIGAND_CHART_HUME) + local shimmering = GetNPCByID(ID.npc.SHIMMERING_POINT) + local jadeEtuis = ID.npc.JADE_ETUI_TABLE + + if qm1 then + local player = GetPlayerByID(qm1:getLocalVar('bChartSpawnerID')) + removeFromEvent(player) + + qm1:resetLocalVars() + qm1:setStatus(xi.status.NORMAL) + end + + if npcHume then + npcHume:setStatus(xi.status.DISAPPEAR) + npcHume:setAnimation(xi.animation.NONE) + end + + if shimmering then + shimmering:setStatus(xi.status.DISAPPEAR) + shimmering:entityAnimationPacket(xi.animationString.STATUS_DISAPPEAR) + end + + -- Disappear Jade Etuis + if jadeEtuis then + for _, id in ipairs(jadeEtuis) do + local jade = GetNPCByID(id) + + if jade then + jade:setStatus(xi.status.DISAPPEAR) + jade:setAnimation(xi.animation.NONE) + jade:entityAnimationPacket(xi.animationString.STATUS_DISAPPEAR) + jade:resetLocalVars() + end + end + end +end + +local eventTable = +{ + -- Event times taken from capture: https://www.youtube.com/watch?v=aQXSByinBn4 + { time = 60, text = ID.text.WHAT_CAN_I_DO + 0 }, + { time = 90, text = ID.text.WHAT_CAN_I_DO + 1 }, + { time = 120, text = ID.text.WHAT_CAN_I_DO + 2 }, + { time = 150, text = ID.text.WHAT_CAN_I_DO + 3 }, + { time = 170, text = ID.text.WHAT_CAN_I_DO + 4 }, + { time = 180, text = ID.text.WHAT_CAN_I_DO + 5 }, +} + +local function tryHumeText(spawner, elapsedTime, timerTable) + if not timerTable then + return + end + + local npcHume = GetNPCByID(ID.npc.BRIGAND_CHART_HUME) + local qm1 = GetNPCByID(ID.npc.BRIGAND_CHART_QM) + local event = timerTable[1] + + if not (qm1 and npcHume and event) then + return + end + + if elapsedTime < event.time then + return timerTable + end + + if event.text then + spawner:showText(qm1, event.text) + end + + table.remove(timerTable, 1) + + return timerTable +end + +local function emoteChecking(npc, spawner, timeRemaining, timeOfLastCheck, endTime, timerTable) + -- Event continues if player leaves zone + -- https://www.youtube.com/watch?v=_opqVW-HIu0 + -- https://discord.com/channels/443544205206355968/446401624102010901/650072608922009660 + + local timeOfCurrentCheck = GetSystemTime() + local timeElapsedThisCheck = timeOfCurrentCheck - timeOfLastCheck + timeRemaining = timeRemaining - timeElapsedThisCheck + local totalTimeElapsed = 180 - timeRemaining + + timerTable = tryHumeText(spawner, totalTimeElapsed, timerTable) + + if timeRemaining > 0 then + npc:timer(1000, function(npcArg) + emoteChecking(npcArg, spawner, timeRemaining, timeOfCurrentCheck, endTime, timerTable) + end) + else + resetEvent() + end +end + +xi.brigandsChart.onTrade = function(player, npc, trade) + if + npc:getStatus() == xi.status.NORMAL and + npcUtil.tradeHasExactly(trade, xi.item.BRIGANDS_CHART) and + false -- disabled until quest fully complete, remove to test + then + player:messageSpecial(ID.text.RETURN_TO_SEA, xi.item.BRIGANDS_CHART) + player:startEvent(902) + end +end + +xi.brigandsChart.onEventUpdate = function(player, csid, option, npc) + if csid == 902 and option == 0 then + player:confirmTrade() + + npc:setLocalVar('bChartSpawnerID', player:getID()) + + player:changeMusic(0, 136) + player:changeMusic(1, 136) + player:changeMusic(2, 136) + player:changeMusic(3, 136) + player:addStatusEffect(xi.effect.LEVEL_RESTRICTION, 20, 0, 0) -- level restriction removed by event end + end +end + +xi.brigandsChart.onEventFinish = function(player, csid, option, npc) + if csid == 902 and option == 0 then + local npcHume = GetNPCByID(ID.npc.BRIGAND_CHART_HUME) + if not npcHume then + printf('[error] Brigand\'s Chart Hume npc not found') + return + end + + local shimmering = GetNPCByID(ID.npc.SHIMMERING_POINT) + if not shimmering then + printf('[error] Brigand\'s Chart shimmering point npc not found') + return + end + + -- Setup starting conditions + npcHume:setStatus(xi.status.NORMAL) + npcHume:setAnimation(xi.animation.NONE) + npc:setStatus(xi.status.DISAPPEAR) + + player:showText(npc, ID.text.MY_ITEM, xi.item.PENGUIN_RING) + + shimmering:setStatus(xi.status.NORMAL) + -- Appearing packet needs time to finish before another packet can be sent successfully + npc:timer(2000, function() + shimmering:entityAnimationPacket(xi.animationString.SHIMMER) + end) + + -- Events will occur for the next 180 seconds according to eventTable + local endTime = GetSystemTime() + 180 + emoteChecking(npc, player, 180, GetSystemTime(), endTime, eventTable) + -- TODO: add fishing hook to catch chests & monster specific to event + end +end + +xi.brigandsChart.jadeEtuiOnTrigger = function(player, npc) + -- TODO: Distribute rewards +end diff --git a/scripts/zones/Buburimu_Peninsula/DefaultActions.lua b/scripts/zones/Buburimu_Peninsula/DefaultActions.lua index 3c6f6486602..7891b5f8495 100644 --- a/scripts/zones/Buburimu_Peninsula/DefaultActions.lua +++ b/scripts/zones/Buburimu_Peninsula/DefaultActions.lua @@ -2,6 +2,7 @@ local ID = zones[xi.zone.BUBURIMU_PENINSULA] return { ['Beastmens_Banner'] = { messageSpecial = ID.text.BEASTMEN_BANNER }, + ['qm1'] = { messageSpecial = ID.text.SHIP_SANK_NEAR_HERE }, ['Song_Runes'] = { messageSpecial = ID.text.SONG_RUNES_DEFAULT }, ['Stone_Monument'] = { event = 900 }, } diff --git a/scripts/zones/Buburimu_Peninsula/IDs.lua b/scripts/zones/Buburimu_Peninsula/IDs.lua index 547abcdf6d5..e830221b02c 100644 --- a/scripts/zones/Buburimu_Peninsula/IDs.lua +++ b/scripts/zones/Buburimu_Peninsula/IDs.lua @@ -37,6 +37,15 @@ zones[xi.zone.BUBURIMU_PENINSULA] = LOGGING_IS_POSSIBLE_HERE = 7415, -- Logging is possible here if you have . CONQUEST = 7431, -- You've earned conquest points! GARRISON_BASE = 7801, -- Hm? What is this? %? How do I know this is not some [San d'Orian/Bastokan/Windurstian] trick? + SHIP_SANK_NEAR_HERE = 7844, -- It seems that long ago, a ship sank near here. If you find any vestige of the wreck and return it to the sea, perhaps it would appease the spirits of the dead. + RETURN_TO_SEA = 7845, -- You return the to the sea. + MY_ITEM = 7851, -- My ... + WHAT_CAN_I_DO = 7852, -- What can I do...? + WORKED_SO_HARD = 7853, -- I worked so hard to get it... + MUST_HAVE_IT_BACK = 7854, -- I must have it back... + MAKE_PARENTS_PROUD = 7855, -- I thought I could make my parents proud... Why can't I do such a simple thing!? + IM_FADING = 7856, -- I-I'm...fading... I can't go on much longer... Could this be the end? + IT_CANT_BE_NOO = 7857, -- It can't be... Nooo!!! TIME_ELAPSED = 7894, -- Time elapsed: [hour/hours] (Vana'diel time) [minute/minutes] and [second/seconds] (Earth time) YOU_CANNOT_ENTER_DYNAMIS = 7907, -- You cannot enter Dynamis - [Dummy/San d'Oria/Bastok/Windurst/Jeuno/Beaucedine/Xarcabard/Valkurm/Buburimu/Qufim/Tavnazia] for [day/days] (Vana'diel time). PLAYERS_HAVE_NOT_REACHED_LEVEL = 7909, -- Players who have not reached level are prohibited from entering Dynamis. @@ -58,9 +67,13 @@ zones[xi.zone.BUBURIMU_PENINSULA] = }, npc = { - LOGGING = GetTableOfIDs('Logging_Point'), - OVERSEER_BASE = GetFirstID('Bonbavour_RK'), - SIGNPOST_OFFSET = GetFirstID('Signpost'), + BRIGAND_CHART_HUME = GetFirstID('Brigand_Chart_Hume'), + BRIGAND_CHART_QM = GetFirstID('qm1'), + JADE_ETUI_TABLE = GetTableOfIDs('Jade_Etui'), + LOGGING = GetTableOfIDs('Logging_Point'), + OVERSEER_BASE = GetFirstID('Bonbavour_RK'), + SHIMMERING_POINT = GetFirstID('Shimmering_Point'), + SIGNPOST_OFFSET = GetFirstID('Signpost'), }, } diff --git a/scripts/zones/Buburimu_Peninsula/npcs/Jade_Etui.lua b/scripts/zones/Buburimu_Peninsula/npcs/Jade_Etui.lua new file mode 100644 index 00000000000..8c8454c1001 --- /dev/null +++ b/scripts/zones/Buburimu_Peninsula/npcs/Jade_Etui.lua @@ -0,0 +1,13 @@ +----------------------------------- +-- Area: Buburimu Peninsula +-- NPC: Jade Etui +-- Involved in Brigand's Chart quest +----------------------------------- +---@type TNpcEntity +local entity = {} + +entity.onTrigger = function(player, npc) + return xi.brigandsChart.jadeEtuiOnTrigger(player, npc) +end + +return entity diff --git a/scripts/zones/Buburimu_Peninsula/npcs/qm1.lua b/scripts/zones/Buburimu_Peninsula/npcs/qm1.lua new file mode 100644 index 00000000000..634118791c4 --- /dev/null +++ b/scripts/zones/Buburimu_Peninsula/npcs/qm1.lua @@ -0,0 +1,24 @@ +----------------------------------- +-- Area: Buburimu Peninsula +-- NPC: qm1 (???) +-- Involved in Brigand's Chart quest +-- !pos -87.2 20.4 -336.4 118 +----------------------------------- +---@type TNpcEntity +local entity = {} + +entity.onTrade = function(player, npc, trade) + if xi.settings.map.FISHING_ENABLE then + return xi.brigandsChart.onTrade(player, npc, trade) + end +end + +entity.onEventUpdate = function(player, csid, option, npc) + return xi.brigandsChart.onEventUpdate(player, csid, option, npc) +end + +entity.onEventFinish = function(player, csid, option, npc) + return xi.brigandsChart.onEventFinish(player, csid, option, npc) +end + +return entity diff --git a/sql/npc_list.sql b/sql/npc_list.sql index 05e1a8e47e7..95a70f32679 100644 --- a/sql/npc_list.sql +++ b/sql/npc_list.sql @@ -15204,8 +15204,8 @@ INSERT INTO `npc_list` VALUES (17261184,'Beastmens_Banner','Beastmen\'s Banner', INSERT INTO `npc_list` VALUES (17261185,'Ramblix','Ramblix',36,-608.395,-40.625,166.305,0,40,40,0,0,0,6,27,0x0000550000000000000000000000000000000000,32,NULL,1); INSERT INTO `npc_list` VALUES (17261186,'Goblin_Footprint','Goblin Footprint',36,-608.395,-40.625,166.305,1,40,40,0,0,0,0,3,0x0000340000000000000000000000000000000000,0,NULL,0); INSERT INTO `npc_list` VALUES (17261187,'qm1','???',0,-87.154,20.410,-336.405,32769,40,40,0,0,64,0,3,0x0000340000000000000000000000000000000000,0,NULL,0); -INSERT INTO `npc_list` VALUES (17261188,'csnpc','',68,-87.586,20.089,-333.795,32769,40,40,0,0,200,2,2075,0x01000D0100100B20063003400850006000700000,32,NULL,1); -INSERT INTO `npc_list` VALUES (17261189,'csnpc','',60,-87.968,21.270,-344.693,32769,40,40,0,0,72,2,2075,0x0000380500000000000000000000000000000000,32,NULL,1); +INSERT INTO `npc_list` VALUES (17261188,'Brigand_Chart_Hume','???',68,-87.586,20.089,-333.795,32769,40,40,0,0,200,2,2075,0x01000D0100100B20063003400850006000700000,32,NULL,1); +INSERT INTO `npc_list` VALUES (17261189,'Shimmering_Point','',60,-87.968,21.270,-344.693,32769,40,40,0,0,72,2,2075,0x0000380500000000000000000000000000000000,32,NULL,1); INSERT INTO `npc_list` VALUES (17261190,'npc[7b]','',0,0.000,0.000,0.000,0,40,40,0,0,0,2,3,0x0000320000000000000000000000000000000000,0,NULL,1); INSERT INTO `npc_list` VALUES (17261191,'Hieroglyphics','Hieroglyphics',215,164.238,0.464,-175.149,32769,40,40,0,0,0,0,3,0x0000340000000000000000000000000000000000,0,'COP',1); INSERT INTO `npc_list` VALUES (17261192,'Ulrich','Ulrich',146,-407.333,-43.303,22.128,0,40,40,0,0,0,6,27,0x01000C0100100F20023005400F50006000700000,32,NULL,1);