From c2bc8d0cc2b62400a21d92ceda6310223b87f7df Mon Sep 17 00:00:00 2001 From: Owills Date: Sun, 28 Sep 2025 22:47:55 -0400 Subject: [PATCH 1/7] Create Prophet Tarot --- objects/consumables/prophet.lua | 290 ++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 objects/consumables/prophet.lua diff --git a/objects/consumables/prophet.lua b/objects/consumables/prophet.lua new file mode 100644 index 00000000..d862a5ee --- /dev/null +++ b/objects/consumables/prophet.lua @@ -0,0 +1,290 @@ +SMODS.Atlas({ + key = "prophet", + path = { + ["default"] = "c_asteroid.png", -- Using asteroid art as requested + ["ru"] = "c_asteroid_ru.png", + }, + px = 71, + py = 95, +}) + +SMODS.Consumable({ + key = "prophet", + set = "Tarot", + atlas = "prophet", + cost = 3, + unlocked = true, + discovered = true, + loc_vars = function(self, info_queue, card) + MP.UTILS.add_nemesis_info(info_queue) + return { vars = {} } + end, + in_pool = function(self) + return MP.LOBBY.code and MP.LOBBY.config.ruleset == "ruleset_mp_smallworld" + end, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + local _card = copier or card + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1", 0.8, 1) + card:juice_up(0.8, 0.5) + + -- Create prophet joker selection UI + MP.create_prophet_ui() + + return true + end, + })) + end, + mp_credits = { + idea = { "olivermarker" }, + art = { "TheTrueRaven" }, -- Using asteroid art + code = { "GitHub Copilot" }, + }, +}) + +-- Function to create the prophet UI showing all jokers using collection-style UI +function MP.create_prophet_ui() + -- Use SMODS collection system for jokers with custom behavior + local pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) + local rows = {5, 5, 5} -- 5 jokers per row, 3 rows per page (15 total per page) + + -- Create card areas for the prophet collection + G.prophet_collection = {} + local deck_tables = {} + local cards_per_page = 0 + local row_totals = {} + + for j = 1, #rows do + row_totals[j] = cards_per_page + cards_per_page = cards_per_page + rows[j] + G.prophet_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2, G.ROOM.T.h, + (rows[j]+0.25)*G.CARD_W, + 0.95*G.CARD_H, + {card_limit = rows[j], type = 'title', highlight_limit = 0, collection = true} + ) + table.insert(deck_tables, { + n = G.UIT.R, + config = {align = "cm", padding = 0.07, no_fill = true}, + nodes = {{n = G.UIT.O, config = {object = G.prophet_collection[j]}}} + }) + end + + -- Create pagination options + local options = {} + for i = 1, math.ceil(#pool/cards_per_page) do + table.insert(options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#pool/cards_per_page))) + end + + -- Function to handle page changes + G.FUNCS.prophet_collection_page = function(e) + if not e or not e.cycle_config then return end + for j = 1, #G.prophet_collection do + for i = #G.prophet_collection[j].cards, 1, -1 do + local c = G.prophet_collection[j]:remove_card(G.prophet_collection[j].cards[i]) + c:remove() + c = nil + end + end + + for j = 1, #rows do + for i = 1, rows[j] do + local center = pool[i+row_totals[j] + (cards_per_page*(e.cycle_config.current_option - 1))] + if not center then break end + local card = Card(G.prophet_collection[j].T.x + G.prophet_collection[j].T.w/2, G.prophet_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + + -- Make the card clickable for Prophet functionality + card.states.hover.can = true + card.click = function() + -- Calculate and show position for this joker + MP.show_joker_position(center.key, center) + end + + card:start_materialize(nil, i>1 or j>1) + G.prophet_collection[j]:emplace(card) + end + end + end + + -- Initialize with first page + G.FUNCS.prophet_collection_page{ cycle_config = { current_option = 1 }} + + -- Create the UI + local ui_definition = create_UIBox_generic_options({ + back_func = 'close_prophet_ui', + contents = { + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = "Select a Joker to See Queue Position", + scale = 0.5, + colour = G.C.WHITE + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.05}, nodes = { + {n = G.UIT.T, config = { + text = "Click any joker to find out how far away it is in your seeded queue", + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + }} + }}, + {n = G.UIT.R, config = {align = "cm", r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = deck_tables}, + (cards_per_page < #pool) and {n = G.UIT.R, config = {align = "cm"}, nodes = { + create_option_cycle({ + options = options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'prophet_collection_page', + current_option = 1, + colour = G.C.RED, + no_pips = true, + focus_args = {snap_to = true, nav = 'wide'} + }) + }} or nil, + } + }) + + -- Show the overlay + G.FUNCS.overlay_menu({ + definition = ui_definition, + }) +end + +-- Function to show a specific joker's position (called when clicking a joker) +function MP.show_joker_position(joker_key, center) + -- Calculate this joker's position in the queue + local max_rerolls = 5000 -- Same value used in calculation + local position = MP.calculate_joker_position(joker_key) + + local position_text, position_color, position_detail + if position then + position_text = "Position #" .. position .. " in joker queue" + position_color = position <= 20 and G.C.GREEN or + position <= 100 and G.C.ORANGE or G.C.WHITE + + if position <= 20 then + position_detail = "Very close! Only " .. (position - 1) .. " joker" .. (position == 2 and "" or "s") .. " ahead of it." + elseif position <= 100 then + position_detail = "Moderately far. " .. (position - 1) .. " jokers ahead of it." + else + position_detail = "Far away. " .. (position - 1) .. " jokers ahead of it." + end + else + position_text = "Not found in next " .. max_rerolls .. " shop rerolls" + position_color = G.C.RED + position_detail = "This joker may be banned, not available in the current pool, or appears beyond " .. max_rerolls .. " rerolls." + end + + -- Create detailed info UI + local info_ui = create_UIBox_generic_options({ + back_func = 'prophet_back_to_collection', + contents = { + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = center.name, + scale = 0.6, + colour = G.C.WHITE + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = position_text, + scale = 0.45, + colour = position_color + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = position_detail, + scale = 0.35, + colour = G.C.UI.TEXT_LIGHT + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = "Rarity: " .. (center.rarity == 1 and "Common" or + center.rarity == 2 and "Uncommon" or + center.rarity == 3 and "Rare" or "Legendary"), + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + }} + }}, + } + }) + + -- Show detailed info + G.FUNCS.overlay_menu({ + definition = info_ui, + }) +end + +-- Function to close the prophet UI completely from detail view +G.FUNCS.prophet_back_to_collection = function(e) + G.FUNCS.exit_overlay_menu(e) + -- For debugging: reopen the prophet collection so you can check multiple jokers + MP.create_prophet_ui() +end + +-- Function to calculate a specific joker's position in the seeded joker queue +function MP.calculate_joker_position(joker_key) + -- Check if the joker exists + local joker_center = G.P_CENTERS[joker_key] + if not joker_center then + return nil -- Joker doesn't exist + end + + -- CRITICAL: Save the ENTIRE pseudorandom state before simulation + local saved_pseudorandom = copy_table(G.GAME.pseudorandom) + + -- Simple approach: use the same key_append as the shop ('sho') + -- Let the RNG state advance naturally with each create_card call + local max_jokers = 5000 -- Check next 5000 jokers in the queue + + for position = 1, max_jokers do + -- Create a joker using the same key_append as the shop + local test_card = create_card('Joker', nil, nil, nil, nil, nil, nil, 'sho') + + if test_card and test_card.config and test_card.config.center then + local selected_joker = test_card.config.center.key + + -- Clean up the test card immediately to avoid memory issues + if test_card.remove then + test_card:remove() + end + + -- Check if this is our target joker + if selected_joker == joker_key then + -- CRITICAL: Restore the ENTIRE pseudorandom state before returning + G.GAME.pseudorandom = copy_table(saved_pseudorandom) + return position + end + end + end + + -- CRITICAL: Restore the ENTIRE pseudorandom state before returning (joker not found) + G.GAME.pseudorandom = copy_table(saved_pseudorandom) + return nil -- Not found in next 5000 positions +end + +-- Close function for the Prophet UI +G.FUNCS.close_prophet_ui = function(e) + -- Clean up prophet collection areas + if G.prophet_collection then + for j = 1, #G.prophet_collection do + for i = #G.prophet_collection[j].cards, 1, -1 do + local c = G.prophet_collection[j]:remove_card(G.prophet_collection[j].cards[i]) + if c then + c:remove() + c = nil + end + end + end + G.prophet_collection = nil + end + G.FUNCS.exit_overlay_menu(e) +end From c2c3b06875a15abfd6f9765d5e6d793282daf0bc Mon Sep 17 00:00:00 2001 From: Owills Date: Fri, 19 Dec 2025 19:22:26 -0500 Subject: [PATCH 2/7] Basic prophet art --- assets/1x/t_prophet.jpeg | Bin 0 -> 5637 bytes assets/2x/t_prophet.jpeg | Bin 0 -> 11841 bytes objects/consumables/prophet.lua | 533 ++++++++++++++++---------------- 3 files changed, 269 insertions(+), 264 deletions(-) create mode 100644 assets/1x/t_prophet.jpeg create mode 100644 assets/2x/t_prophet.jpeg diff --git a/assets/1x/t_prophet.jpeg b/assets/1x/t_prophet.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..6b5bcd978174af4ff38ca9872f989876e05ea009 GIT binary patch literal 5637 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 literal 0 HcmV?d00001 diff --git a/assets/2x/t_prophet.jpeg b/assets/2x/t_prophet.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..70cb73a1dd2bc746dbb675d3f4f3f843838a083a GIT binary patch literal 11841 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 MKie{-=5p$P0b+vLF8}}l literal 0 HcmV?d00001 diff --git a/objects/consumables/prophet.lua b/objects/consumables/prophet.lua index d862a5ee..71bfadae 100644 --- a/objects/consumables/prophet.lua +++ b/objects/consumables/prophet.lua @@ -1,290 +1,295 @@ SMODS.Atlas({ - key = "prophet", - path = { - ["default"] = "c_asteroid.png", -- Using asteroid art as requested - ["ru"] = "c_asteroid_ru.png", - }, - px = 71, - py = 95, + key = "prophet", + path = "t_prophet.jpeg", -- Assign a single string value to match the expected type + px = 71, + py = 95, }) SMODS.Consumable({ - key = "prophet", - set = "Tarot", - atlas = "prophet", - cost = 3, - unlocked = true, - discovered = true, - loc_vars = function(self, info_queue, card) - MP.UTILS.add_nemesis_info(info_queue) - return { vars = {} } - end, - in_pool = function(self) - return MP.LOBBY.code and MP.LOBBY.config.ruleset == "ruleset_mp_smallworld" - end, - can_use = function(self, card) - return true - end, - use = function(self, card, area, copier) - local _card = copier or card - G.E_MANAGER:add_event(Event({ - trigger = "after", - delay = 0.4, - func = function() - play_sound("tarot1", 0.8, 1) - card:juice_up(0.8, 0.5) - - -- Create prophet joker selection UI - MP.create_prophet_ui() - - return true - end, - })) - end, - mp_credits = { - idea = { "olivermarker" }, - art = { "TheTrueRaven" }, -- Using asteroid art - code = { "GitHub Copilot" }, + key = "prophet", + set = "Tarot", + atlas = "prophet", + cost = 3, + unlocked = true, + discovered = true, + loc_txt = { + name = "The Prophet", + text = { + "View all {C:attention}Jokers{} and", + "see their position in", + "your seeded {C:attention}shop queue{}" + } }, + loc_vars = function(self, info_queue, card) + MP.UTILS.add_nemesis_info(info_queue) + return { vars = {} } + end, + in_pool = function(self) + return MP.LOBBY.code and MP.LOBBY.config.ruleset == "ruleset_mp_smallworld" + end, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + local _card = copier or card + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1", 0.8, 1) + card:juice_up(0.8, 0.5) + + -- Create prophet joker selection UI + MP.create_prophet_ui() + + return true + end, + })) + end, + mp_credits = { + idea = { "Oliver Marker" }, + art = { "Jonah Jaffe" }, + code = { "Oliver Marker" }, + }, }) -- Function to create the prophet UI showing all jokers using collection-style UI function MP.create_prophet_ui() - -- Use SMODS collection system for jokers with custom behavior - local pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) - local rows = {5, 5, 5} -- 5 jokers per row, 3 rows per page (15 total per page) - - -- Create card areas for the prophet collection - G.prophet_collection = {} - local deck_tables = {} - local cards_per_page = 0 - local row_totals = {} - - for j = 1, #rows do - row_totals[j] = cards_per_page - cards_per_page = cards_per_page + rows[j] - G.prophet_collection[j] = CardArea( - G.ROOM.T.x + 0.2*G.ROOM.T.w/2, G.ROOM.T.h, - (rows[j]+0.25)*G.CARD_W, - 0.95*G.CARD_H, - {card_limit = rows[j], type = 'title', highlight_limit = 0, collection = true} - ) - table.insert(deck_tables, { - n = G.UIT.R, - config = {align = "cm", padding = 0.07, no_fill = true}, - nodes = {{n = G.UIT.O, config = {object = G.prophet_collection[j]}}} - }) - end - - -- Create pagination options - local options = {} - for i = 1, math.ceil(#pool/cards_per_page) do - table.insert(options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#pool/cards_per_page))) - end - - -- Function to handle page changes - G.FUNCS.prophet_collection_page = function(e) - if not e or not e.cycle_config then return end - for j = 1, #G.prophet_collection do - for i = #G.prophet_collection[j].cards, 1, -1 do - local c = G.prophet_collection[j]:remove_card(G.prophet_collection[j].cards[i]) - c:remove() - c = nil - end - end - - for j = 1, #rows do - for i = 1, rows[j] do - local center = pool[i+row_totals[j] + (cards_per_page*(e.cycle_config.current_option - 1))] - if not center then break end - local card = Card(G.prophet_collection[j].T.x + G.prophet_collection[j].T.w/2, G.prophet_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) - - -- Make the card clickable for Prophet functionality - card.states.hover.can = true - card.click = function() - -- Calculate and show position for this joker - MP.show_joker_position(center.key, center) - end - - card:start_materialize(nil, i>1 or j>1) - G.prophet_collection[j]:emplace(card) - end - end - end - - -- Initialize with first page - G.FUNCS.prophet_collection_page{ cycle_config = { current_option = 1 }} - - -- Create the UI - local ui_definition = create_UIBox_generic_options({ - back_func = 'close_prophet_ui', - contents = { - {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { - {n = G.UIT.T, config = { - text = "Select a Joker to See Queue Position", - scale = 0.5, - colour = G.C.WHITE - }} - }}, - {n = G.UIT.R, config = {align = "cm", padding = 0.05}, nodes = { - {n = G.UIT.T, config = { - text = "Click any joker to find out how far away it is in your seeded queue", - scale = 0.3, - colour = G.C.UI.TEXT_LIGHT - }} - }}, - {n = G.UIT.R, config = {align = "cm", r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = deck_tables}, - (cards_per_page < #pool) and {n = G.UIT.R, config = {align = "cm"}, nodes = { - create_option_cycle({ - options = options, - w = 4.5, - cycle_shoulders = true, - opt_callback = 'prophet_collection_page', - current_option = 1, - colour = G.C.RED, - no_pips = true, - focus_args = {snap_to = true, nav = 'wide'} - }) - }} or nil, - } - }) - - -- Show the overlay - G.FUNCS.overlay_menu({ - definition = ui_definition, - }) + -- Use SMODS collection system for jokers with custom behavior + local pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) + local rows = {5, 5, 5} -- 5 jokers per row, 3 rows per page (15 total per page) + + -- Create card areas for the prophet collection + G.prophet_collection = {} + local deck_tables = {} + local cards_per_page = 0 + local row_totals = {} + + for j = 1, #rows do + row_totals[j] = cards_per_page + cards_per_page = cards_per_page + rows[j] + G.prophet_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2, G.ROOM.T.h, + (rows[j]+0.25)*G.CARD_W, + 0.95*G.CARD_H, + {card_limit = rows[j], type = 'title', highlight_limit = 0, collection = true} + ) + table.insert(deck_tables, { + n = G.UIT.R, + config = {align = "cm", padding = 0.07, no_fill = true}, + nodes = {{n = G.UIT.O, config = {object = G.prophet_collection[j]}}} + }) + end + + -- Create pagination options + local options = {} + for i = 1, math.ceil(#pool/cards_per_page) do + table.insert(options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#pool/cards_per_page))) + end + + -- Function to handle page changes + G.FUNCS.prophet_collection_page = function(e) + if not e or not e.cycle_config then return end + for j = 1, #G.prophet_collection do + for i = #G.prophet_collection[j].cards, 1, -1 do + local c = G.prophet_collection[j]:remove_card(G.prophet_collection[j].cards[i]) + c:remove() + c = nil + end + end + + for j = 1, #rows do + for i = 1, rows[j] do + local center = pool[i+row_totals[j] + (cards_per_page*(e.cycle_config.current_option - 1))] + if not center then break end + local card = Card(G.prophet_collection[j].T.x + G.prophet_collection[j].T.w/2, G.prophet_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + + -- Make the card clickable for Prophet functionality + card.states.hover.can = true + card.click = function() + -- Calculate and show position for this joker + MP.show_joker_position(center.key, center) + end + + card:start_materialize(nil, i>1 or j>1) + G.prophet_collection[j]:emplace(card) + end + end + end + + -- Initialize with first page + G.FUNCS.prophet_collection_page{ cycle_config = { current_option = 1 }} + + -- Create the UI + local ui_definition = MP.UIBox_generic_options({ + back_func = 'close_prophet_ui', + contents = { + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = "Select a Joker to See Queue Position", + scale = 0.5, + colour = G.C.WHITE + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.05}, nodes = { + {n = G.UIT.T, config = { + text = "Click any joker to find out how far away it is in your seeded queue", + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + }} + }}, + {n = G.UIT.R, config = {align = "cm", r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = deck_tables}, + (cards_per_page < #pool) and {n = G.UIT.R, config = {align = "cm"}, nodes = { + create_option_cycle({ + options = options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'prophet_collection_page', + current_option = 1, + colour = G.C.RED, + no_pips = true, + focus_args = {snap_to = true, nav = 'wide'} + }) + }} or nil, + } + }) + + -- Show the overlay + G.FUNCS.overlay_menu(ui_definition) end -- Function to show a specific joker's position (called when clicking a joker) function MP.show_joker_position(joker_key, center) - -- Calculate this joker's position in the queue - local max_rerolls = 5000 -- Same value used in calculation - local position = MP.calculate_joker_position(joker_key) - - local position_text, position_color, position_detail - if position then - position_text = "Position #" .. position .. " in joker queue" - position_color = position <= 20 and G.C.GREEN or - position <= 100 and G.C.ORANGE or G.C.WHITE - - if position <= 20 then - position_detail = "Very close! Only " .. (position - 1) .. " joker" .. (position == 2 and "" or "s") .. " ahead of it." - elseif position <= 100 then - position_detail = "Moderately far. " .. (position - 1) .. " jokers ahead of it." - else - position_detail = "Far away. " .. (position - 1) .. " jokers ahead of it." - end - else - position_text = "Not found in next " .. max_rerolls .. " shop rerolls" - position_color = G.C.RED - position_detail = "This joker may be banned, not available in the current pool, or appears beyond " .. max_rerolls .. " rerolls." - end - - -- Create detailed info UI - local info_ui = create_UIBox_generic_options({ - back_func = 'prophet_back_to_collection', - contents = { - {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { - {n = G.UIT.T, config = { - text = center.name, - scale = 0.6, - colour = G.C.WHITE - }} - }}, - {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { - {n = G.UIT.T, config = { - text = position_text, - scale = 0.45, - colour = position_color - }} - }}, - {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { - {n = G.UIT.T, config = { - text = position_detail, - scale = 0.35, - colour = G.C.UI.TEXT_LIGHT - }} - }}, - {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { - {n = G.UIT.T, config = { - text = "Rarity: " .. (center.rarity == 1 and "Common" or - center.rarity == 2 and "Uncommon" or - center.rarity == 3 and "Rare" or "Legendary"), - scale = 0.3, - colour = G.C.UI.TEXT_LIGHT - }} - }}, - } - }) - - -- Show detailed info - G.FUNCS.overlay_menu({ - definition = info_ui, - }) + -- Calculate this joker's position in the queue + local max_rerolls = 5000 -- Same value used in calculation + local position = MP.calculate_joker_position(joker_key) + + local position_text, position_color, position_detail + if position then + position_text = "Position #" .. position .. " in joker queue" + position_color = position <= 20 and G.C.GREEN or + position <= 100 and G.C.ORANGE or G.C.WHITE + + if position <= 20 then + position_detail = "Very close! Only " .. (position - 1) .. " joker" .. (position == 2 and "" or "s") .. " ahead of it." + elseif position <= 100 then + position_detail = "Moderately far. " .. (position - 1) .. " jokers ahead of it." + else + position_detail = "Far away. " .. (position - 1) .. " jokers ahead of it." + end + else + position_text = "Not found in next " .. max_rerolls .. " shop rerolls" + position_color = G.C.RED + position_detail = "This joker may be banned, not available in the current pool, or appears beyond " .. max_rerolls .. " rerolls." + end + + -- Create detailed info UI + local info_ui = create_UIBox_generic_options({ + back_func = 'prophet_back_to_collection', + contents = { + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = center.name, + scale = 0.6, + colour = G.C.WHITE + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = position_text, + scale = 0.45, + colour = position_color + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = position_detail, + scale = 0.35, + colour = G.C.UI.TEXT_LIGHT + }} + }}, + {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.T, config = { + text = "Rarity: " .. (center.rarity == 1 and "Common" or + center.rarity == 2 and "Uncommon" or + center.rarity == 3 and "Rare" or "Legendary"), + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + }} + }}, + } + }) + + -- Show detailed info + G.FUNCS.overlay_menu({ + definition = info_ui, + }) end -- Function to close the prophet UI completely from detail view G.FUNCS.prophet_back_to_collection = function(e) - G.FUNCS.exit_overlay_menu(e) - -- For debugging: reopen the prophet collection so you can check multiple jokers - MP.create_prophet_ui() + G.FUNCS.exit_overlay_menu(e) + -- For debugging: reopen the prophet collection so you can check multiple jokers + MP.create_prophet_ui() end -- Function to calculate a specific joker's position in the seeded joker queue function MP.calculate_joker_position(joker_key) - -- Check if the joker exists - local joker_center = G.P_CENTERS[joker_key] - if not joker_center then - return nil -- Joker doesn't exist - end - - -- CRITICAL: Save the ENTIRE pseudorandom state before simulation - local saved_pseudorandom = copy_table(G.GAME.pseudorandom) - - -- Simple approach: use the same key_append as the shop ('sho') - -- Let the RNG state advance naturally with each create_card call - local max_jokers = 5000 -- Check next 5000 jokers in the queue - - for position = 1, max_jokers do - -- Create a joker using the same key_append as the shop - local test_card = create_card('Joker', nil, nil, nil, nil, nil, nil, 'sho') - - if test_card and test_card.config and test_card.config.center then - local selected_joker = test_card.config.center.key - - -- Clean up the test card immediately to avoid memory issues - if test_card.remove then - test_card:remove() - end - - -- Check if this is our target joker - if selected_joker == joker_key then - -- CRITICAL: Restore the ENTIRE pseudorandom state before returning - G.GAME.pseudorandom = copy_table(saved_pseudorandom) - return position - end - end - end - - -- CRITICAL: Restore the ENTIRE pseudorandom state before returning (joker not found) - G.GAME.pseudorandom = copy_table(saved_pseudorandom) - return nil -- Not found in next 5000 positions + -- Check if the joker exists + local joker_center = G.P_CENTERS[joker_key] + if not joker_center then + return nil -- Joker doesn't exist + end + + -- CRITICAL: Save the ENTIRE pseudorandom state before simulation + local saved_pseudorandom = copy_table(G.GAME.pseudorandom) + + -- Simple approach: use the same key_append as the shop ('sho') + -- Let the RNG state advance naturally with each create_card call + local max_jokers = 1000 -- Check next 1000 jokers in the queue + + for position = 1, max_jokers do + -- Create a joker using the same key_append as the shop + local test_card = create_card('Joker', nil, nil, nil, nil, nil, nil, 'sho') + + if test_card and test_card.config and test_card.config.center then + local selected_joker = test_card.config.center.key + + -- Clean up the test card immediately to avoid memory issues + if test_card.remove then + test_card:remove() + end + + -- Check if this is our target joker + if selected_joker == joker_key or + (joker_key == "hanging_chad" and selected_joker == "mp_hanging_chad") then + -- CRITICAL: Restore the ENTIRE pseudorandom state before returning + G.GAME.pseudorandom = saved_pseudorandom + return position + end + end + end + + -- CRITICAL: Restore the ENTIRE pseudorandom state before returning (joker not found) + G.GAME.pseudorandom = saved_pseudorandom + return nil -- Not found in next 1000 positions end -- Close function for the Prophet UI G.FUNCS.close_prophet_ui = function(e) - -- Clean up prophet collection areas - if G.prophet_collection then - for j = 1, #G.prophet_collection do - for i = #G.prophet_collection[j].cards, 1, -1 do - local c = G.prophet_collection[j]:remove_card(G.prophet_collection[j].cards[i]) - if c then - c:remove() - c = nil - end - end - end - G.prophet_collection = nil - end - G.FUNCS.exit_overlay_menu(e) + -- Clean up prophet collection areas + if G.prophet_collection then + for j = 1, #G.prophet_collection do + for i = #G.prophet_collection[j].cards, 1, -1 do + local c = G.prophet_collection[j]:remove_card(G.prophet_collection[j].cards[i]) + if c then + c:remove() + c = nil + end + end + end + G.prophet_collection = nil + end + G.FUNCS.exit_overlay_menu(e) end + From 85074acc93b575d786b0f324184a0c1d340d2e25 Mon Sep 17 00:00:00 2001 From: Owills Date: Fri, 19 Dec 2025 20:00:21 -0500 Subject: [PATCH 3/7] Fix prophet crash --- objects/consumables/prophet.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/objects/consumables/prophet.lua b/objects/consumables/prophet.lua index 71bfadae..82903cab 100644 --- a/objects/consumables/prophet.lua +++ b/objects/consumables/prophet.lua @@ -6,7 +6,7 @@ SMODS.Atlas({ }) SMODS.Consumable({ - key = "prophet", + key = "mp_prophet", set = "Tarot", atlas = "prophet", cost = 3, @@ -25,7 +25,7 @@ SMODS.Consumable({ return { vars = {} } end, in_pool = function(self) - return MP.LOBBY.code and MP.LOBBY.config.ruleset == "ruleset_mp_smallworld" + return MP.LOBBY.code end, can_use = function(self, card) return true @@ -121,7 +121,7 @@ function MP.create_prophet_ui() G.FUNCS.prophet_collection_page{ cycle_config = { current_option = 1 }} -- Create the UI - local ui_definition = MP.UIBox_generic_options({ + local ui_definition = create_UIBox_generic_options({ back_func = 'close_prophet_ui', contents = { {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { @@ -155,13 +155,15 @@ function MP.create_prophet_ui() }) -- Show the overlay - G.FUNCS.overlay_menu(ui_definition) + G.FUNCS.overlay_menu({ + definition = ui_definition, + }) end -- Function to show a specific joker's position (called when clicking a joker) function MP.show_joker_position(joker_key, center) -- Calculate this joker's position in the queue - local max_rerolls = 5000 -- Same value used in calculation + local max_rerolls = 1000 -- Same value used in calculation local position = MP.calculate_joker_position(joker_key) local position_text, position_color, position_detail From 9d510ed88cdcfa62a53d59f50f9110eb59986fd3 Mon Sep 17 00:00:00 2001 From: Owills Date: Sat, 20 Dec 2025 13:23:19 -0500 Subject: [PATCH 4/7] fixes --- assets/1x/c_prophet.png | Bin 0 -> 3996 bytes assets/1x/t_prophet.jpeg | Bin 5637 -> 0 bytes assets/2x/c_prophet.png | Bin 0 -> 5324 bytes assets/2x/t_prophet.jpeg | Bin 11841 -> 0 bytes objects/consumables/prophet.lua | 48 ++++++++++++++++++++++++-------- 5 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 assets/1x/c_prophet.png delete mode 100644 assets/1x/t_prophet.jpeg create mode 100644 assets/2x/c_prophet.png delete mode 100644 assets/2x/t_prophet.jpeg diff --git a/assets/1x/c_prophet.png b/assets/1x/c_prophet.png new file mode 100644 index 0000000000000000000000000000000000000000..5e12a17ce3020757d9d804aeeb57c69f039adf0d GIT binary patch literal 3996 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 PFUHc;#-x0oTikyD>IKe< literal 0 HcmV?d00001 diff --git a/assets/1x/t_prophet.jpeg b/assets/1x/t_prophet.jpeg deleted file mode 100644 index 6b5bcd978174af4ff38ca9872f989876e05ea009..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5637 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 diff --git a/assets/2x/c_prophet.png b/assets/2x/c_prophet.png new file mode 100644 index 0000000000000000000000000000000000000000..59bd2b62ceeaeeaa531c5e1cd783c07dbd0fe59e GIT binary patch literal 5324 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 literal 0 HcmV?d00001 diff --git a/assets/2x/t_prophet.jpeg b/assets/2x/t_prophet.jpeg deleted file mode 100644 index 70cb73a1dd2bc746dbb675d3f4f3f843838a083a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11841 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 MKie{-=5p$P0b+vLF8}}l diff --git a/objects/consumables/prophet.lua b/objects/consumables/prophet.lua index 82903cab..e7dd1dbf 100644 --- a/objects/consumables/prophet.lua +++ b/objects/consumables/prophet.lua @@ -1,6 +1,6 @@ SMODS.Atlas({ key = "prophet", - path = "t_prophet.jpeg", -- Assign a single string value to match the expected type + path = "c_prophet.png", -- Assign a single string value to match the expected type px = 71, py = 95, }) @@ -25,7 +25,7 @@ SMODS.Consumable({ return { vars = {} } end, in_pool = function(self) - return MP.LOBBY.code + return MP.LOBBY.code and MP.LOBBY.config.ruleset == "ruleset_mp_smallworld" end, can_use = function(self, card) return true @@ -53,10 +53,32 @@ SMODS.Consumable({ }, }) +-- Function to map joker keys (handles replacements like hanging_chad -> mp_hanging_chad) +local function map_joker_key(key) + local key_mappings = { + ["j_hanging_chad"] = "j_mp_hanging_chad" + -- Add more mappings here as needed + } + return key_mappings[key] or key +end + -- Function to create the prophet UI showing all jokers using collection-style UI function MP.create_prophet_ui() - -- Use SMODS collection system for jokers with custom behavior - local pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) + -- Use SMODS collection pool, but filter and map keys + local raw_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) + local pool = {} + + -- Filter and map joker keys + for _, center in ipairs(raw_pool) do + local mapped_key = map_joker_key(center.key) + local mapped_center = G.P_CENTERS[mapped_key] + if mapped_center then + table.insert(pool, mapped_center) + else + table.insert(pool, center) -- Keep original if mapping doesn't exist + end + end + local rows = {5, 5, 5} -- 5 jokers per row, 3 rows per page (15 total per page) -- Create card areas for the prophet collection @@ -107,8 +129,9 @@ function MP.create_prophet_ui() -- Make the card clickable for Prophet functionality card.states.hover.can = true card.click = function() - -- Calculate and show position for this joker - MP.show_joker_position(center.key, center) + -- Calculate and show position for this joker (use mapped key) + local mapped_key = map_joker_key(center.key) + MP.show_joker_position(mapped_key, G.P_CENTERS[mapped_key] or center) end card:start_materialize(nil, i>1 or j>1) @@ -231,14 +254,16 @@ end -- Function to close the prophet UI completely from detail view G.FUNCS.prophet_back_to_collection = function(e) G.FUNCS.exit_overlay_menu(e) - -- For debugging: reopen the prophet collection so you can check multiple jokers - MP.create_prophet_ui() + end -- Function to calculate a specific joker's position in the seeded joker queue function MP.calculate_joker_position(joker_key) + -- Map the input key + local mapped_key = map_joker_key(joker_key) + -- Check if the joker exists - local joker_center = G.P_CENTERS[joker_key] + local joker_center = G.P_CENTERS[mapped_key] if not joker_center then return nil -- Joker doesn't exist end @@ -262,9 +287,8 @@ function MP.calculate_joker_position(joker_key) test_card:remove() end - -- Check if this is our target joker - if selected_joker == joker_key or - (joker_key == "hanging_chad" and selected_joker == "mp_hanging_chad") then + -- Check if this is our target joker (compare with mapped key) + if selected_joker == mapped_key then -- CRITICAL: Restore the ENTIRE pseudorandom state before returning G.GAME.pseudorandom = saved_pseudorandom return position From 0c7dd088b7240cab3ec39290061fd9dd7baf8e30 Mon Sep 17 00:00:00 2001 From: Owills Date: Thu, 1 Jan 2026 20:49:36 -0500 Subject: [PATCH 5/7] Cleanup --- objects/consumables/prophet.lua | 55 ++++++--------------------------- 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/objects/consumables/prophet.lua b/objects/consumables/prophet.lua index e7dd1dbf..c97b98dc 100644 --- a/objects/consumables/prophet.lua +++ b/objects/consumables/prophet.lua @@ -12,11 +12,11 @@ SMODS.Consumable({ cost = 3, unlocked = true, discovered = true, - loc_txt = { + loc_txt = { name = "The Prophet", text = { - "View all {C:attention}Jokers{} and", - "see their position in", + "Pick a {C:attention}Joker{} and", + "see its position in", "your seeded {C:attention}shop queue{}" } }, @@ -186,28 +186,8 @@ end -- Function to show a specific joker's position (called when clicking a joker) function MP.show_joker_position(joker_key, center) -- Calculate this joker's position in the queue - local max_rerolls = 1000 -- Same value used in calculation local position = MP.calculate_joker_position(joker_key) - local position_text, position_color, position_detail - if position then - position_text = "Position #" .. position .. " in joker queue" - position_color = position <= 20 and G.C.GREEN or - position <= 100 and G.C.ORANGE or G.C.WHITE - - if position <= 20 then - position_detail = "Very close! Only " .. (position - 1) .. " joker" .. (position == 2 and "" or "s") .. " ahead of it." - elseif position <= 100 then - position_detail = "Moderately far. " .. (position - 1) .. " jokers ahead of it." - else - position_detail = "Far away. " .. (position - 1) .. " jokers ahead of it." - end - else - position_text = "Not found in next " .. max_rerolls .. " shop rerolls" - position_color = G.C.RED - position_detail = "This joker may be banned, not available in the current pool, or appears beyond " .. max_rerolls .. " rerolls." - end - -- Create detailed info UI local info_ui = create_UIBox_generic_options({ back_func = 'prophet_back_to_collection', @@ -221,25 +201,9 @@ function MP.show_joker_position(joker_key, center) }}, {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { {n = G.UIT.T, config = { - text = position_text, + text = position and ("Position #" .. position .. " in queue") or "Not found in next 1000 jokers", scale = 0.45, - colour = position_color - }} - }}, - {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { - {n = G.UIT.T, config = { - text = position_detail, - scale = 0.35, - colour = G.C.UI.TEXT_LIGHT - }} - }}, - {n = G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = { - {n = G.UIT.T, config = { - text = "Rarity: " .. (center.rarity == 1 and "Common" or - center.rarity == 2 and "Uncommon" or - center.rarity == 3 and "Rare" or "Legendary"), - scale = 0.3, - colour = G.C.UI.TEXT_LIGHT + colour = position and (position <= 20 and G.C.GREEN or position <= 100 and G.C.ORANGE or G.C.WHITE) or G.C.RED }} }}, } @@ -254,11 +218,12 @@ end -- Function to close the prophet UI completely from detail view G.FUNCS.prophet_back_to_collection = function(e) G.FUNCS.exit_overlay_menu(e) - end -- Function to calculate a specific joker's position in the seeded joker queue function MP.calculate_joker_position(joker_key) + local MAX_QUEUE_CHECK = 1000 -- Check next 1000 jokers in the queue + -- Map the input key local mapped_key = map_joker_key(joker_key) @@ -271,11 +236,9 @@ function MP.calculate_joker_position(joker_key) -- CRITICAL: Save the ENTIRE pseudorandom state before simulation local saved_pseudorandom = copy_table(G.GAME.pseudorandom) - -- Simple approach: use the same key_append as the shop ('sho') + -- Use the same key_append as the shop ('sho') -- Let the RNG state advance naturally with each create_card call - local max_jokers = 1000 -- Check next 1000 jokers in the queue - - for position = 1, max_jokers do + for position = 1, MAX_QUEUE_CHECK do -- Create a joker using the same key_append as the shop local test_card = create_card('Joker', nil, nil, nil, nil, nil, nil, 'sho') From 8a9d4d3df0862ba55fe1f9596f338bc3a1f5c339 Mon Sep 17 00:00:00 2001 From: Owills Date: Thu, 1 Jan 2026 21:01:39 -0500 Subject: [PATCH 6/7] improvements --- objects/consumables/prophet.lua | 41 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/objects/consumables/prophet.lua b/objects/consumables/prophet.lua index c97b98dc..d1a4de2c 100644 --- a/objects/consumables/prophet.lua +++ b/objects/consumables/prophet.lua @@ -1,6 +1,6 @@ SMODS.Atlas({ key = "prophet", - path = "c_prophet.png", -- Assign a single string value to match the expected type + path = "c_prophet.png", px = 71, py = 95, }) @@ -20,18 +20,16 @@ SMODS.Consumable({ "your seeded {C:attention}shop queue{}" } }, - loc_vars = function(self, info_queue, card) - MP.UTILS.add_nemesis_info(info_queue) + loc_vars = function(self, _info_queue, _card) return { vars = {} } end, in_pool = function(self) return MP.LOBBY.code and MP.LOBBY.config.ruleset == "ruleset_mp_smallworld" end, - can_use = function(self, card) + can_use = function(self, _card) return true end, - use = function(self, card, area, copier) - local _card = copier or card + use = function(self, card, _area, _copier) G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.4, @@ -53,13 +51,22 @@ SMODS.Consumable({ }, }) --- Function to map joker keys (handles replacements like hanging_chad -> mp_hanging_chad) +-- Function to map joker keys (handles reworked jokers like hanging_chad -> mp_hanging_chad) local function map_joker_key(key) - local key_mappings = { - ["j_hanging_chad"] = "j_mp_hanging_chad" - -- Add more mappings here as needed - } - return key_mappings[key] or key + -- Check if this joker has a reworked version in the current ruleset + if MP.LOBBY.config and MP.LOBBY.config.ruleset then + local ruleset = SMODS.get_ruleset(MP.LOBBY.config.ruleset) + if ruleset and ruleset.reworked_jokers then + -- Check if there's a reworked version (indicated by j_mp_ prefix) + local mp_key = "j_mp_" .. key:sub(3) -- Convert j_hanging_chad to j_mp_hanging_chad + for _, reworked_key in ipairs(ruleset.reworked_jokers) do + if reworked_key == mp_key then + return mp_key + end + end + end + end + return key end -- Function to create the prophet UI showing all jokers using collection-style UI @@ -104,9 +111,10 @@ function MP.create_prophet_ui() end -- Create pagination options + local total_pages = math.ceil(#pool/cards_per_page) local options = {} - for i = 1, math.ceil(#pool/cards_per_page) do - table.insert(options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#pool/cards_per_page))) + for i = 1, total_pages do + table.insert(options, localize('k_page')..' '..tostring(i)..'/'..tostring(total_pages)) end -- Function to handle page changes @@ -122,7 +130,8 @@ function MP.create_prophet_ui() for j = 1, #rows do for i = 1, rows[j] do - local center = pool[i+row_totals[j] + (cards_per_page*(e.cycle_config.current_option - 1))] + local page_offset = (e.cycle_config.current_option - 1) * cards_per_page + local center = pool[i + row_totals[j] + page_offset] if not center then break end local card = Card(G.prophet_collection[j].T.x + G.prophet_collection[j].T.w/2, G.prophet_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) @@ -162,7 +171,7 @@ function MP.create_prophet_ui() }} }}, {n = G.UIT.R, config = {align = "cm", r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = deck_tables}, - (cards_per_page < #pool) and {n = G.UIT.R, config = {align = "cm"}, nodes = { + (total_pages > 1) and {n = G.UIT.R, config = {align = "cm"}, nodes = { create_option_cycle({ options = options, w = 4.5, From d1b070e692d874fe90743022d43709b8240c7b84 Mon Sep 17 00:00:00 2001 From: Owills Date: Sat, 10 Jan 2026 21:34:04 -0500 Subject: [PATCH 7/7] debug --- objects/consumables/prophet.lua | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/objects/consumables/prophet.lua b/objects/consumables/prophet.lua index d1a4de2c..d29c9684 100644 --- a/objects/consumables/prophet.lua +++ b/objects/consumables/prophet.lua @@ -53,20 +53,11 @@ SMODS.Consumable({ -- Function to map joker keys (handles reworked jokers like hanging_chad -> mp_hanging_chad) local function map_joker_key(key) - -- Check if this joker has a reworked version in the current ruleset - if MP.LOBBY.config and MP.LOBBY.config.ruleset then - local ruleset = SMODS.get_ruleset(MP.LOBBY.config.ruleset) - if ruleset and ruleset.reworked_jokers then - -- Check if there's a reworked version (indicated by j_mp_ prefix) - local mp_key = "j_mp_" .. key:sub(3) -- Convert j_hanging_chad to j_mp_hanging_chad - for _, reworked_key in ipairs(ruleset.reworked_jokers) do - if reworked_key == mp_key then - return mp_key - end - end - end - end - return key + -- Hardcoded mapping for reworked jokers (only hanging_chad is reworked in Small World) + local key_mappings = { + ["j_hanging_chad"] = "j_mp_hanging_chad" + } + return key_mappings[key] or key end -- Function to create the prophet UI showing all jokers using collection-style UI