From 750116e46cbf73599bed30131e41b1cf84f70e18 Mon Sep 17 00:00:00 2001 From: davidmolgard Date: Fri, 25 Apr 2025 15:30:27 -0600 Subject: [PATCH] changed size of button --- EngineSimulate.lua | 727 ++++++++++++++++++++++--------------------- FantomsPreview.json | 2 +- InterfacePreview.lua | 33 +- Jokers/_Vanilla.lua | 19 +- 4 files changed, 390 insertions(+), 391 deletions(-) diff --git a/EngineSimulate.lua b/EngineSimulate.lua index 903b9b4..f4a34ee 100644 --- a/EngineSimulate.lua +++ b/EngineSimulate.lua @@ -2,419 +2,422 @@ -- -- The heart of this library: it replicates the game's score evaluation. -function FN.SIM.run() - local null_ret = {score = {min=0, exact=0, max=0}, dollars = {min=0, exact=0, max=0}} - if #G.hand.highlighted < 1 then return null_ret end +if not FN.SIM.run then + function FN.SIM.run() + local null_ret = {score = {min=0, exact=0, max=0}, dollars = {min=0, exact=0, max=0}} + if #G.hand.highlighted < 1 then return null_ret end - FN.SIM.init() + FN.SIM.init() - FN.SIM.manage_state("SAVE") - FN.SIM.update_state_variables() + FN.SIM.manage_state("SAVE") + FN.SIM.update_state_variables() - if not FN.SIM.simulate_blind_debuffs() then - FN.SIM.simulate_joker_before_effects() - FN.SIM.add_base_chips_and_mult() - FN.SIM.simulate_blind_effects() - FN.SIM.simulate_scoring_cards() - FN.SIM.simulate_held_cards() - FN.SIM.simulate_joker_global_effects() - FN.SIM.simulate_consumable_effects() - FN.SIM.simulate_deck_effects() - else -- Only Matador at this point: - FN.SIM.simulate_all_jokers(G.jokers, {debuffed_hand = true}) - end - - FN.SIM.manage_state("RESTORE") - - return FN.SIM.get_results() -end - -function FN.SIM.init() - -- Reset: - FN.SIM.running = { - min = {chips = 0, mult = 0, dollars = 0}, - exact = {chips = 0, mult = 0, dollars = 0}, - max = {chips = 0, mult = 0, dollars = 0}, - reps = 0 - } - - -- Fetch metadata about simulated play: - local hand_name, _, poker_hands, scoring_hand, _ = G.FUNCS.get_poker_hand_info(G.hand.highlighted) - FN.SIM.env.scoring_name = hand_name - - -- Identify played cards and extract necessary data: - FN.SIM.env.played_cards = {} - FN.SIM.env.scoring_cards = {} - local is_splash_joker = next(find_joker("Splash")) - table.sort(G.hand.highlighted, function(a, b) return a.T.x < b.T.x end) -- Sorts by positional x-value to mirror card order! - for _, card in ipairs(G.hand.highlighted) do - local is_scoring = false - for _, scoring_card in ipairs(scoring_hand) do - -- Either card is scoring because it's part of the scoring hand, - -- or there is Splash joker, or it's a Stone Card: - if card.sort_id == scoring_card.sort_id - or is_splash_joker - or card.ability.effect == "Stone Card" - then - is_scoring = true - break - end + if not FN.SIM.simulate_blind_debuffs() then + FN.SIM.simulate_joker_before_effects() + FN.SIM.add_base_chips_and_mult() + FN.SIM.simulate_blind_effects() + FN.SIM.simulate_scoring_cards() + FN.SIM.simulate_held_cards() + FN.SIM.simulate_joker_global_effects() + FN.SIM.simulate_consumable_effects() + FN.SIM.simulate_deck_effects() + else -- Only Matador at this point: + FN.SIM.simulate_all_jokers(G.jokers, {debuffed_hand = true}) end - local card_data = FN.SIM.get_card_data(card) - table.insert(FN.SIM.env.played_cards, card_data) - if is_scoring then table.insert(FN.SIM.env.scoring_cards, card_data) end + FN.SIM.manage_state("RESTORE") + + return FN.SIM.get_results() end - -- Identify held cards and extract necessary data: - FN.SIM.env.held_cards = {} - for _, card in ipairs(G.hand.cards) do - -- Highlighted cards are simulated as played cards: - if not card.highlighted then + function FN.SIM.init() + -- Reset: + FN.SIM.running = { + min = {chips = 0, mult = 0, dollars = 0}, + exact = {chips = 0, mult = 0, dollars = 0}, + max = {chips = 0, mult = 0, dollars = 0}, + reps = 0 + } + + -- Fetch metadata about simulated play: + local hand_name, _, poker_hands, scoring_hand, _ = G.FUNCS.get_poker_hand_info(G.hand.highlighted) + FN.SIM.env.scoring_name = hand_name + + -- Identify played cards and extract necessary data: + FN.SIM.env.played_cards = {} + FN.SIM.env.scoring_cards = {} + local is_splash_joker = next(find_joker("Splash")) + table.sort(G.hand.highlighted, function(a, b) return a.T.x < b.T.x end) -- Sorts by positional x-value to mirror card order! + for _, card in ipairs(G.hand.highlighted) do + local is_scoring = false + for _, scoring_card in ipairs(scoring_hand) do + -- Either card is scoring because it's part of the scoring hand, + -- or there is Splash joker, or it's a Stone Card: + if card.sort_id == scoring_card.sort_id + or is_splash_joker + or card.ability.effect == "Stone Card" + then + is_scoring = true + break + end + end + local card_data = FN.SIM.get_card_data(card) - table.insert(FN.SIM.env.held_cards, card_data) - end - end - - -- Extract necessary joker data: - FN.SIM.env.jokers = {} - for _, joker in ipairs(G.jokers.cards) do - local joker_data = { - -- P_CENTER keys for jokers have the form j_NAME, get rid of j_ - id = joker.config.center.key:sub(3, #joker.config.center.key), - ability = copy_table(joker.ability), - edition = copy_table(joker.edition), - rarity = joker.config.center.rarity, - debuff = joker.debuff - } - table.insert(FN.SIM.env.jokers, joker_data) - end - - -- Extract necessary consumable data: - FN.SIM.env.consumables = {} - for _, consumable in ipairs(G.consumeables.cards) do - local consumable_data = { - -- P_CENTER keys have the form x_NAME, get rid of x_ - id = consumable.config.center.key:sub(3, #consumable.config.center.key), - ability = copy_table(consumable.ability) - } - table.insert(FN.SIM.env.consumables, consumable_data) - end - - -- Set extensible context template: - FN.SIM.get_context = function(cardarea, args) - local context = { - cardarea = cardarea, - full_hand = FN.SIM.env.played_cards, - scoring_name = hand_name, - scoring_hand = FN.SIM.env.scoring_cards, - poker_hands = poker_hands - } - - for k, v in pairs(args) do - context[k] = v + table.insert(FN.SIM.env.played_cards, card_data) + if is_scoring then table.insert(FN.SIM.env.scoring_cards, card_data) end end - return context - end -end - -function FN.SIM.get_card_data(card_obj) - return { - rank = card_obj.base.id, - suit = card_obj.base.suit, - base_chips = card_obj.base.nominal, - ability = copy_table(card_obj.ability), - edition = copy_table(card_obj.edition), - seal = card_obj.seal, - debuff = card_obj.debuff, - lucky_trigger = {} - } -end - -function FN.SIM.get_results() - local FNSR = FN.SIM.running - - local min_score = math.floor(FNSR.min.chips * FNSR.min.mult) - local exact_score = math.floor(FNSR.exact.chips * FNSR.exact.mult) - local max_score = math.floor(FNSR.max.chips * FNSR.max.mult) - - return { - score = {min = min_score, exact = exact_score, max = max_score}, - dollars = {min = FNSR.min.dollars, exact = FNSR.exact.dollars, max = FNSR.max.dollars} - } -end - --- --- GAME STATE MANAGEMENT: --- - -function FN.SIM.manage_state(save_or_restore) - local FNSO = FN.SIM.orig - - if save_or_restore == "SAVE" then - FNSO.random_data = copy_table(G.GAME.pseudorandom) - FNSO.hand_data = copy_table(G.GAME.hands) - return - end - - if save_or_restore == "RESTORE" then - G.GAME.pseudorandom = FNSO.random_data - G.GAME.hands = FNSO.hand_data - return - end -end - -function FN.SIM.update_state_variables() - -- Increment poker hand played this run/round: - local hand_info = G.GAME.hands[FN.SIM.env.scoring_name] - hand_info.played = hand_info.played + 1 - hand_info.played_this_round = hand_info.played_this_round + 1 -end - --- --- MACRO LEVEL: --- - -function FN.SIM.simulate_scoring_cards() - for _, scoring_card in ipairs(FN.SIM.env.scoring_cards) do - FN.SIM.simulate_card_in_context(scoring_card, G.play) - end -end - -function FN.SIM.simulate_held_cards() - for _, held_card in ipairs(FN.SIM.env.held_cards) do - FN.SIM.simulate_card_in_context(held_card, G.hand) - end -end - -function FN.SIM.simulate_joker_global_effects() - for _, joker in ipairs(FN.SIM.env.jokers) do - if joker.edition then -- Foil and Holo: - if joker.edition.chips then FN.SIM.add_chips(joker.edition.chips) end - if joker.edition.mult then FN.SIM.add_mult(joker.edition.mult) end - end - - FN.SIM.simulate_joker(joker, FN.SIM.get_context(G.jokers, {global = true})) - - -- Joker-on-joker effects (eg. Blueprint): - FN.SIM.simulate_all_jokers(G.jokers, {other_joker = joker}) - - if joker.edition then -- Poly: - if joker.edition.x_mult then FN.SIM.x_mult(joker.edition.x_mult) end - end - end -end - -function FN.SIM.simulate_consumable_effects() - for _, consumable in ipairs(FN.SIM.env.consumables) do - if consumable.ability.set == "Planet" and not consumable.debuff then - if G.GAME.used_vouchers.v_observatory and consumable.ability.consumeable.hand_type == FN.SIM.env.scoring_name then - FN.SIM.x_mult(G.P_CENTERS.v_observatory.config.extra) + -- Identify held cards and extract necessary data: + FN.SIM.env.held_cards = {} + for _, card in ipairs(G.hand.cards) do + -- Highlighted cards are simulated as played cards: + if not card.highlighted then + local card_data = FN.SIM.get_card_data(card) + table.insert(FN.SIM.env.held_cards, card_data) end end - end -end -function FN.SIM.add_base_chips_and_mult() - local played_hand_data = G.GAME.hands[FN.SIM.env.scoring_name] - FN.SIM.add_chips(played_hand_data.chips) - FN.SIM.add_mult(played_hand_data.mult) -end - -function FN.SIM.simulate_joker_before_effects() - for _, joker in ipairs(FN.SIM.env.jokers) do - FN.SIM.simulate_joker(joker, FN.SIM.get_context(G.jokers, {before = true})) - end -end - -function FN.SIM.simulate_blind_effects() - if G.GAME.blind.disabled then return end - - if G.GAME.blind.name == "The Flint" then - local function flint(data) - local half_chips = math.floor(data.chips/2 + 0.5) - local half_mult = math.floor(data.mult/2 + 0.5) - data.chips = mod_chips(math.max(half_chips, 0)) - data.mult = mod_mult(math.max(half_mult, 1)) + -- Extract necessary joker data: + FN.SIM.env.jokers = {} + for _, joker in ipairs(G.jokers.cards) do + local joker_data = { + -- P_CENTER keys for jokers have the form j_NAME, get rid of j_ + id = joker.config.center.key:sub(3, #joker.config.center.key), + ability = copy_table(joker.ability), + edition = copy_table(joker.edition), + rarity = joker.config.center.rarity, + debuff = joker.debuff + } + table.insert(FN.SIM.env.jokers, joker_data) end - flint(FN.SIM.running.min) - flint(FN.SIM.running.exact) - flint(FN.SIM.running.max) - else - -- Other blinds do not impact scoring; refer to Blind:modify_hand(..) - end -end - -function FN.SIM.simulate_deck_effects() - if G.GAME.selected_back.name == 'Plasma Deck' then - local function plasma(data) - local sum = data.chips + data.mult - local half_sum = math.floor(sum/2) - data.chips = mod_chips(half_sum) - data.mult = mod_mult(half_sum) + -- Extract necessary consumable data: + FN.SIM.env.consumables = {} + for _, consumable in ipairs(G.consumeables.cards) do + local consumable_data = { + -- P_CENTER keys have the form x_NAME, get rid of x_ + id = consumable.config.center.key:sub(3, #consumable.config.center.key), + ability = copy_table(consumable.ability) + } + table.insert(FN.SIM.env.consumables, consumable_data) end - plasma(FN.SIM.running.min) - plasma(FN.SIM.running.exact) - plasma(FN.SIM.running.max) - else - -- Other decks do not impact scoring; refer to Back:trigger_effect(..) + -- Set extensible context template: + FN.SIM.get_context = function(cardarea, args) + local context = { + cardarea = cardarea, + full_hand = FN.SIM.env.played_cards, + scoring_name = hand_name, + scoring_hand = FN.SIM.env.scoring_cards, + poker_hands = poker_hands + } + + for k, v in pairs(args) do + context[k] = v + end + + return context + end end -end -function FN.SIM.simulate_blind_debuffs() - local blind_obj = G.GAME.blind - if blind_obj.disabled then return false end + function FN.SIM.get_card_data(card_obj) + return { + rank = card_obj.base.id, + suit = card_obj.base.suit, + base_chips = card_obj.base.nominal, + ability = copy_table(card_obj.ability), + edition = copy_table(card_obj.edition), + seal = card_obj.seal, + debuff = card_obj.debuff, + lucky_trigger = {} + } + end - -- The following are part of Blind:press_play() + function FN.SIM.get_results() + local FNSR = FN.SIM.running - if blind_obj.name == "The Hook" then - blind_obj.triggered = true - for _ = 1, math.min(2, #FN.SIM.env.held_cards) do - -- TODO: Identify cards-in-hand that can affect score, simulate with/without them for min/max - local selected_card, card_key = pseudorandom_element(FN.SIM.env.held_cards, pseudoseed('hook')) - table.remove(FN.SIM.env.held_cards, card_key) - for _, joker in ipairs(FN.SIM.env.jokers) do - -- Note that the cardarea argument is largely arbitrary (used for FN.SIM.JOKERS), - -- I use G.hand because The Hook discards from the hand - FN.SIM.simulate_joker(joker, FN.SIM.get_context(G.hand, {discard = true, other_card = selected_card})) + local min_score = math.floor(FNSR.min.chips * FNSR.min.mult) + local exact_score = math.floor(FNSR.exact.chips * FNSR.exact.mult) + local max_score = math.floor(FNSR.max.chips * FNSR.max.mult) + + return { + score = {min = min_score, exact = exact_score, max = max_score}, + dollars = {min = FNSR.min.dollars, exact = FNSR.exact.dollars, max = FNSR.max.dollars} + } + end + + -- + -- GAME STATE MANAGEMENT: + -- + + function FN.SIM.manage_state(save_or_restore) + local FNSO = FN.SIM.orig + + if save_or_restore == "SAVE" then + FNSO.random_data = copy_table(G.GAME.pseudorandom) + FNSO.hand_data = copy_table(G.GAME.hands) + return + end + + if save_or_restore == "RESTORE" then + G.GAME.pseudorandom = FNSO.random_data + G.GAME.hands = FNSO.hand_data + return + end + end + + function FN.SIM.update_state_variables() + -- Increment poker hand played this run/round: + local hand_info = G.GAME.hands[FN.SIM.env.scoring_name] + hand_info.played = hand_info.played + 1 + hand_info.played_this_round = hand_info.played_this_round + 1 + end + + -- + -- MACRO LEVEL: + -- + + function FN.SIM.simulate_scoring_cards() + for _, scoring_card in ipairs(FN.SIM.env.scoring_cards) do + FN.SIM.simulate_card_in_context(scoring_card, G.play) + end + end + + function FN.SIM.simulate_held_cards() + for _, held_card in ipairs(FN.SIM.env.held_cards) do + FN.SIM.simulate_card_in_context(held_card, G.hand) + end + end + + function FN.SIM.simulate_joker_global_effects() + for _, joker in ipairs(FN.SIM.env.jokers) do + if joker.edition then -- Foil and Holo: + if joker.edition.chips then FN.SIM.add_chips(joker.edition.chips) end + if joker.edition.mult then FN.SIM.add_mult(joker.edition.mult) end + end + + FN.SIM.simulate_joker(joker, FN.SIM.get_context(G.jokers, {global = true})) + + -- Joker-on-joker effects (eg. Blueprint): + FN.SIM.simulate_all_jokers(G.jokers, {other_joker = joker}) + + if joker.edition then -- Poly: + if joker.edition.x_mult then FN.SIM.x_mult(joker.edition.x_mult) end end end end - if blind_obj.name == "The Tooth" then - blind_obj.triggered = true - FN.SIM.add_dollars((-1) * #FN.SIM.env.played_cards) - end - - -- The following are part of Blind:debuff_hand(..) - - if blind_obj.name == "The Arm" then - blind_obj.triggered = false - - local played_hand_name = FN.SIM.env.scoring_name - if G.GAME.hands[played_hand_name].level > 1 then - blind_obj.triggered = true - -- NOTE: Important to save/restore G.GAME.hands here - -- NOTE: Implementation mirrors level_up_hand(..) - local played_hand_data = G.GAME.hands[played_hand_name] - played_hand_data.level = math.max(1, played_hand_data.level - 1) - played_hand_data.mult = math.max(1, played_hand_data.s_mult + (played_hand_data.level-1) * played_hand_data.l_mult) - played_hand_data.chips = math.max(0, played_hand_data.s_chips + (played_hand_data.level-1) * played_hand_data.l_chips) + function FN.SIM.simulate_consumable_effects() + for _, consumable in ipairs(FN.SIM.env.consumables) do + if consumable.ability.set == "Planet" and not consumable.debuff then + if G.GAME.used_vouchers.v_observatory and consumable.ability.consumeable.hand_type == FN.SIM.env.scoring_name then + FN.SIM.x_mult(G.P_CENTERS.v_observatory.config.extra) + end + end end - return false -- IMPORTANT: Avoid duplicate effects from Blind:debuff_hand() below end - if blind_obj.name == "The Ox" then - blind_obj.triggered = false + function FN.SIM.add_base_chips_and_mult() + local played_hand_data = G.GAME.hands[FN.SIM.env.scoring_name] + FN.SIM.add_chips(played_hand_data.chips) + FN.SIM.add_mult(played_hand_data.mult) + end - if FN.SIM.env.scoring_name == G.GAME.current_round.most_played_poker_hand then - blind_obj.triggered = true - FN.SIM.add_dollars(-G.GAME.dollars) + function FN.SIM.simulate_joker_before_effects() + for _, joker in ipairs(FN.SIM.env.jokers) do + FN.SIM.simulate_joker(joker, FN.SIM.get_context(G.jokers, {before = true})) end - return false -- IMPORTANT: Avoid duplicate effects from Blind:debuff_hand() below end - return blind_obj:debuff_hand(G.hand.highlighted, FN.SIM.env.poker_hands, FN.SIM.env.scoring_name, true) -end + function FN.SIM.simulate_blind_effects() + if G.GAME.blind.disabled then return end --- --- MICRO LEVEL (CARDS): --- + if G.GAME.blind.name == "The Flint" then + local function flint(data) + local half_chips = math.floor(data.chips/2 + 0.5) + local half_mult = math.floor(data.mult/2 + 0.5) + data.chips = mod_chips(math.max(half_chips, 0)) + data.mult = mod_mult(math.max(half_mult, 1)) + end -function FN.SIM.simulate_card_in_context(card, cardarea) - -- Reset and collect repetitions: - FN.SIM.running.reps = 1 - if card.seal == "Red" then FN.SIM.add_reps(1) end - FN.SIM.simulate_all_jokers(cardarea, {other_card = card, repetition = true}) - - -- Apply effects: - for _ = 1, FN.SIM.running.reps do - FN.SIM.simulate_card(card, FN.SIM.get_context(cardarea, {})) - FN.SIM.simulate_all_jokers(cardarea, {other_card = card, individual = true}) - end -end - -function FN.SIM.simulate_card(card_data, context) - -- Do nothing if debuffed: - if card_data.debuff then return end - - if context.cardarea == G.play then - -- Chips: - if card_data.ability.effect == "Stone Card" then - FN.SIM.add_chips(card_data.ability.bonus + (card_data.ability.perma_bonus or 0)) + flint(FN.SIM.running.min) + flint(FN.SIM.running.exact) + flint(FN.SIM.running.max) else - FN.SIM.add_chips(card_data.base_chips + card_data.ability.bonus + (card_data.ability.perma_bonus or 0)) + -- Other blinds do not impact scoring; refer to Blind:modify_hand(..) end + end - -- Mult: - if card_data.ability.effect == "Lucky Card" then - local exact_mult, min_mult, max_mult = FN.SIM.get_probabilistic_extremes(pseudorandom("nope"), 5, card_data.ability.mult, 0) - FN.SIM.add_mult(exact_mult, min_mult, max_mult) - -- Careful not to overwrite `card_data.lucky_trigger` outright: - if exact_mult > 0 then card_data.lucky_trigger.exact = true end - if min_mult > 0 then card_data.lucky_trigger.min = true end - if max_mult > 0 then card_data.lucky_trigger.max = true end + function FN.SIM.simulate_deck_effects() + if G.GAME.selected_back.name == 'Plasma Deck' then + local function plasma(data) + local sum = data.chips + data.mult + local half_sum = math.floor(sum/2) + data.chips = mod_chips(half_sum) + data.mult = mod_mult(half_sum) + end + + plasma(FN.SIM.running.min) + plasma(FN.SIM.running.exact) + plasma(FN.SIM.running.max) else - FN.SIM.add_mult(card_data.ability.mult) + -- Other decks do not impact scoring; refer to Back:trigger_effect(..) + end + end + + function FN.SIM.simulate_blind_debuffs() + local blind_obj = G.GAME.blind + if blind_obj.disabled then return false end + + -- The following are part of Blind:press_play() + + if blind_obj.name == "The Hook" then + blind_obj.triggered = true + for _ = 1, math.min(2, #FN.SIM.env.held_cards) do + -- TODO: Identify cards-in-hand that can affect score, simulate with/without them for min/max + local selected_card, card_key = pseudorandom_element(FN.SIM.env.held_cards, pseudoseed('hook')) + table.remove(FN.SIM.env.held_cards, card_key) + for _, joker in ipairs(FN.SIM.env.jokers) do + -- Note that the cardarea argument is largely arbitrary (used for FN.SIM.JOKERS), + -- I use G.hand because The Hook discards from the hand + FN.SIM.simulate_joker(joker, FN.SIM.get_context(G.hand, {discard = true, other_card = selected_card})) + end + end end - -- XMult: - if card_data.ability.x_mult > 1 then - FN.SIM.x_mult(card_data.ability.x_mult) + if blind_obj.name == "The Tooth" then + blind_obj.triggered = true + FN.SIM.add_dollars((-1) * #FN.SIM.env.played_cards) end - -- Dollars: - if card_data.seal == "Gold" then - FN.SIM.add_dollars(3) + -- The following are part of Blind:debuff_hand(..) + + if blind_obj.name == "The Arm" then + blind_obj.triggered = false + + local played_hand_name = FN.SIM.env.scoring_name + if G.GAME.hands[played_hand_name].level > 1 then + blind_obj.triggered = true + -- NOTE: Important to save/restore G.GAME.hands here + -- NOTE: Implementation mirrors level_up_hand(..) + local played_hand_data = G.GAME.hands[played_hand_name] + played_hand_data.level = math.max(1, played_hand_data.level - 1) + played_hand_data.mult = math.max(1, played_hand_data.s_mult + (played_hand_data.level-1) * played_hand_data.l_mult) + played_hand_data.chips = math.max(0, played_hand_data.s_chips + (played_hand_data.level-1) * played_hand_data.l_chips) + end + return false -- IMPORTANT: Avoid duplicate effects from Blind:debuff_hand() below end - if card_data.ability.p_dollars > 0 then - if card_data.ability.effect == "Lucky Card" then - local exact_dollars, min_dollars, max_dollars = FN.SIM.get_probabilistic_extremes(pseudorandom("notthistime"), 15, card_data.ability.p_dollars, 0) - FN.SIM.add_dollars(exact_dollars, min_dollars, max_dollars) - -- Careful not to overwrite `card_data.lucky_trigger` outright: - if exact_dollars > 0 then card_data.lucky_trigger.exact = true end - if min_dollars > 0 then card_data.lucky_trigger.min = true end - if max_dollars > 0 then card_data.lucky_trigger.max = true end + + if blind_obj.name == "The Ox" then + blind_obj.triggered = false + + if FN.SIM.env.scoring_name == G.GAME.current_round.most_played_poker_hand then + blind_obj.triggered = true + FN.SIM.add_dollars(-G.GAME.dollars) + end + return false -- IMPORTANT: Avoid duplicate effects from Blind:debuff_hand() below + end + + return blind_obj:debuff_hand(G.hand.highlighted, FN.SIM.env.poker_hands, FN.SIM.env.scoring_name, true) + end + + -- + -- MICRO LEVEL (CARDS): + -- + + function FN.SIM.simulate_card_in_context(card, cardarea) + -- Reset and collect repetitions: + FN.SIM.running.reps = 1 + if card.seal == "Red" then FN.SIM.add_reps(1) end + FN.SIM.simulate_all_jokers(cardarea, {other_card = card, repetition = true}) + + -- Apply effects: + for _ = 1, FN.SIM.running.reps do + FN.SIM.simulate_card(card, FN.SIM.get_context(cardarea, {})) + FN.SIM.simulate_all_jokers(cardarea, {other_card = card, individual = true}) + end + end + + function FN.SIM.simulate_card(card_data, context) + -- Do nothing if debuffed: + if card_data.debuff then return end + + if context.cardarea == G.play then + -- Chips: + if card_data.ability.effect == "Stone Card" then + FN.SIM.add_chips(card_data.ability.bonus + (card_data.ability.perma_bonus or 0)) else - FN.SIM.add_dollars(card_data.ability.p_dollars) + FN.SIM.add_chips(card_data.base_chips + card_data.ability.bonus + (card_data.ability.perma_bonus or 0)) + end + + -- Mult: + if card_data.ability.effect == "Lucky Card" then + local exact_mult, min_mult, max_mult = FN.SIM.get_probabilistic_extremes(pseudorandom("nope"), 5, card_data.ability.mult, 0) + FN.SIM.add_mult(exact_mult, min_mult, max_mult) + -- Careful not to overwrite `card_data.lucky_trigger` outright: + if exact_mult > 0 then card_data.lucky_trigger.exact = true end + if min_mult > 0 then card_data.lucky_trigger.min = true end + if max_mult > 0 then card_data.lucky_trigger.max = true end + else + FN.SIM.add_mult(card_data.ability.mult) + end + + -- XMult: + if card_data.ability.x_mult > 1 then + FN.SIM.x_mult(card_data.ability.x_mult) + end + + -- Dollars: + if card_data.seal == "Gold" then + FN.SIM.add_dollars(3) + end + if card_data.ability.p_dollars > 0 then + if card_data.ability.effect == "Lucky Card" then + local exact_dollars, min_dollars, max_dollars = FN.SIM.get_probabilistic_extremes(pseudorandom("notthistime"), 15, card_data.ability.p_dollars, 0) + FN.SIM.add_dollars(exact_dollars, min_dollars, max_dollars) + -- Careful not to overwrite `card_data.lucky_trigger` outright: + if exact_dollars > 0 then card_data.lucky_trigger.exact = true end + if min_dollars > 0 then card_data.lucky_trigger.min = true end + if max_dollars > 0 then card_data.lucky_trigger.max = true end + else + FN.SIM.add_dollars(card_data.ability.p_dollars) + end + end + + -- Edition: + if card_data.edition then + if card_data.edition.chips then FN.SIM.add_chips(card_data.edition.chips) end + if card_data.edition.mult then FN.SIM.add_mult(card_data.edition.mult) end + if card_data.edition.x_mult then FN.SIM.x_mult(card_data.edition.x_mult) end + end + + elseif context.cardarea == G.hand then + if card_data.ability.h_mult > 0 then + FN.SIM.add_mult(card_data.ability.h_mult) + end + + if card_data.ability.h_x_mult > 0 then + FN.SIM.x_mult(card_data.ability.h_x_mult) end end + end - -- Edition: - if card_data.edition then - if card_data.edition.chips then FN.SIM.add_chips(card_data.edition.chips) end - if card_data.edition.mult then FN.SIM.add_mult(card_data.edition.mult) end - if card_data.edition.x_mult then FN.SIM.x_mult(card_data.edition.x_mult) end - end + -- + -- MICRO LEVEL (JOKERS): + -- - elseif context.cardarea == G.hand then - if card_data.ability.h_mult > 0 then - FN.SIM.add_mult(card_data.ability.h_mult) - end - - if card_data.ability.h_x_mult > 0 then - FN.SIM.x_mult(card_data.ability.h_x_mult) + function FN.SIM.simulate_all_jokers(cardarea, context_args) + for _, joker in ipairs(FN.SIM.env.jokers) do + FN.SIM.simulate_joker(joker, FN.SIM.get_context(cardarea, context_args)) end end -end --- --- MICRO LEVEL (JOKERS): --- + function FN.SIM.simulate_joker(joker_obj, context) + -- Do nothing if debuffed: + if joker_obj.debuff then return end -function FN.SIM.simulate_all_jokers(cardarea, context_args) - for _, joker in ipairs(FN.SIM.env.jokers) do - FN.SIM.simulate_joker(joker, FN.SIM.get_context(cardarea, context_args)) + local joker_simulation_function = FN.SIM.JOKERS["simulate_" .. joker_obj.id] + if joker_simulation_function then joker_simulation_function(joker_obj, context) end end -end -function FN.SIM.simulate_joker(joker_obj, context) - -- Do nothing if debuffed: - if joker_obj.debuff then return end - - local joker_simulation_function = FN.SIM.JOKERS["simulate_" .. joker_obj.id] - if joker_simulation_function then joker_simulation_function(joker_obj, context) end -end +end \ No newline at end of file diff --git a/FantomsPreview.json b/FantomsPreview.json index 06762d5..2e7cd7b 100644 --- a/FantomsPreview.json +++ b/FantomsPreview.json @@ -7,7 +7,7 @@ "prefix": "fn", "main_file": "CorePreview.lua", "priority": 0, - "version": "2.1.0", + "version": "2.2.0", "dependencies": [], "conflicts": [] } diff --git a/InterfacePreview.lua b/InterfacePreview.lua index 9b8f597..e962b1a 100644 --- a/InterfacePreview.lua +++ b/InterfacePreview.lua @@ -8,28 +8,13 @@ function create_UIBox_HUD() local contents = orig_hud() - local score_node_wrap = {n=G.UIT.R, config={id = "fn_pre_score_wrap", align = "cm", padding = 0.05}, nodes={}} + local score_node_wrap = {n=G.UIT.R, config={id = "fn_pre_score_wrap", align = "cm", padding = 0.1}, nodes={}} table.insert(score_node_wrap.nodes, FN.PRE.get_score_node()) - local calculate_score_button = {n = G.UIT.R, - config = { - align = "cm", - padding = 0.01, - }, - nodes = { - UIBox_button({ - id = "calculate_score_button", - label = { - "Calculate Score" - }, - colour = G.C.RED, - button = "calculate_score_button", - scale = 0.5, - }), - }, - } + local calculate_score_button_wrap = {n=G.UIT.R, config={id = "fn_calculate_score_button_wrap", align = "cm", padding = 0.1}, nodes={}} + table.insert(calculate_score_button_wrap.nodes, FN.PRE.get_calculate_score_button()) table.insert(contents.nodes[1].nodes[1].nodes[4].nodes[1].nodes, score_node_wrap) - table.insert(contents.nodes[1].nodes[1].nodes[4].nodes[1].nodes, calculate_score_button) + table.insert(contents.nodes[1].nodes[1].nodes[4].nodes[1].nodes, calculate_score_button_wrap) --[[local dollars_node_wrap = {n=G.UIT.C, config={id = "fn_pre_dollars_wrap", align = "cm"}, nodes={}} if G.SETTINGS.FN.preview_dollars then table.insert(dollars_node_wrap.nodes, FN.PRE.get_dollars_node()) end @@ -42,6 +27,16 @@ function G.FUNCS.calculate_score_button() FN.PRE.start_new_coroutine() end +function FN.PRE.get_calculate_score_button() + + return {n=G.UIT.C, config={id = "calculate_score_button", button = "calculate_score_button", align = "cm", minh = 0.42, padding = 0.05, r = 0.02, colour = G.C.RED, hover = true, shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = " Calculate Score ", colour = G.C.UI.TEXT_LIGHT, shadow = true, scale = 0.36}} + }} + }} +end + + function FN.PRE.get_score_node() local text_scale = nil if true then text_scale = 0.5 diff --git a/Jokers/_Vanilla.lua b/Jokers/_Vanilla.lua index 4ca6701..494d1ff 100644 --- a/Jokers/_Vanilla.lua +++ b/Jokers/_Vanilla.lua @@ -805,26 +805,27 @@ FNSJ.simulate_seeing_double = function(joker_obj, context) -- Account for all 'real' suits: for _, card in ipairs(context.scoring_hand) do if card.ability.effect ~= "Wild Card" then - if FN.SIM.is_suit(card, "Hearts") then inc_suit("Hearts") end - if FN.SIM.is_suit(card, "Diamonds") then inc_suit("Diamonds") end - if FN.SIM.is_suit(card, "Spades") then inc_suit("Spades") end - if FN.SIM.is_suit(card, "Clubs") then inc_suit("Clubs") end + if DV.SIM.is_suit(card, "Hearts") then inc_suit("Hearts") end + if DV.SIM.is_suit(card, "Diamonds") then inc_suit("Diamonds") end + if DV.SIM.is_suit(card, "Spades") then inc_suit("Spades") end + if DV.SIM.is_suit(card, "Clubs") then inc_suit("Clubs") end end end -- Let Wild Cards fill in the gaps: for _, card in ipairs(context.scoring_hand) do if card.ability.effect == "Wild Card" then - if suit_count["Clubs"] == 0 then inc_suit("Clubs") - elseif FN.SIM.is_suit(card, "Diamonds") and suit_count["Diamonds"] == 0 then inc_suit("Diamonds") - elseif FN.SIM.is_suit(card, "Spades") and suit_count["Spades"] == 0 then inc_suit("Spades") - elseif FN.SIM.is_suit(card, "Hearts") and suit_count["Hearts"] == 0 then inc_suit("Hearts") + -- IMPORTANT: Clubs must come first here, because Clubs are required for xmult. This is in line with game's implementation. + if DV.SIM.is_suit(card, "Clubs") and suit_count["Clubs"] == 0 then inc_suit("Clubs") + elseif DV.SIM.is_suit(card, "Hearts") and suit_count["Hearts"] == 0 then inc_suit("Hearts") + elseif DV.SIM.is_suit(card, "Diamonds") and suit_count["Diamonds"] == 0 then inc_suit("Diamonds") + elseif DV.SIM.is_suit(card, "Spades") and suit_count["Spades"] == 0 then inc_suit("Spades") end end end if suit_count["Clubs"] > 0 and (suit_count["Hearts"] > 0 or suit_count["Diamonds"] > 0 or suit_count["Spades"] > 0) then - FN.SIM.x_mult(joker_obj.ability.extra) + DV.SIM.x_mult(joker_obj.ability.extra) end end end