From a3b72d46e627241c89c1e7be02fd2145c824573b Mon Sep 17 00:00:00 2001 From: sruon Date: Fri, 13 Feb 2026 00:00:31 -0700 Subject: [PATCH 1/2] Jeuno Quest: Unlisted Qualities Adventuring Fellows unlock quest 1/3 --- scripts/globals/quests.lua | 2 +- scripts/quests/jeuno/Unlisted_Qualities.lua | 251 +++++++++++++++++++ scripts/zones/Port_Jeuno/DefaultActions.lua | 2 +- scripts/zones/Upper_Jeuno/DefaultActions.lua | 1 + scripts/zones/Upper_Jeuno/npcs/Bheem.lua | 23 -- 5 files changed, 254 insertions(+), 25 deletions(-) create mode 100644 scripts/quests/jeuno/Unlisted_Qualities.lua delete mode 100644 scripts/zones/Upper_Jeuno/npcs/Bheem.lua diff --git a/scripts/globals/quests.lua b/scripts/globals/quests.lua index 8efa44c63ea..1aa616c841d 100644 --- a/scripts/globals/quests.lua +++ b/scripts/globals/quests.lua @@ -397,7 +397,7 @@ xi.quest.id = THE_GOBBIEBAG_PART_V = 74, -- + Converted THE_GOBBIEBAG_PART_VI = 75, -- + Converted BEYOND_THE_SUN = 76, -- + Converted - UNLISTED_QUALITIES = 77, + UNLISTED_QUALITIES = 77, -- + Converted GIRL_IN_THE_LOOKING_GLASS = 78, MIRROR_MIRROR = 79, -- + PAST_REFLECTIONS = 80, diff --git a/scripts/quests/jeuno/Unlisted_Qualities.lua b/scripts/quests/jeuno/Unlisted_Qualities.lua new file mode 100644 index 00000000000..3e3e1318b49 --- /dev/null +++ b/scripts/quests/jeuno/Unlisted_Qualities.lua @@ -0,0 +1,251 @@ +----------------------------------- +-- Unlisted Qualities +----------------------------------- +-- Log ID: 3, Quest ID: 77 +-- Luto Mewrilah: !pos -52 0 46 244 +-- Red Ghost: !pos -99 0 0 246 +-- Akta: !pos 6 0 -68.6 243 +-- Kuah Dakonsa: !pos -40 0 -65 245 +-- Bheem: !pos -89 0 168 244 +----------------------------------- + +local quest = Quest:new(xi.questLog.JEUNO, xi.quest.id.jeuno.UNLISTED_QUALITIES) + +-- Packs Fellow properties into a single integer, as expected by events. +-- 0000 0000 FFFF SS00 00PP PP00 0RRR NNNN +-- Face, Size, Personality, Race, Name +local function packData(name, race, pers, face, size) + return bit.bor( + name, + bit.lshift(race, 4), + bit.lshift(pers >= 1 and pers or 0, 10), + bit.lshift(size >= 1 and size or 0, 18), + bit.lshift(face >= 0 and face or 15, 20) + ) +end + +local function unpackData(packed) + local rawPers = bit.band(bit.rshift(packed, 10), 0x0F) + local rawSize = bit.band(bit.rshift(packed, 18), 0x03) + local rawFace = bit.band(bit.rshift(packed, 20), 0x0F) + + return + { + name = bit.band(packed, 0x0F), + race = bit.band(bit.rshift(packed, 4), 0x07), + pers = rawPers >= 1 and rawPers or -1, + size = rawSize >= 1 and rawSize or -1, + face = rawFace < 15 and rawFace or -1, + } +end + +-- Check if all 3 NPC info gathered, advance to Bheem phase +local function checkAdvanceToPhase1(player) + local d = unpackData(quest:getVar(player, 'Data')) + if d.pers >= 1 and d.face >= 0 and d.size >= 1 then + quest:setVar(player, 'Prog', 1) + end +end + +quest.sections = +{ + -- Quest start: Talk to Luto, select name/race + { + check = function(player, status, vars) + return status == xi.questStatus.QUEST_AVAILABLE + end, + + [xi.zone.UPPER_JEUNO] = + { + ['Luto_Mewrilah'] = quest:event(10031):importantOnce(), + + onEventFinish = + { + [10031] = function(player, csid, option, npc) + if option == utils.EVENT_CANCELLED_OPTION then + return + end + + -- Parse race and name from option (0-indexed) + local name = bit.band(option, 0x0F) + local race = bit.band(bit.rshift(option, 4), 0x07) + + -- 8 races, 8 names per race. + if race > 7 or name > 7 then + return + end + + quest:begin(player) + quest:setVar(player, 'Data', packData(name, race, -1, -1, -1)) + end, + }, + }, + }, + + -- Prog 0: Gather info from NPCs (size, face, personality) + { + check = function(player, status, vars) + return status == xi.questStatus.QUEST_ACCEPTED and vars.Prog == 0 + end, + + [xi.zone.UPPER_JEUNO] = + { + ['Luto_Mewrilah'] = quest:event(10033), + ['Bheem'] = quest:event(10037):oncePerZone(), + }, + + [xi.zone.RULUDE_GARDENS] = + { + ['Akta'] = + { + onTrigger = function(player, npc) + local data = quest:getVar(player, 'Data') + local d = unpackData(data) + + if d.size == -1 then + return quest:event(10103, { [7] = data }) + end + end, + }, + + onEventFinish = + { + [10103] = function(player, csid, option, npc) + -- 1 = Small, 2 = Average Height, 3 = Pretty Tall + if option < 1 or option > 3 then + return + end + + local d = unpackData(quest:getVar(player, 'Data')) + quest:setVar(player, 'Data', packData(d.name, d.race, d.pers, d.face, option)) + checkAdvanceToPhase1(player) + end, + }, + }, + + [xi.zone.LOWER_JEUNO] = + { + ['Kuah_Dakonsa'] = + { + onTrigger = function(player, npc) + local data = quest:getVar(player, 'Data') + local d = unpackData(data) + + if d.face == -1 then + return quest:event(20000, { [7] = data }) + end + end, + }, + + onEventFinish = + { + [20000] = function(player, csid, option, npc) + -- Event returns 0-15 for faces 1A-8B + if option < 0 or option > 15 then + return + end + + local d = unpackData(quest:getVar(player, 'Data')) + quest:setVar(player, 'Data', packData(d.name, d.race, d.pers, option, d.size)) + checkAdvanceToPhase1(player) + end, + }, + }, + + [xi.zone.PORT_JEUNO] = + { + ['Red_Ghost'] = + { + onTrigger = function(player, npc) + local data = quest:getVar(player, 'Data') + local d = unpackData(data) + + if d.pers == -1 then + return quest:event(320, { [7] = data }) + end + end, + }, + + onEventFinish = + { + [320] = function(player, csid, option, npc) + if option < 0 and option > 11 then + return + end + + local d = unpackData(quest:getVar(player, 'Data')) + -- Race is 0-indexed here (unlike xi.race enum) + local isFemale = (d.race % 2 == 1 and d.race ~= 7) or d.race == 6 + + -- Red Ghost returns 0-5 for male, 6-11 for female + local validMale = option >= 0 and option <= 5 + local validFemale = option >= 6 and option <= 11 + + if (isFemale and validFemale) or (not isFemale and validMale) then + -- This gets stored as +1 as Bheem expects it 1-indexed. + quest:setVar(player, 'Data', packData(d.name, d.race, option + 1, d.face, d.size)) + checkAdvanceToPhase1(player) + end + end, + }, + }, + }, + + -- Prog 1: Talk to Bheem after gathering all info + { + check = function(player, status, vars) + return status == xi.questStatus.QUEST_ACCEPTED and vars.Prog == 1 + end, + + [xi.zone.UPPER_JEUNO] = + { + ['Bheem'] = + { + onTrigger = function(player, npc) + local data = quest:getVar(player, 'Data') + return quest:event(10171, { [7] = data }) + end, + + onEventUpdate = function(player, csid, option, npc) + local data = quest:getVar(player, 'Data') + player:updateEvent({ [7] = data }) + end, + }, + + onEventFinish = + { + [10171] = function(player, csid, option, npc) + if option == utils.EVENT_CANCELLED_OPTION then + return + end + + quest:setVar(player, 'Prog', 2) + end, + }, + }, + }, + + -- Prog 2: Return to Luto Mewrilah to complete + { + check = function(player, status, vars) + return status == xi.questStatus.QUEST_ACCEPTED and vars.Prog == 2 + end, + + [xi.zone.UPPER_JEUNO] = + { + ['Luto_Mewrilah'] = quest:progressEvent(10032), + + -- TODO: Quest completion disabled until core changes made to save Fellows + -- onEventFinish = + -- { + -- [10032] = function(player, csid, option, npc) + -- if option == 0 and npcUtil.giveItem(player, xi.item.SILVER_INGOT) then + -- quest:complete(player) + -- end + -- end, + -- }, + }, + }, +} + +return quest diff --git a/scripts/zones/Port_Jeuno/DefaultActions.lua b/scripts/zones/Port_Jeuno/DefaultActions.lua index 5ab529483a9..a4809344b7e 100644 --- a/scripts/zones/Port_Jeuno/DefaultActions.lua +++ b/scripts/zones/Port_Jeuno/DefaultActions.lua @@ -44,7 +44,7 @@ return { ['qm1'] = { text = ID.text.OLD_BOX }, ['Rachocho'] = { event = 47 }, ['Ramicharaux'] = { event = 60 }, - ['Red_Ghost'] = { event = 320 }, + ['Red_Ghost'] = { event = 34 }, ['Rinzei'] = { event = 56 }, ['Roupatour'] = { event = 54 }, ['Sanosuke'] = { event = 303 }, diff --git a/scripts/zones/Upper_Jeuno/DefaultActions.lua b/scripts/zones/Upper_Jeuno/DefaultActions.lua index 1323475d5f6..ba0b1b01aee 100644 --- a/scripts/zones/Upper_Jeuno/DefaultActions.lua +++ b/scripts/zones/Upper_Jeuno/DefaultActions.lua @@ -9,6 +9,7 @@ return { ['Artush'] = { event = 42 }, ['Balkehr'] = { event = 39 }, ['Baudin'] = { event = 173 }, + ['Bheem'] = { event = 157 }, ['Bozz'] = { event = 35 }, ['Brutus'] = { event = 66 }, ['Chiphraix'] = { event = 153 }, diff --git a/scripts/zones/Upper_Jeuno/npcs/Bheem.lua b/scripts/zones/Upper_Jeuno/npcs/Bheem.lua deleted file mode 100644 index 6b0a6c42c8f..00000000000 --- a/scripts/zones/Upper_Jeuno/npcs/Bheem.lua +++ /dev/null @@ -1,23 +0,0 @@ ------------------------------------ --- Area: Upper Jeuno --- NPC: Bheem --- !pos -90.009 0.0 169.885 ------------------------------------ ----@type TNpcEntity -local entity = {} - -entity.onTrigger = function(player, npc) - if player:getLocalVar('spokenBheem') == 0 then - return player:startEvent(10037) - else - return player:startEvent(157) - end -end - -entity.onEventFinish = function(player, csid, option, npc) - if csid == 10037 then - player:setLocalVar('spokenBheem', 1) - end -end - -return entity From 1fcccb52fefddb8344398f2af2a410b189665009 Mon Sep 17 00:00:00 2001 From: sruon Date: Fri, 13 Feb 2026 20:32:45 -0700 Subject: [PATCH 2/2] Adventuring Fellows names --- src/map/entities/fellowentity.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/map/entities/fellowentity.cpp b/src/map/entities/fellowentity.cpp index 6e833ec2d97..21875f69733 100644 --- a/src/map/entities/fellowentity.cpp +++ b/src/map/entities/fellowentity.cpp @@ -21,6 +21,20 @@ #include "fellowentity.h" +namespace +{ +const std::map> fellowNames = { + { CharRace::HumeMale, { "Feliz", "Ferdinand", "Gunnar", "Massimo", "Oldrich", "Siegward", "Theobald", "Zenji" } }, + { CharRace::HumeFemale, { "Amerita", "Beatrice", "Henrietta", "Jesimae", "Karyn", "Nanako", "Sharlene", "Sieghilde" } }, + { CharRace::ElvaanMale, { "Chanandit", "Deulmaeux", "Demresinaux", "Ephealgaux", "Gauldeval", "Grauffemart", "Migaifongut", "Romidiant" } }, + { CharRace::ElvaanFemale, { "Armittie", "Cadepure", "Clearite", "Epilleve", "Liabelle", "Nauthima", "Radille", "Vimechue" } }, + { CharRace::TarutaruMale, { "Balu-Falu", "Burg-Ladarg", "Ehgo-Ryuhgo", "Kolui-Pelui", "Nokum-Akkum", "Savul-Kivul", "Vinja-Kanja", "Yarga-Umiga" } }, + { CharRace::TarutaruFemale, { "Cupapa", "Jajuju", "Kalokoko", "Mahoyaya", "Pakurara", "Ripokeke", "Yawawa", "Yufafa" } }, + { CharRace::Mithra, { "Fhig Lahrv", "Khuma Tagyawhan", "Pimy Kettihl", "Raka Maimhov", "Sahyu Banjyao", "Sufhi Uchnouma", "Tsuim Nhomango", "Yoli Kohlpaka" } }, + { CharRace::Galka, { "Durib", "Dzapiwa", "Jugowa", "Mugido", "Voldai", "Wagwei", "Zayag", "Zoldof" } }, +}; +} + CFellowEntity::CFellowEntity(CCharEntity* PChar) : CMobEntity() {