From c1b3191ed5deeb281b1dc77f385b40a5003467ff Mon Sep 17 00:00:00 2001 From: Xaver-DaRed Date: Sat, 14 Feb 2026 22:23:26 +0100 Subject: [PATCH] Track which AF pieces from coffers have been obtained --- scripts/globals/treasure.lua | 34 +++++--- scripts/quests/jeuno/helpers.lua | 31 ++++--- scripts/zones/Lower_Jeuno/npcs/Vingijard.lua | 89 ++++++++++++++------ 3 files changed, 107 insertions(+), 47 deletions(-) diff --git a/scripts/globals/treasure.lua b/scripts/globals/treasure.lua index 9278961fcd9..9eb1e52eddc 100644 --- a/scripts/globals/treasure.lua +++ b/scripts/globals/treasure.lua @@ -1734,13 +1734,13 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) -- Early return: Distance check. if player:checkDistance(npc) > 2 then player:messageSpecial(ID.text.CHEST_UNLOCKED - 5) - return + return 0 end -- Early return: Can't lockpick while weakened. if player:hasStatusEffect(xi.effect.WEAKNESS) then player:messageSpecial(ID.text.CHEST_UNLOCKED + 3) - return + return 0 end -- Early return: Treasure is already open. @@ -1749,7 +1749,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) npc:getLocalVar('traded') ~= 0 then player:messageSpecial(ID.text.CHEST_UNLOCKED - 7) - return + return 0 end ----------------------------------- @@ -1779,7 +1779,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) trade:getItemCount() ~= 1 then player:messageSpecial(ID.text.CHEST_UNLOCKED + 7, treasureKey) - return + return 0 end ----------------------------------- @@ -1810,7 +1810,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) playerEntity:setFreezeFlag(false) end) - return + return 0 end -- It's a trap! @@ -1832,7 +1832,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) playerEntity:setFreezeFlag(false) end) - return + return 0 end -- Mimic (Coffers only) @@ -1846,7 +1846,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) if not mimic then playerEntity:messageName(ID.text.CHEST_UNLOCKED + 1, playerEntity) npc:setLocalVar('traded', 0) - return + return 0 end mimic:setSpawn(npc:getXPos(), npc:getYPos(), npc:getZPos(), npc:getRotPos()) @@ -1856,7 +1856,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) playerEntity:setFreezeFlag(false) end) - return + return 0 end ----------------------------------- @@ -1866,7 +1866,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) -- Early return: Player has no room for items. if player:getFreeSlotsCount() == 0 then player:messageSpecial(ID.text.CHEST_UNLOCKED - 6) - return + return 0 end kneelBeforeChest(player, npc) @@ -1884,7 +1884,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) end) npc:setLocalVar('traded', 0) - return + return bypassReward ----------------------------------- -- Handle quest Key Item reward. @@ -1903,7 +1903,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) playerEntity:setFreezeFlag(false) end) - return + return bypassReward end ----------------------------------- @@ -1926,7 +1926,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) playerEntity:setFreezeFlag(false) end) - return + return treasureMap end ----------------------------------- @@ -1938,7 +1938,7 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) moveTreasure(npc, respawnType.REGULAR) end) - return + return 0 end ----------------------------------- @@ -1955,6 +1955,8 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) end end + local reward = 0 + -- Gil if itemId == xi.item.NONE then -- Distribute gil. @@ -1971,6 +1973,8 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) playerEntity:setFreezeFlag(false) end) + reward = xi.item.GIL + -- Items (Gems or others) else kneelBeforeChest(player, npc) @@ -1985,6 +1989,8 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) player:timer(4000, function(playerEntity) playerEntity:setFreezeFlag(false) end) + + reward = itemId end -- Handle illusion timers. @@ -1993,6 +1999,8 @@ xi.treasure.onTrade = function(player, npc, trade, bypassType, bypassReward) else npc:setLocalVar('illusionCooldown', GetSystemTime() + math.random(xi.settings.main.COFFER_MIN_ILLUSION_TIME, xi.settings.main.COFFER_MAX_ILLUSION_TIME)) end + + return reward end xi.treasure.onTrigger = function(player, npc) diff --git a/scripts/quests/jeuno/helpers.lua b/scripts/quests/jeuno/helpers.lua index ddcd7a41da7..be08c62dac5 100644 --- a/scripts/quests/jeuno/helpers.lua +++ b/scripts/quests/jeuno/helpers.lua @@ -612,8 +612,9 @@ function xi.jeuno.helpers.BorghertzQuests:new(params) { check = function(player, status, vars) - return status ~= xi.questStatus.QUEST_AVAILABLE and - player:getMainJob() == params.requiredJobId + return status ~= xi.questStatus.QUEST_AVAILABLE and -- Quest must be already started or completed. + player:getMainJob() == params.requiredJobId and -- Player must be on the appropiate job. + not utils.mask.getBit(player:getCharVar('[AF]ZilartCoffer'), params.requiredJobId * 2 - 1) end, [params.optionalZoneId1] = @@ -621,25 +622,35 @@ function xi.jeuno.helpers.BorghertzQuests:new(params) ['Treasure_Coffer'] = { onTrade = function(player, npc, trade) - if not player:hasItem(params.optionalArtifact1) then - xi.treasure.onTrade(player, npc, trade, 1, params.optionalArtifact1) - - return quest:noAction() + local result = xi.treasure.onTrade(player, npc, trade, 1, params.optionalArtifact1) + if result == params.optionalArtifact1 then + player:incrementCharVar('[AF]ZilartCoffer', bit.lshift(1, params.requiredJobId * 2 - 1)) end + + return quest:noAction() end, }, }, + }, + + { + check = function(player, status, vars) + return status ~= xi.questStatus.QUEST_AVAILABLE and + player:getMainJob() == params.requiredJobId and + not utils.mask.getBit(player:getCharVar('[AF]ZilartCoffer'), params.requiredJobId * 2) + end, [params.optionalZoneId2] = { ['Treasure_Coffer'] = { onTrade = function(player, npc, trade) - if not player:hasItem(params.optionalArtifact2) then - xi.treasure.onTrade(player, npc, trade, 1, params.optionalArtifact2) - - return quest:noAction() + local result = xi.treasure.onTrade(player, npc, trade, 1, params.optionalArtifact2) + if result == params.optionalArtifact2 then + player:incrementCharVar('[AF]ZilartCoffer', bit.lshift(1, params.requiredJobId * 2)) end + + return quest:noAction() end, }, }, diff --git a/scripts/zones/Lower_Jeuno/npcs/Vingijard.lua b/scripts/zones/Lower_Jeuno/npcs/Vingijard.lua index 60367b7fde1..9826b73f5c8 100644 --- a/scripts/zones/Lower_Jeuno/npcs/Vingijard.lua +++ b/scripts/zones/Lower_Jeuno/npcs/Vingijard.lua @@ -7,9 +7,9 @@ local entity = {} local jobGearTable = { - -- [job ID] = { Weapon, Head, Body, Hands, Legs, Feet }, - -- [xi.job.WAR] = { xi.item.RAZOR_AXE, xi.item.FIGHTERS_MASK, xi.item.FIGHTERS_LORICA, xi.item.FIGHTERS_MUFFLERS, xi.item.FIGHTERS_CUISSES, xi.item.FIGHTERS_CALLIGAE }, - -- [xi.job.MNK] = { xi.item.BEAT_CESTI, xi.item.TEMPLE_CROWN, xi.item.TEMPLE_CYCLAS, xi.item.TEMPLE_GLOVES, xi.item.TEMPLE_HOSE, xi.item.TEMPLE_GAITERS }, + -- [job ID] = { Weapon, Head, Body, Hands, Legs, Feet }, + [xi.job.WAR] = { xi.item.RAZOR_AXE, xi.item.FIGHTERS_MASK, xi.item.FIGHTERS_LORICA, xi.item.FIGHTERS_MUFFLERS, xi.item.FIGHTERS_CUISSES, xi.item.FIGHTERS_CALLIGAE }, + [xi.job.MNK] = { xi.item.BEAT_CESTI, xi.item.TEMPLE_CROWN, xi.item.TEMPLE_CYCLAS, xi.item.TEMPLE_GLOVES, xi.item.TEMPLE_HOSE, xi.item.TEMPLE_GAITERS }, -- [xi.job.WHM] = { }, -- [xi.job.BLM] = { }, -- [xi.job.RDM] = { }, @@ -178,14 +178,33 @@ local jobQuestsTable = }, } +local jobCofferTable = +{ + [xi.job.WAR] = { '[AF]ZilartCoffer', 1, 2 }, + [xi.job.MNK] = { '[AF]ZilartCoffer', 3, 4 }, + [xi.job.WHM] = { '[AF]ZilartCoffer', 5, 6 }, + [xi.job.BLM] = { '[AF]ZilartCoffer', 7, 8 }, + [xi.job.RDM] = { '[AF]ZilartCoffer', 9, 10 }, + [xi.job.THF] = { '[AF]ZilartCoffer', 11, 12 }, + [xi.job.PLD] = { '[AF]ZilartCoffer', 13, 14 }, + [xi.job.DRK] = { '[AF]ZilartCoffer', 15, 16 }, + [xi.job.BST] = { '[AF]ZilartCoffer', 17, 18 }, + [xi.job.BRD] = { '[AF]ZilartCoffer', 19, 20 }, + [xi.job.RNG] = { '[AF]ZilartCoffer', 21, 22 }, + [xi.job.SAM] = { '[AF]ZilartCoffer', 23, 24 }, + [xi.job.NIN] = { '[AF]ZilartCoffer', 25, 26 }, + [xi.job.DRG] = { '[AF]ZilartCoffer', 27, 28 }, + [xi.job.SMN] = { '[AF]ZilartCoffer', 29, 30 }, +} + local function isJobResetingEligible(player, jobId) -- Check for gear table job entry existence. - if jobGearTable[jobId] == nil then + if not jobGearTable[jobId] then return false end -- Check for quest table job entry existence. - if jobQuestsTable[jobId] == nil then + if not jobQuestsTable[jobId] then return false end @@ -196,6 +215,16 @@ local function isJobResetingEligible(player, jobId) end end + -- Check if Job has AF pieces from chest and if player has gotten them. + if jobCofferTable[jobId] then + if + not utils.mask.getBit(player:getCharVar(jobCofferTable[jobId][1]), jobCofferTable[jobId][2]) or + not utils.mask.getBit(player:getCharVar(jobCofferTable[jobId][1]), jobCofferTable[jobId][3]) + then + return false + end + end + -- Check if the player has completed all job AF quests. for questEntry = 1, #jobQuestsTable[jobId] do if not player:hasCompletedQuest(jobQuestsTable[jobId][questEntry][1], jobQuestsTable[jobId][questEntry][2]) then @@ -206,18 +235,6 @@ local function isJobResetingEligible(player, jobId) return true end -local function performJobResetting(player, jobId) - -- Safety check. - if not isJobResetingEligible(player, jobId) then - return - end - - -- Delete all quests. - for questEntry = 1, #jobQuestsTable[jobId] do - player:delQuest(jobQuestsTable[jobId][questEntry][1], jobQuestsTable[jobId][questEntry][2]) - end -end - entity.onTrigger = function(player, npc) local currentGil = player:getGil() local optionBitmask = 8388606 -- 23 bits. 1st = false. 2nd to 23th = true. @@ -233,13 +250,37 @@ entity.onTrigger = function(player, npc) end entity.onEventFinish = function(player, csid, option, npc) - if - csid == 10034 and - option >= xi.job.WAR and - option <= xi.job.RUN and - player:getGil() >= 10000 -- Safety check. - then - performJobResetting(player, option) + if csid ~= 10034 then + return + end + + if option < xi.job.WAR or option > xi.job.RUN then + return + end + + if not isJobResetingEligible(player, option) then + return + end + + if player:getGil() < 10000 then + return + end + + -- Delete gil. + player:delGil(10000) + + -- Delete all quests. + for questEntry = 1, #jobQuestsTable[option] do + player:delQuest(jobQuestsTable[option][questEntry][1], jobQuestsTable[option][questEntry][2]) + end + + -- Delete coffer var. + if jobCofferTable[option] then + local afBitmaskName = jobCofferTable[option][1] + local bitOneValue = 2 ^ jobCofferTable[option][2] + local bitTwoValue = 2 ^ jobCofferTable[option][3] + + player:setCharVar(afBitmaskName, player:getCharVar(afBitmaskName) - bitOneValue - bitTwoValue) end end