local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local atlas = SMODS.Atlas { key = "joker", path = "joker.png", px = 71, py = 95, } local negative = {key = "e_negative_consumable", set = "Edition", config = {extra = 1}} local joker = (function() atlas:inject() local z = 0 local row ---@return {x: number, y: number} local function inc() row = row or atlas.image_data:getWidth() / atlas.px / G.SETTINGS.GRAPHICS.texture_scaling local ret = {x = z % row, y = math.floor(z / row)} z = z + 1 return ret end ---@param tbl SMODS.Joker|{artist?: string, idea?: string, Bakery_can_use: (fun(self: self, card: Card): boolean?), Bakery_use_button_text: (fun(self: self, card: Card): string|table|nil), Bakery_use_joker: fun(self: self, card: Card), attributes?: Attributes[]} return function(tbl) tbl.pos = inc() tbl.atlas = "joker" tbl.idea = tbl.idea and "Roland_" .. tbl.idea or nil tbl.artist = tbl.artist and "Roland_" .. tbl.artist or nil if ((tbl.config or {}).extra or {}).flipped ~= nil then tbl.config.extra.front_pos = tbl.pos tbl.config.extra.back_pos = inc() end return q(SMODS.Joker(tbl)) end end)() ---@generic T ---@param tbl T[] ---@return fun(t: T[], i: integer): integer, T ---@return T[] local function ipairs_reversed(tbl) return function(t, i) local k = i and i - 1 or #t local v = t[k] if v ~= nil then return k, v end end, tbl end ---@param card Card local function is_frozen(card) return card.edition and card.edition.key == "e_Roland_frozen" end ---@param card? Card|false|{suit: string, value: string} ---@param fallback? string ---@return {vars: {[number]: string|table|nil}|{colours: {[number]: {[1]: number, [2]: number, [3]: number, [4]: number}}}} local function localize_card(card, fallback) if not card then return { vars = { "", localize {type = "variable", key = fallback or "b_Roland_na"}, "", colours = {G.C.JOKER_GREY, G.C.JOKER_GREY}, }, } end local suit = (card.base or card).suit local value = (card.base or card).value local name = (card.ability or card).name or "" local no_suit = card.base and SMODS.has_no_suit(card) or not suit local no_rank = card.base and SMODS.has_no_rank(card) or not value return { vars = { (value and not no_rank) and localize(value or 14, "ranks") or name, (not no_rank and not no_suit) and localize {type = "variable", key = "b_Roland_of"} or "", no_suit and "" or localize(suit, "suits_plural"), colours = {G.C.IMPORTANT, G.C.SUITS[suit] or G.C.JOKER_GREY}, }, } end ---@param card Card ---@return boolean local function has_rank_and_suit(card) return not SMODS.has_no_rank(card) and not SMODS.has_no_suit(card) end ---@param value table ---@return fun(other: table): boolean local function ranksuitnq(value) return function(other) local v, o = value.base or value, other.base or other return value == other or v.suit ~= o.suit or v.value ~= o.value end end SMODS.Sound { key = "nilly", path = "nilly.ogg", } SMODS.Sound { key = "excalibur", path = "excalibur.ogg", } joker { key = "msjoker", pronouns = "she_her", artist = "ghostlyfield", cost = 1, rarity = 1, eternal_compat = true, blueprint_compat = true, perishable_compat = true, config = {extra = {chips = 30}}, attributes = {"chips"}, loc_vars = function(_, _, card) return {vars = {card.ability.extra.chips}} end, calculate = function(_, card, context) return (context.joker_main or context.forcetrigger) and {card = card, chips = card.ability.extra.chips} or nil end, } joker { key = "jokersr", pronouns = "he_him", artist = "ghostlyfield", config = {extra = {xmult = 1.25}}, attributes = {"xmult"}, cost = 2, rarity = 2, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return {vars = {card.ability.extra.xmult}} end, calculate = function(_, card, context) return (context.joker_main or context.forcetrigger) and {card = card, xmult = card.ability.extra.xmult} or nil end, } joker { key = "mrsbones", pronouns = "she_her", artist = "ghostlyfield", idea = "redstoad", config = {extra = {xmult = 4, requirement = 4}}, attributes = {"xmult"}, cost = G.P_CENTERS.j_mr_bones.cost - 1, rarity = 2, eternal_compat = false, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return {vars = {card.ability.extra.xmult, card.ability.extra.requirement * 100}} end, calculate = function(_, card, context) if context.joker_main or context.forcetrigger then return {card = card, xmult = card.ability.extra.xmult} end if not card.getting_sliced and context.end_of_round and G.GAME.chips / card.ability.extra.requirement < G.GAME.blind.chips then card.getting_sliced = true local message = localize {type = "variable", key = "b_Roland_bye"} SMODS.calculate_effect({message = message, colour = G.C.RED, message_card = card, repetitions = 0}, card) q(function() G.hand_text_area.blind_chips:juice_up() G.hand_text_area.game_chips:juice_up() card:start_dissolve() play_sound "tarot1" end) end end, } joker { key = "sunny", pronouns = "it_its", artist = "char", idea = "redstoad", attributes = {"food", "on_sell"}, cost = 2, rarity = 1, eternal_compat = false, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return localize_card(card.area == G.jokers and ((G.deck or {}).cards or {})[1]) end, calculate = function(_, _, context) if not context.selling_self and not context.forcetrigger then return end draw_card(G.deck, G.hand, 100, "up", false, G.deck.cards[1]) local current_round = G.GAME.current_round local facing_blind = G.GAME.facing_blind SMODS.calculate_context {drawing_cards = true, draw = {G.deck.cards}} SMODS.calculate_context { first_hand_drawn = not current_round.any_hand_drawn and facing_blind or nil, hand_drawn = facing_blind and {G.deck.cards[1]} --[[@as true]], other_drawn = not facing_blind and {G.deck.cards[1]}, } if type(facing_blind) == "table" then facing_blind.any_hand_drawn = facing_blind.any_hand_drawn or facing_blind end end, } joker { key = "hardboiled", pronouns = "it_its", artist = "ghostlyfield", attributes = {"food", "editions", "modify_card", "on_sell"}, cost = 5, rarity = 2, eternal_compat = false, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, info_queue) table.insert(info_queue, G.P_CENTERS.e_Roland_frozen) end, calculate = function(_, _, context) return (context.selling_self or context.forcetrigger) and f(G.hand.cards):each(function(v) q { delay = 0.1, func = function() v:set_edition {Roland_frozen = true} end, } end) or nil end, } joker { key = "basket", pronouns = "they_them", artist = "ghostlyfield", attributes = {"food", "generation", "on_sell"}, cost = 8, rarity = 3, eternal_compat = false, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, info_queue) table.insert(info_queue, negative) end, calculate = function(_, _, context) return (context.selling_self or context.forcetrigger) and f(G.consumeables.cards):each(function(v) return v.ability.consumeable and q { delay = 1, func = function() play_sound "timpani" SMODS.add_card {edition = "e_negative", key = v.config.center.key} end, } end) or nil end, } joker { key = "monomino", pronouns = "it_its", artist = "char", config = {extra = {price = 4, hand_name = "Four of a Kind"}}, attributes = {"sell_value", "scaling", "economy", "hand_type"}, cost = 4, rarity = 1, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.price, localize(extra.hand_name, "poker_hands")}} end, calculate = function(_, card, context) local extra = card.ability.extra if (not context.before or context.scoring_name ~= extra.hand_name) and not context.forcetrigger then return end local c = context.blueprint_card or card c.ability.extra_value = c.ability.extra_value + extra.price c:set_cost() return {message = localize "k_val_up", colour = G.C.MONEY, message_card = card} end, } joker { key = "domino", pronouns = "it_its", artist = "char", config = {extra = {mult_gain = 4, hand_name = "Four of a Kind", mult = 0}}, attributes = {"mult", "scaling", "hand_type"}, cost = 4, rarity = 2, eternal_compat = true, blueprint_compat = true, perishable_compat = false, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.mult_gain, localize(extra.hand_name, "poker_hands"), extra.mult}} end, calculate = function(_, card, context) local extra = card.ability.extra if context.joker_main or context.forcetrigger then return {card = card, mult = extra.mult} end if not context.before or context.scoring_name ~= extra.hand_name then return end extra.mult = extra.mult + extra.mult_gain return {message = localize "k_upgrade_ex", colour = G.C.RED, message_card = card} end, } joker { key = "trimino", pronouns = "it_its", artist = "char", config = {extra = {times = 4, hand_name = "Four of a Kind"}}, attributes = {"generation", "hand_type"}, cost = 8, rarity = 3, eternal_compat = false, blueprint_compat = false, perishable_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.times, localize(extra.hand_name, "poker_hands")}} end, calculate = function(_, card, context) local extra = card.ability.extra if (not context.before or context.blueprint or card.getting_sliced or context.scoring_name ~= extra.hand_name) and not context.forcetrigger then return end card.getting_sliced = not context.forcetrigger q(function() local scored_cards = f(G.play.cards):where "highlighted":table() local copied = {} if not next(scored_cards) then card.getting_sliced = nil return end f(extra.times):each(function() G.playing_card = (G.playing_card or 0) + 1 G.deck.config.card_limit = G.deck.config.card_limit + 1 local chosen = pseudorandom_element(scored_cards, pseudoseed "Roland_trimino") local copy = copy_card(chosen, nil, nil, G.playing_card) copy:add_to_deck() G.hand:emplace(copy) copy:start_materialize() table.insert(copied, copy) table.insert(G.playing_cards, copy) end) SMODS.calculate_effect({{message = localize "k_copied_ex", message_card = card}}, card) playing_card_joker_effects(copied) if not context.forcetrigger then card:start_dissolve() end end) end, } joker { key = "cold", pronouns = "he_him", artist = "ghostlyfield", config = {extra = {xmult = 2}}, attributes = {"xmult"}, cost = 4, rarity = 1, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, info_queue, card) table.insert(info_queue, G.P_CENTERS.e_Roland_frozen) return {vars = {card.ability.extra.xmult}} end, calculate = function(_, card, context) local target = (context.scoring_hand or {})[3] return (context.individual and context.cardarea == G.play and context.other_card == target and is_frozen(context.other_card) or context.forcetrigger) and {xmult = card.ability.extra.xmult} or nil end, } joker { key = "snowsquall", pronouns = "he_they", config = {extra = {mult_gain = 3, mult = 0}}, attributes = {"mult", "scaling", "hand_type"}, cost = 6, rarity = 2, eternal_compat = true, blueprint_compat = true, perishable_compat = false, loc_vars = function(_, info_queue, card) table.insert(info_queue, G.P_CENTERS.e_Roland_frozen) local extra = card.ability.extra return {vars = {extra.mult_gain, extra.mult}} end, calculate = function(_, card, context) local extra = card.ability.extra if context.joker_main or context.forcetrigger then return {card = card, mult = extra.mult} end if context.blueprint or not context.individual or context.cardarea ~= G.play or not is_frozen(context.other_card) then return end extra.mult = extra.mult + extra.mult_gain return {message = localize "k_upgrade_ex", colour = G.C.RED, message_card = card} end, } joker { key = "arctic", pronouns = "they_them", cost = 10, rarity = 3, config = {extra = {frozen = 1, non_frozen = 0, max_stack = 6, color = "DARK_EDITION"}}, attributes = {"retrigger", "editions"}, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, info_queue) table.insert(info_queue, G.P_CENTERS.e_Roland_frozen) end, calculate = function(_, card, context) local extra = card.ability.extra if extra.max_stack and context.blueprint_copiers_stack and extra.max_stack < #context.blueprint_copiers_stack then return end if context.repetition and context.other_card then local repetitions = is_frozen(context.other_card) and extra.frozen or extra.non_frozen if repetitions and repetitions > 0 then return {card = card, colour = G.C[extra.color], repetitions = repetitions} end end local key = card.config.center.key local merged = SMODS.merge_effects( f(G.jokers.cards):where(is_frozen):where(function(v) return v.config.center.key ~= key end):map(function(v) return SMODS.blueprint_effect(card, v, context) end):where(type, "table"):map(function(v) v.colour = G.C[extra.color] return v end):values():table() ) return merged end, } joker { key = "sapling", pronouns = "they_them", artist = "char", idea = "redstoad", attributes = {"mult", "suit"}, cost = 4, rarity = 1, config = {extra = {mult = 15, suits = 3}}, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.mult, extra.suits}} end, calculate = function(_, card, context) local function count(_, k) return f(context.scoring_hand):any(function(v) return v:is_suit(k) end) end if not context.joker_main and not context.forcetrigger then return end local extra = card.ability.extra if f(SMODS.Suits):count(count) >= card.ability.extra.suits or context.forcetrigger then return {card = card, mult = extra.mult} end end, } joker { key = "yard", pronouns = "he_him", config = {extra = {money = 2}}, attributes = {"economy"}, cost = 4, rarity = 1, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return {vars = {card.ability.extra.money}} end, calculate = function(_, card, context) return (context.remove_playing_cards or context.forcetrigger) and {dollars = card.ability.extra.money * #context.removed, card = card} or nil end, } joker { key = "misfortune", pronouns = "she_they", artist = "char", attributes = {"discard", "generation", "seals"}, cost = 6, rarity = 2, eternal_compat = true, blueprint_compat = false, perishable_compat = true, loc_vars = function(_, info_queue) table.insert(info_queue, G.P_SEALS.Purple) table.insert(info_queue, negative) end, in_pool = function() return f(G.playing_cards):any(function(v) return v.seal == "Purple" end) end, } joker { key = "amber", pronouns = "they_them", config = {extra = {xmult = 3}}, pixel_size = {w = 68, h = 68}, attributes = {"xmult"}, cost = 6, rarity = 3, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return {vars = {card.ability.extra.xmult}} end, calculate = function(_, card, context) if context.joker_main or context.forcetrigger then return {card = card, xmult = card.ability.extra.xmult} end card.Roland_amber_waiting = card.Roland_amber_waiting and not context.after local no = card.debuff or context.blueprint or card.Roland_amber_waiting or not context.press_play if no and not context.forcetrigger then return end card.Roland_amber_waiting = true local cards = card.area.cards local keys = f(cards):where(f.nq(card)):keys():table() if not next(keys) then return end local key = pseudorandom_element(keys, pseudoseed "Roland_amber") or card.rank local next = card.rank < key and 1 or -1 for i = card.rank, key - next, next do cards[i], cards[i + next] = cards[i + next], cards[i] end end, } joker { key = "cerulean", pronouns = "she_her", config = {extra = {xmult = 3}}, pixel_size = {w = 68, h = 68}, attributes = {"xmult"}, cost = 6, rarity = 3, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return {vars = {card.ability.extra.xmult}} end, remove_from_deck = function(self) self:cerulean(true) end, calculate = function(_, card, context) return (context.joker_main or context.forcetrigger) and {card = card, xmult = card.ability.extra.xmult} or nil end, update = function(self, card) local _ = card.area == G.jokers and self:cerulean(false) end, cerulean = function(_, value) f(G.jokers.cards):each(function(v) f {"click", "drag", "focus", "hover"}:map(f.index_into(v.states)):each(function(s) s.can = value or v.config.center.key == "j_Roland_cerulean" end) end) end, } joker { key = "crimson", pronouns = "she_her", config = {extra = {xmult = 3}}, pixel_size = {w = 68, h = 68}, attributes = {"xmult"}, cost = 6, rarity = 3, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return {vars = {card.ability.extra.xmult}} end, calculate = function(self, card, context) return (self.crimson() or context.joker_main or context.forcetrigger) and {card = card, xmult = card.ability.extra.xmult} or nil end, crimson = function() f(G.jokers.cards, ipairs_reversed):where(is_frozen, false):each(function(v) local right = G.jokers.cards[v.rank + 1] local debuffed_by_crimson = right and not right.debuff and v.config.center.key ~= "j_turtle_bean" and right.config.center.key == "j_Roland_crimson" if debuffed_by_crimson and v.ability.Roland_crimson == nil then v.ability.Roland_crimson = not not v.debuff v:set_debuff(true) elseif not debuffed_by_crimson and v.ability.Roland_crimson ~= nil then v:set_debuff(v.ability.Roland_crimson) v.ability.Roland_crimson = nil end end) end, } local orig_stop_drag = Card.stop_drag function Card:stop_drag(...) local _ = self.area == G.jokers and G.P_CENTERS.j_Roland_crimson.crimson() return orig_stop_drag(self, ...) end joker { key = "verdant", pronouns = "she_her", config = {extra = {debuffs = 2, xmult = 3}}, pixel_size = {w = 68, h = 68}, attributes = {"xmult"}, cost = 6, rarity = 3, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.xmult, extra.debuffs}} end, calculate = function(self, card, context) local extra = card.ability.extra self:verdant(context.scoring_hand, extra.debuffs) return (context.joker_main or context.forcetrigger) and {card = card, xmult = extra.xmult} or nil end, update = function() local _ = G.STATE == G.STATES.SELECTING_HAND and G.hand and G.hand:parse_highlighted() end, verdant = function(_, cards, amount) return cards and next(cards) and f(G.playing_cards):each(function(v) table.sort(cards, function(a, b) return a.rank < b.rank end) local should_be_verdant = f(amount):map(f.index_into(cards)):any(f.eq(v)) local has_verdant = v.ability.Roland_verdant ~= nil if should_be_verdant and not has_verdant then v.ability.Roland_verdant = not not v.debuff v:set_debuff(true) elseif not should_be_verdant and has_verdant then v:set_debuff(v.ability.Roland_verdant) v.ability.Roland_verdant = nil end end) or nil end, } local orig_unhighlight_all = CardArea.unhighlight_all function CardArea:unhighlight_all(...) G.P_CENTERS.j_Roland_verdant:verdant(self.cards) return orig_unhighlight_all(self, ...) end joker { key = "violet", pronouns = "she_they", idea = "hamester", config = {extra = {before = 0.1, xmult = 9}}, pixel_size = {w = 68, h = 68}, attributes = {"xmult"}, cost = 6, rarity = 3, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.xmult, extra.before}} end, calculate = function(_, card, context) local extra = card.ability.extra return (context.joker_main or context.forcetrigger) and {card = card, xmult = extra.xmult} or (context.initial_scoring_step and {card = card, xmult = extra.before} or nil) end, } joker { key = "venerable", pronouns = "any_all", config = {extra = {xdiscard = 3}}, pixel_size = {w = 68, h = 68}, attributes = {"discard", "passive"}, cost = 6, rarity = 3, eternal_compat = true, blueprint_compat = false, perishable_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.xdiscard}} end, add_to_deck = function(self, card) self:venerable(math.ceil(G.GAME.round_resets.discards * card.ability.extra.xdiscard)) end, remove_from_deck = function(self, card) self:venerable(math.floor(G.GAME.round_resets.discards / card.ability.extra.xdiscard)) end, venerable = function(_, amount) local round_resets = G.GAME.round_resets local discards = round_resets.discards round_resets.discards = amount ease_discard(round_resets.discards - discards) end, } joker { key = "bulldozer", pronouns = "she_her", idea = "redstoad", attributes = {"xmult"}, cost = 6, rarity = 2, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(self, _, card) return {vars = {self:xmult_frozen(card)}} end, calculate = function(self, card, context) return (context.joker_main or context.forcetrigger) and {card = card, xmult = self:xmult_frozen(card)} or nil end, xmult_frozen = function(self, card) if not is_frozen(card) then return self.xmult() end local ability = card.Roland_frozen_ability or {} card.Roland_frozen_ability = ability ability.Roland_frozen_xmult = ability.Roland_frozen_xmult or self.xmult() return ability.Roland_frozen_xmult end, xmult = function() local function mult(id) local next = {Select = true, Upcoming = true} local states = G.GAME.round_resets.blind_states local choices = G.GAME.round_resets.blind_choices return next[states[id]] and G.P_BLINDS[choices[id]].mult end local m = ((G.GAME or {}).blind or {}).mult return m and m ~= 0 and m or mult "Small" or mult "Big" or mult "Boss" or 1 end, } joker { key = "phytoestrogens", pronouns = "she_her", artist = "ghostlyfield", config = {extra = {xmult = 0.3}}, attributes = {"mult", "xmult"}, cost = 8, rarity = 3, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) return {vars = {card.ability.extra.xmult}} end, calculate = function(_, card, context) if not context.joker_main and not context.forcetrigger then return end SMODS.calculate_effect({mult = hand_chips}, card) SMODS.calculate_effect({xmult = card.ability.extra.xmult}, card) end, } joker { key = "nilly", pronouns = "any_all", artist = "ghostlyfield", idea = "redstoad", config = {extra = {flipped = false}}, attributes = {"xmult", "bakery_double_sided"}, cost = 0, rarity = 2, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) local key = card.ability.extra.flipped and "b_Roland_enabled" or "b_Roland_disabled" return {vars = {localize {type = "variable", key = key}}} end, calculate = function(_, card, context) if (not context.joker_main or not card.ability.extra.flipped) and not context.forcetrigger then return end mult = mod_mult(0) update_hand_text({delay = 0}, {chips = hand_chips, mult = mult}) return {sound = "Roland_nilly", message = "0X " .. localize "k_mult", colour = G.C.MULT, message_card = card} end, Bakery_can_use = function(_, card) return not card.debuff end, Bakery_use_button_text = function(_, card) return localize {type = "variable", key = card.debuff and "b_Roland_debuffed" or "b_Roland_toggle"} end, Bakery_use_joker = function(_, card) local _ = card.debuff or Bakery_API.flip_double_sided(card) end, } joker { key = "martingale", pronouns = "he_him", idea = "redstoad", config = {extra = {odds = 2}}, attributes = {"xmult", "chance"}, cost = 8, rarity = 2, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(_, _, card) local odds = card.ability.extra.odds local normal = SMODS.get_probability_vars(card, 1, odds, "RolandMartingaleLoc") local vars = {normal} for i = 0, 7 do table.insert(vars, math.pow(odds, i)) end return {vars = vars} end, calculate = function(self, card, context) if not context.joker_main and not context.forcetrigger then return end local extra, numerator, xmult = card.ability.extra, 1, 1 if G.GAME.blind.name == "bl_mp_nemesis" then return {card = card, xmult = extra.odds} end for _ = 1, 256 do local key = "RolandMartingale" .. tostring(G.GAME.modifiers.Roland_martingale_seed or "") if SMODS.pseudorandom_probability(card, self.key, 1, extra.odds, key) then break end local other_key = "RolandMartingaleNumerator" numerator = numerator * (extra.odds - SMODS.get_probability_vars(card, 1, extra.odds, other_key)) xmult = xmult * extra.odds local message = number_format(numerator) .. "/" .. number_format(xmult) SMODS.calculate_effect({card = card, repetitions = 1, message = message, message_card = card}, card) end SMODS.calculate_effect({card = card, xmult = xmult}, card) end, } joker { key = "idle", pronouns = "any_all", rarity = 2, Roland_idle_capacity = 6, cost = G.P_CENTERS.j_idol.cost, config = {extra = {cards = {}, xmult = 2}}, attributes = {"rank", "suit", "xmult"}, eternal_compat = true, blueprint_compat = true, perishable_compat = true, loc_vars = function(self, _, card) local extra = card.ability.extra local col = {} local vars = f(self.Roland_idle_capacity):flatmap(function(v) local l = localize_card(extra.cards[v], "b_Roland_unassigned").vars f(l.colours):each(function(c) col[#col + 1] = c end) l.colours = nil return l end):values():table() table.insert(vars, extra.xmult) vars["colours"] = col return {vars = vars} end, calculate = function(_, card, context) return context.forcetrigger or context.individual and context.cardarea == G.play and f(card.ability.extra.cards or {}):map(function(v) return f(context.scoring_hand):any(function(x) return x:is_suit(v.suit) and x.base.value == v.value end) or {} end):any(f.eq(context.other_card)) and {xmult = card.ability.extra.xmult} or nil end, Bakery_can_use = function(self, card) local key = self:Roland_idle_status(card) local new_cards = G.hand.highlighted local extra = card.ability.extra if extra.last_text ~= key then extra.last_text = key Bakery_API.rehighlight(card) end local function all_cards() return f(new_cards):concat(extra.cards or {}) end return not card.debuff and next(new_cards) and self.Roland_idle_capacity - #extra.cards - #new_cards >= 0 and f(new_cards):all(has_rank_and_suit) and all_cards():all(function(v) return all_cards():all(ranksuitnq(v)) end) end, Bakery_use_button_text = function(self, card) return localize {type = "variable", key = self:Roland_idle_status(card)} end, Bakery_use_joker = function(_, card) f(G.hand.highlighted):each(function(v) table.insert(card.ability.extra.cards, {suit = v.base.suit, value = v.base.value}) end) end, Roland_idle_status = function(self, card) return card.debuff and "b_Roland_debuffed" or (self.Roland_idle_capacity - #card.ability.extra.cards > 0 and "b_Roland_add" or "b_Roland_full") end, } joker { key = "suitable", pronouns = "she_they", attributes = {"suit", "passive", "hearts", "diamonds", "spades", "clubs"}, cost = 4, rarity = 2, eternal_compat = true, blueprint_compat = false, perishable_compat = true, loc_vars = function(_, info_queue) table.insert(info_queue, G.P_CENTERS.m_wild) local suit = (G.GAME.current_round.Roland_suitable or {}).suit or "Spades" return {vars = {localize(suit, "suits_plural"), colours = {G.C.SUITS[suit]}}} end, add_to_deck = function() G.GAME.modifiers.Roland_suitable = (G.GAME.modifiers.Roland_suitable or 0) + 1 end, remove_from_deck = function() G.GAME.modifiers.Roland_suitable = (G.GAME.modifiers.Roland_suitable or 0) - 1 end, } function SMODS.current_mod.reset_game_globals() local immutable = f(SMODS.find_card "j_Roland_suitable"):any(is_frozen) local suitable = {suit = "Spades"} G.GAME.current_round.Roland_suitable = immutable and G.GAME.current_round.Roland_suitable or suitable if immutable then return end local suits = f(G.playing_cards):where(SMODS.has_no_suit, false):table() local card = pseudorandom_element(suits, "Roland_suitable" .. G.GAME.round_resets.ante) suitable.suit = card and card.base.suit or suitable.suit end local orig_get_enhancements = SMODS.get_enhancements function SMODS.get_enhancements(card, ...) if (G.GAME.modifiers.Roland_suitable or 0) <= 0 or card.base.suit ~= G.GAME.current_round.Roland_suitable.suit or (card.area ~= G.hand and card.area ~= G.play) then return orig_get_enhancements(card, ...) end local ret = orig_get_enhancements(card, ...) or {} ret.m_wild = true return ret end joker { key = "artemis", pronouns = "any_all", artist = "hamester", idea = "redstoad", cost = 6, rarity = 2, config = {extra = { dollars = 3, increase = 4, progress = 0, flipped = false, sequence = {"c_earth", "c_mars", "c_earth"}, }}, attributes = {"economy", "scaling", "planet", "space", "bakery_double_sided"}, eternal_compat = true, blueprint_compat = false, perishable_compat = false, calc_dollar_bonus = function(_, card) return card.ability.extra.dollars end, loc_vars = function(_, info_queue, card) local extra = card.ability.extra local function color(i) return i == extra.progress + 1 and (i == 2 and G.C.ORANGE or G.C.BLUE) or G.C.JOKER_GREY end f(extra.sequence):each(function(v) table.insert(info_queue, G.P_CENTERS[v]) end) return {vars = {extra.dollars, extra.increase, colours = f(#extra.sequence):map(color):table()}} end, calculate = function(_, card, context) local extra = card.ability.extra if not context.forcetrigger and (context.blueprint or not context.using_consumeable or context.consumeable.config.center.key ~= extra.sequence[extra.progress + 1]) then return end extra.progress = extra.progress + 1 _ = (extra.progress == 1) ~= extra.flipped and Bakery_API.flip_double_sided(card) if extra.sequence[extra.progress + 1] then return { message = localize "k_progress", colour = extra.progress == 2 and G.C.ORANGE or G.C.BLUE, message_card = card, } end extra.progress = 0 extra.dollars = extra.dollars + extra.increase return { message = localize "k_upgrade_ex", colour = G.C.MONEY, message_card = card, } end, } joker { key = "excalibur", pronouns = "he_him", idea = "redstoad", cost = 8, rarity = 3, config = {extra = { scored = 0, xmult = 1.5, required = 15, flipped = false, }}, attributes = {"xmult", "enhancements", "bakery_double_sided"}, eternal_compat = true, blueprint_compat = false, perishable_compat = true, loc_vars = function(_, info_queue, card) local extra = card.ability.extra local _ = card.fake_card or table.insert(info_queue, G.P_CENTERS.m_stone) return {vars = {extra.required, extra.scored, extra.xmult}} end, generate_ui = function(self, info_queue, card, ...) Bakery_API.werewolf_ui "j_Roland_excalibur_Back" (self, info_queue, card, ...) if not card or not card.ability.extra.flipped then return end f(info_queue):keys():each(function(v) info_queue[v] = nil end) table.insert(info_queue, G.P_CENTERS.m_stone) end, calculate = function(_, card, context) local extra = card.ability.extra if not context.forcetrigger and (context.end_of_round or not context.individual or (context.cardarea ~= G.play and context.cardarea ~= G.hand) or not SMODS.has_enhancement(context.other_card, "m_stone")) then return end local delta = (context.cardarea == G.play and not card.blueprint) and 1 or 0 extra.scored = extra.scored + delta if not extra.getting_flipped and not extra.flipped and extra.scored == extra.required then extra.getting_flipped = true Bakery_API.flip_double_sided(card) q(function() play_sound "Roland_excalibur" end) end return extra.scored >= extra.required and {card = card, xmult = card.ability.extra.xmult} or (delta > 0 and { message = extra.scored .. "/" .. extra.required, colour = G.C.JOKER_GREY, message_card = card, } or nil) end, } joker { key = "oops", pronouns = "she_they", artist = "char", cost = 7, rarity = 3, config = {extra = {probability = 1, probability_mult = 2, reset = 1}}, attributes = {"hands", "mod_chance", "scaling", "reset"}, eternal_compat = true, blueprint_compat = false, perishable_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra return {vars = {extra.probability_mult, extra.probability}} end, calculate = function(_, card, context) if context.blueprint and not context.forcetrigger or not card.ability then return end local extra = card.ability.extra if context.mod_probability then return {card = card, numerator = extra.probability} end if context.end_of_round and extra.probability ~= extra.reset then extra.probability = extra.reset return {message = localize "k_reset", colour = G.C.RED, message_card = card, repetitions = 0} end if context.after then extra.probability = extra.probability * extra.probability_mult return {message = localize "k_upgrade_ex", colour = G.C.GREEN, message_card = card} end end, }