From b50dc1ba1ad3136e1581233ea6fa09b40d2b0b48 Mon Sep 17 00:00:00 2001 From: Flibe-XI Date: Fri, 27 Mar 2026 11:27:12 -0400 Subject: [PATCH 1/2] Mob fishable for brigands chart --- sql/fishing_area.sql | 1 + sql/fishing_mob.sql | 2 +- sql/mob_spawn_points.sql | 2 +- src/map/utils/fishingutils.cpp | 28 ++++++++++++++++++++++++++-- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/sql/fishing_area.sql b/sql/fishing_area.sql index e7dde53cca2..60625de39a2 100644 --- a/sql/fishing_area.sql +++ b/sql/fishing_area.sql @@ -157,6 +157,7 @@ INSERT INTO `fishing_area` VALUES (116,4,'Other Waterside (rivers)',2,50,0,0x7B1 INSERT INTO `fishing_area` VALUES (116,5,'Lake Tepokalipuka',2,20,0,0x0000B4C30000000000004CC30000B4C300000000000096C2A4707FC300000000000096C233F36BC300000000EC51E6C2523894C200000000F628E7C266E6A2C2000000003D8A2AC37BD478C3000000007B1429C3CD8C83C3000000005CCF46C3,0.000,-1.000,0.000); -- Buburimu Peninsula INSERT INTO `fishing_area` VALUES (118,1,'Whole Zone',0,0,0,NULL,0.000,0.000,0.000); +INSERT INTO `fishing_area` VALUES (118,2,'Brigands Chart Quest',0,0,0,NULL,0.000,0.000,0.000); -- Sarumugue Champaign INSERT INTO `fishing_area` VALUES (120,1,'Whole Zone',0,0,0,NULL,0.000,0.000,0.000); -- The Sanctuary of Zi'Tah diff --git a/sql/fishing_mob.sql b/sql/fishing_mob.sql index 57de46bc56c..059a5293a7d 100644 --- a/sql/fishing_mob.sql +++ b/sql/fishing_mob.sql @@ -229,7 +229,7 @@ INSERT INTO `fishing_mob` VALUES (17260546,'Puffer_Pugil',118,10,1,1,10,16,10,15 INSERT INTO `fishing_mob` VALUES (17260547,'Snipper',118,10,1,1,10,16,10,15,255,255,0,0,0,1000,0,0,0,0,0,0,1); INSERT INTO `fishing_mob` VALUES (17260548,'Shoal_Pugil',118,10,1,1,10,16,10,15,255,255,0,0,0,1000,0,0,0,0,0,0,1); INSERT INTO `fishing_mob` VALUES (17260549,'Clipper',118,10,1,1,10,16,10,15,255,255,0,0,0,1000,0,0,0,0,0,0,0); -INSERT INTO `fishing_mob` VALUES (17261040,'Puffer Pugil',118,1,1,1,1,16,10,15,255,255,0,0,0,1000,0,0,0,0,0,1,0); +INSERT INTO `fishing_mob` VALUES (17261040,'Puffer_Pugil_Brigand',118,10,1,1,10,16,10,15,255,255,0,0,2,1000,0,0,0,0,0,0,0); INSERT INTO `fishing_mob` VALUES (17268737,'Big_Jaw',120,10,1,1,10,16,10,15,255,255,0,0,0,1000,0,0,0,0,0,0,0); INSERT INTO `fishing_mob` VALUES (17268738,'Snipper',120,10,1,1,10,16,10,15,255,255,0,0,0,1000,0,0,0,0,0,0,0); INSERT INTO `fishing_mob` VALUES (17268739,'Greater_Pugil',120,10,1,1,10,16,10,15,255,255,0,0,0,1000,0,0,0,0,0,0,0); diff --git a/sql/mob_spawn_points.sql b/sql/mob_spawn_points.sql index 779b4607b5d..ce4b56f03bd 100644 --- a/sql/mob_spawn_points.sql +++ b/sql/mob_spawn_points.sql @@ -42194,7 +42194,7 @@ INSERT INTO `mob_spawn_points` VALUES (17261032,0,'Goblin_Shaman','Goblin Shaman INSERT INTO `mob_spawn_points` VALUES (17261033,0,'Goblin_Shaman','Goblin Shaman',49,30,35,-463.815,-31.213,59.398,115); INSERT INTO `mob_spawn_points` VALUES (17261034,0,'Goblin_Guide','Goblin Guide',50,35,35,-451.610,-32.269,84.223,120); -INSERT INTO `mob_spawn_points` VALUES (17261040,0,'Puffer_Pugil','Puffer Pugil',2,15,18,-81.741,19.635,-325.773,94); +INSERT INTO `mob_spawn_points` VALUES (17261040,0,'Puffer_Pugil_Brigand','Puffer Pugil',2,15,18,-81.741,19.635,-325.773,94); INSERT INTO `mob_spawn_points` VALUES (17261041,0,'Pixie','Pixie',51,51,54,0.000,0.000,0.000,0); INSERT INTO `mob_spawn_points` VALUES (17261042,0,'Pixie','Pixie',51,51,54,0.000,0.000,0.000,0); INSERT INTO `mob_spawn_points` VALUES (17261043,0,'Pixie','Pixie',51,51,54,0.000,0.000,0.000,0); diff --git a/src/map/utils/fishingutils.cpp b/src/map/utils/fishingutils.cpp index 0add795fad8..b4c71dfe8bd 100644 --- a/src/map/utils/fishingutils.cpp +++ b/src/map/utils/fishingutils.cpp @@ -2430,6 +2430,26 @@ fishresponse_t* FishingCheck(CCharEntity* PChar, uint8 fishingSkill, rod_t* rod, ItemPoolWeight = 0; } + if (PChar->getZone() == ZONE_BUBURIMU_PENINSULA && PChar->GetLocalVar("bChartActive") == 1) + { + MobHookPool.clear(); + + for (auto fishmob : FishZoneMobList[PChar->getZone()]) + { + if (fishmob.second->mobName == "Puffer_Pugil_Brigand") + { + CMobEntity* PMob = dynamic_cast(zoneutils::GetEntity(fishmob.second->mobId, TYPE_MOB)); + if (PMob != nullptr && PMob->GetLocalVar("hooked") == 0 && !PMob->isAlive()) + { + auto* mob = fishmob.second; + MobHookPool.insert(std::make_pair(mob, 100)); + } + + break; + } + } + } + // Select mob if (!MobHookPool.empty()) { @@ -2447,7 +2467,7 @@ fishresponse_t* FishingCheck(CCharEntity* PChar, uint8 fishingSkill, rod_t* rod, if (!ChestPool.empty()) { // Brigand's Chart Quest - if (PChar->GetLocalVar("bChartActive") == 1) + if (PChar->getZone() == ZONE_BUBURIMU_PENINSULA && PChar->GetLocalVar("bChartActive") == 1) { for (uint32 chestId : ChestPool) { @@ -2709,10 +2729,14 @@ void FishingAction(CCharEntity* PChar, const GP_CLI_COMMAND_FISHING_2_MODE mode, fishingarea_t* fishingArea = GetFishingArea(PChar); fishresponse_t* response = nullptr; - if (PChar->GetLocalVar("pChartActive") == 1 && PChar->getZone() == ZONE_VALKURM_DUNES) + if (PChar->getZone() == ZONE_VALKURM_DUNES && PChar->GetLocalVar("pChartActive") == 1) { fishingArea = FishingAreaList[ZONE_VALKURM_DUNES][2]; } + else if (PChar->getZone() == ZONE_BUBURIMU_PENINSULA && PChar->GetLocalVar("bChartActive") == 1) + { + fishingArea = FishingAreaList[ZONE_BUBURIMU_PENINSULA][2]; + } if (PChar->hookedFish != nullptr) { From 87679ec73c2f31c380c9ac61910e0ad710bb9168 Mon Sep 17 00:00:00 2001 From: Flibe-XI Date: Fri, 27 Mar 2026 11:29:03 -0400 Subject: [PATCH 2/2] Add rewards to brigands chart --- scripts/globals/brigands_chart.lua | 154 +++++++++++++++--- .../mobs/Puffer_Pugil_Brigand.lua | 13 ++ 2 files changed, 141 insertions(+), 26 deletions(-) create mode 100644 scripts/zones/Buburimu_Peninsula/mobs/Puffer_Pugil_Brigand.lua diff --git a/scripts/globals/brigands_chart.lua b/scripts/globals/brigands_chart.lua index 344d20ad5f5..b746a4749e3 100644 --- a/scripts/globals/brigands_chart.lua +++ b/scripts/globals/brigands_chart.lua @@ -10,21 +10,35 @@ local ID = zones[xi.zone.BUBURIMU_PENINSULA] xi = xi or {} xi.brigandsChart = xi.brigandsChart or {} +local function removeChest(npc) + npc:setAnimationSub(0, false) + npc:setStatus(xi.status.DISAPPEAR) + npc:resetLocalVars() +end + +local function clearChests() + for _, chestId in pairs(ID.npc.JADE_ETUI_TABLE) do + local chest = GetNPCByID(chestId) + if chest then + removeChest(chest) + end + end +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')) - if player then + if player and player:getLocalVar('bChartActive') == 1 then + player:setLocalVar('bChartActive', 0) player:delStatusEffect(xi.effect.LEVEL_RESTRICTION) player:changeMusic(0, 0) player:changeMusic(1, 0) player:changeMusic(2, 101) player:changeMusic(3, 102) - player:setLocalVar('bChartActive', 0) end qm1:resetLocalVars() @@ -42,18 +56,7 @@ local function resetEvent() 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 + clearChests() end local eventTable = @@ -67,7 +70,7 @@ local eventTable = [6] = { time = 180, text = ID.text.WHAT_CAN_I_DO + 5 }, } -local function emoteChecking(npc, spawner, timeRemaining, timeOfLastCheck, phase) +local function emoteChecking(npc, timeRemaining, timeOfLastCheck, phase) -- Event continues if player leaves zone -- https://www.youtube.com/watch?v=_opqVW-HIu0 -- https://discord.com/channels/443544205206355968/446401624102010901/650072608922009660 @@ -78,9 +81,9 @@ local function emoteChecking(npc, spawner, timeRemaining, timeOfLastCheck, phase -- Check time. Show text and move phase if enough time has passed. if totalTimeElapsed > eventTable[phase].time then - local qm1 = GetNPCByID(ID.npc.BRIGAND_CHART_QM) - if qm1 then - spawner:showText(qm1, eventTable[phase].text) + local spawner = GetPlayerByID(npc:getLocalVar('bChartSpawnerID')) + if spawner then + spawner:showText(npc, eventTable[phase].text) end phase = phase + 1 @@ -88,7 +91,7 @@ local function emoteChecking(npc, spawner, timeRemaining, timeOfLastCheck, phase if newTimeRemaining > 0 then npc:timer(1000, function(npcArg) - emoteChecking(npcArg, spawner, newTimeRemaining, currentTime, phase) + emoteChecking(npcArg, newTimeRemaining, currentTime, phase) end) else resetEvent() @@ -96,24 +99,24 @@ local function emoteChecking(npc, spawner, timeRemaining, timeOfLastCheck, phase end xi.brigandsChart.onTrade = function(player, npc, trade) - --[[ if npc:getStatus() == xi.status.NORMAL and + npc:getLocalVar('bChartSpawnerID') == 0 and npcUtil.tradeHasExactly(trade, xi.item.BRIGANDS_CHART) 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() + clearChests() npc:setLocalVar('bChartSpawnerID', player:getID()) - player:setLocalVar('bChartActive', 1) + player:setLocalVar('bChartActive', 1) player:changeMusic(0, 136) player:changeMusic(1, 136) player:changeMusic(2, 136) @@ -148,11 +151,110 @@ xi.brigandsChart.onEventFinish = function(player, csid, option, npc) player:showText(npc, ID.text.MY_ITEM, xi.item.PENGUIN_RING) -- Events will occur for the next 180 seconds according to eventTable - emoteChecking(npc, player, 180, GetSystemTime(), 1) - -- TODO: add fishing hook to catch chests & monster specific to event + emoteChecking(npc, 180, GetSystemTime(), 1) end end +xi.brigandsChart.rewards = +{ + common = + { + { + { itemId = xi.item.BEASTCOIN, weight = xi.loot.weight.NORMAL }, + { itemId = xi.item.BLUE_PITCHER, weight = xi.loot.weight.NORMAL }, + { itemId = xi.item.COPY_OF_LINES_AND_SPACE, weight = xi.loot.weight.VERY_LOW }, + { itemId = xi.item.DWARF_PUGIL, weight = xi.loot.weight.NORMAL }, + { itemId = xi.item.GOLD_BEASTCOIN, weight = xi.loot.weight.LOW }, + { itemId = xi.item.MYTHRIL_BEASTCOIN, weight = xi.loot.weight.LOW }, + { itemId = xi.item.MYTHRIL_SWORD, weight = xi.loot.weight.LOW }, + { itemId = xi.item.ORDELLE_BRONZEPIECE, weight = xi.loot.weight.LOW }, + { itemId = xi.item.ONE_BYNE_BILL, weight = xi.loot.weight.VERY_LOW }, + { itemId = xi.item.PLATINUM_BEASTCOIN, weight = xi.loot.weight.VERY_LOW }, + { itemId = xi.item.RUSTY_CAP, weight = xi.loot.weight.LOW }, + { itemId = xi.item.RUSTY_LEGGINGS, weight = xi.loot.weight.NORMAL }, + { itemId = xi.item.SILVER_BEASTCOIN, weight = xi.loot.weight.NORMAL }, + { itemId = xi.item.SKY_POT, weight = xi.loot.weight.LOW }, + { itemId = xi.item.WOODEN_FLOWERPOT, weight = xi.loot.weight.VERY_LOW }, + }, + }, + + special = + { + { + { itemId = xi.item.NONE, weight = xi.loot.weight.NORMAL }, + { itemId = xi.item.PENGUIN_RING, weight = xi.loot.weight.NORMAL }, + }, + }, +} + xi.brigandsChart.jadeEtuiOnTrigger = function(player, npc) - -- TODO: Distribute rewards + local qm1 = GetNPCByID(ID.npc.BRIGAND_CHART_QM) + if not qm1 then + return + end + + local spawnerID = qm1:getLocalVar('bChartSpawnerID') + if spawnerID == 0 then + -- Event has been reset since this chest spawned, remove it + removeChest(npc) + return + end + + if + player:getID() ~= spawnerID or + npc:getLocalVar(xi.animationString.OPEN_CRATE_GLOW) ~= 0 + then + return + end + + npc:entityAnimationPacket(xi.animationString.OPEN_CRATE_GLOW) + npc:setLocalVar(xi.animationString.OPEN_CRATE_GLOW, 1) + + local chestNumber = player:getLocalVar('bChartChestNum') + local rewardTable = {} + local penguinFound = false + + -- Fourth or fifth chest, chance to give penguin ring + if chestNumber >= 3 then + local specialReward = utils.selectFromLootGroups(player, xi.brigandsChart.rewards.special)[1] + if + specialReward and + specialReward.itemId == xi.item.PENGUIN_RING + then + table.insert(rewardTable, specialReward.itemId) + table.insert(rewardTable, xi.item.YELLOW_GLOBE) + table.insert(rewardTable, xi.item.YELLOW_GLOBE) + table.insert(rewardTable, xi.item.YELLOW_GLOBE) + penguinFound = true + end + end + + if #rewardTable == 0 then + local commonReward = utils.selectFromLootGroups(player, xi.brigandsChart.rewards.common)[1] + table.insert(rewardTable, commonReward.itemId) + end + + for _, reward in pairs(rewardTable) do + if reward ~= nil then + player:addTreasure(reward, npc) + end + end + + if + penguinFound or + chestNumber > 4 + then + -- Event automatically ends once the ring has been found + resetEvent() + else + player:setLocalVar('bChartChestNum', chestNumber + 1) + + npc:timer(15000, function(npcArg) + npcArg:entityAnimationPacket(xi.animationString.STATUS_DISAPPEAR) + end) + + npc:timer(16000, function(npcArg) + removeChest(npcArg) + end) + end end diff --git a/scripts/zones/Buburimu_Peninsula/mobs/Puffer_Pugil_Brigand.lua b/scripts/zones/Buburimu_Peninsula/mobs/Puffer_Pugil_Brigand.lua new file mode 100644 index 00000000000..bb3189cb19d --- /dev/null +++ b/scripts/zones/Buburimu_Peninsula/mobs/Puffer_Pugil_Brigand.lua @@ -0,0 +1,13 @@ +----------------------------------- +-- Area: Buburimu Peninsula +-- Mob: Puffer Pugil +-- The one mob able to be caught during Brigand's Chart quest +----------------------------------- +---@type TMobEntity +local entity = {} + +entity.onMobSpawn = function(mob) + mob:setMobMod(xi.mobMod.IDLE_DESPAWN, 180) +end + +return entity