From 6ecd7d73e34b7428ec236d9d4fcaf1aa905085fe Mon Sep 17 00:00:00 2001 From: Skold <113406182+Skold177@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:46:26 -0400 Subject: [PATCH] [lua] ENM Pulling the Strings framework Implements the framework/loot pools for the ENM Pulling the Strings --- .../Mine_Shaft_2716/pulling_the_strings.lua | 190 ++++++++++++++++++ scripts/enum/item.lua | 1 + scripts/zones/Mine_Shaft_2716/IDs.lua | 15 +- 3 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 scripts/battlefields/Mine_Shaft_2716/pulling_the_strings.lua diff --git a/scripts/battlefields/Mine_Shaft_2716/pulling_the_strings.lua b/scripts/battlefields/Mine_Shaft_2716/pulling_the_strings.lua new file mode 100644 index 00000000000..82eb0833816 --- /dev/null +++ b/scripts/battlefields/Mine_Shaft_2716/pulling_the_strings.lua @@ -0,0 +1,190 @@ +----------------------------------- +-- Pulling the Strings +-- Level 60 ENM +-- !addkeyitem SHAFT_GATE_OPERATING_DIAL +----------------------------------- +local mineshaftID = zones[xi.zone.MINE_SHAFT_2716] +----------------------------------- + +local content = Battlefield:new({ + zoneId = xi.zone.MINE_SHAFT_2716, + battlefieldId = xi.battlefield.id.PULLING_THE_STRINGS, + maxPlayers = 1, + levelCap = 60, + allowSubjob = false, + experimental = true, + timeLimit = utils.minutes(15), + index = 3, + entryNpc = '_0d0', + exitNpcs = { '_0d1', '_0d2', '_0d3' }, + requiredKeyItems = { xi.ki.SHAFT_GATE_OPERATING_DIAL, message = mineshaftID.text.SNAPS_IN_TWO, }, + grantXP = 2000, + armouryCrates = + { + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 1, + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 8, + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 15, + }, +}) + +----------------------------------- +-- Registration Gate - DNC, SCH, GEO and RUN cannot run this ENM +----------------------------------- +function content:onEntryEventUpdate(player, csid, option, npc) + if not player:hasStatusEffect(xi.effect.BATTLEFIELD) then + local job = player:getMainJob() + + if job >= xi.job.DNC then + player:updateEvent(xi.battlefield.returnCode.REQS_NOT_MET) + player:setLocalVar('noPosUpdate', 1) + return 0 + end + end + + return Battlefield.onEntryEventUpdate(self, player, csid, option, npc) +end + +----------------------------------- +-- Initialize Battlefield +----------------------------------- +function content:onBattlefieldInitialize(battlefield) + -- We store the initiators job to determine which type of fantoccini model to use and to determine the type of loot comes from the armoury crate. + local initiatorJob = GetPlayerByID(battlefield:getInitiator()):getMainJob() or xi.job.WAR + + battlefield:setLocalVar('initiatorJob', initiatorJob) + + Battlefield.onBattlefieldInitialize(self, battlefield) +end + +----------------------------------- +-- Battlefield Groups +----------------------------------- +content.groups = +{ + { + mobIds = + { + { + mineshaftID.mob.MOBLIN_FANTOCCINIMAN, + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 2, + }, + + { + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 7, + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 9, + }, + + { + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 14, + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 16, + }, + }, + allDeath = utils.bind(content.handleAllMonstersDefeated, content), + }, + + { + mobIds = + { + { + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 3, -- Funguar, Lizard + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 4, -- Wyvern + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 5, -- Avatar + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 6, -- Automaton + }, + + { + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 10, -- Funguar, Lizard + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 11, -- Wyvern + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 12, -- Avatar + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 13, -- Automaton + }, + + { + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 17, -- Funguar, Lizard + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 18, -- Wyvern + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 19, -- Avatar + mineshaftID.mob.MOBLIN_FANTOCCINIMAN + 20, -- Automaton + }, + }, + spawned = false, + }, +} + +----------------------------------- +-- Loot Tables +----------------------------------- +local lootTables = +{ + [ 1] = xi.item.JANIZARY_EARRING, + [ 2] = xi.item.COUNTER_EARRING, + [ 3] = xi.item.HEALING_FEATHER, + [ 4] = xi.item.SPIRIT_LANTERN, + [ 5] = xi.item.SANATION_RING, + [ 6] = xi.item.ASSASSINS_RING, + [ 7] = xi.item.VIAL_OF_REFRESH_MUSK, + [ 8] = xi.item.TACTICAL_RING, + [ 9] = xi.item.PACIFIST_RING, + [10] = xi.item.GETSUL_RING, + [11] = xi.item.DEADEYE_EARRING, + [12] = xi.item.GAMUSHARA_EARRING, + [13] = xi.item.NARUKO_EARRING, + [14] = xi.item.BAG_OF_WYVERN_FEED, + [15] = xi.item.ASTRAL_POT, + [16] = xi.item.DEATH_CHAKRAM, + [17] = xi.item.CORSAIR_BULLET_POUCH, + [18] = { xi.item.ATTUNER, xi.item.TACTICAL_PROCESSOR, xi.item.DRUM_MAGAZINE, xi.item.EQUALIZER, xi.item.TARGET_MARKER, xi.item.MANA_CHANNELER, xi.item.ERASER, xi.item.SMOKE_SCREEN }, +} + +local function getLootPool(battlefield) + -- Get loot table based on job. PUP gets a chance to drop an additional attachment. + local key = battlefield:getLocalVar('initiatorJob') + local lootPool = lootTables[key] + local bonusLootPool = nil + + if type(lootPool) == 'table' then + lootPool = utils.randomEntry(lootPool) + -- 20 percent chance to drop an additional attachment if the initiator is a PUP. + if key == xi.job.PUP and math.random(100) <= 20 then + local bonusAttachment = lootTables[key] + if type(bonusAttachment) == 'table' then + bonusLootPool = utils.randomEntry(bonusAttachment) + end + end + end + + -- Build loot table. + local lootTable = + { + { + { itemId = xi.item.SACK_OF_LITTLE_WORM_MULCH, weight = 10000 }, + }, + + { + { itemId = xi.item.NONE, weight = 5000 }, + { itemId = lootPool, weight = 5000 }, + }, + } + + if bonusLootPool then + table.insert(lootTable, + { + { itemId = bonusLootPool, weight = 10000 }, + }) + end + + return lootTable or content.loot +end + +function content:handleOpenArmouryCrate(player, npc) + npcUtil.openCrate(npc, function() + local battlefield = player:getBattlefield() + local lootPool = getLootPool(battlefield) + + self:handleLootRolls(battlefield, lootPool, npc, player:getMod(xi.mod.MOGHANCEMENT_GIL_BONUS_P)) + battlefield:setStatus(xi.battlefield.status.WON) + + return true + end) +end + +return content:register() diff --git a/scripts/enum/item.lua b/scripts/enum/item.lua index b54806cac78..e4623aff679 100644 --- a/scripts/enum/item.lua +++ b/scripts/enum/item.lua @@ -1219,6 +1219,7 @@ xi.item = SPOOL_OF_RED_GRASS_THREAD = 1828, SQUARE_OF_RED_GRASS_CLOTH = 1829, SACK_OF_LUGWORM_SAND = 1830, + SACK_OF_LITTLE_WORM_MULCH = 1831, BROKEN_HUME_FISHING_ROD = 1832, BROKEN_HALCYON_FISHING_ROD = 1833, SABER_SHOOT = 1837, diff --git a/scripts/zones/Mine_Shaft_2716/IDs.lua b/scripts/zones/Mine_Shaft_2716/IDs.lua index a9b106b87c5..b3b801b2acb 100644 --- a/scripts/zones/Mine_Shaft_2716/IDs.lua +++ b/scripts/zones/Mine_Shaft_2716/IDs.lua @@ -35,13 +35,14 @@ zones[xi.zone.MINE_SHAFT_2716] = }, mob = { - BUGBBY = GetFirstID('Bugbby'), - BUGBOY = GetFirstID('Bugboy'), - CHEKOCHUK = GetFirstID('Chekochuk'), - MOVAMUQ = GetFirstID('Movamuq'), - SWIPOSTIK = GetFirstID('Swipostik'), - TRIKOTRAK = GetFirstID('Trikotrak'), - HUME_AUTOMATON = GetFirstID('Hume_Automaton'), + BUGBBY = GetFirstID('Bugbby'), + BUGBOY = GetFirstID('Bugboy'), + CHEKOCHUK = GetFirstID('Chekochuk'), + HUME_AUTOMATON = GetFirstID('Hume_Automaton'), + MOBLIN_FANTOCCINIMAN = GetFirstID('Moblin_Fantocciniman'), + MOVAMUQ = GetFirstID('Movamuq'), + SWIPOSTIK = GetFirstID('Swipostik'), + TRIKOTRAK = GetFirstID('Trikotrak'), }, npc = {