balatro-mods/Cryptid/lib/overrides.lua

2214 lines
66 KiB
Lua

-- overrides.lua - Adds hooks and overrides used by multiple features.
--Get Pack hooks
-- dumb hook because i don't feel like aggressively patching get_pack to do stuff
-- very inefficient
-- maybe smods should overwrite the function and make it more targetable?
local getpackref = get_pack
function get_pack(_key, _type)
local temp_banned = copy_table(G.GAME.banned_keys)
--Add banished keys (via DELETE) to banned_keys so they don't appear in shop
for k, v in pairs(G.GAME.cry_banished_keys) do
G.GAME.banned_keys[k] = v
end
local abc = getpackref(_key, _type)
--Convert banned keys back to what it was originally
G.GAME.banned_keys = copy_table(temp_banned)
if G.GAME.modifiers.cry_equilibrium then
if not P_CRY_ITEMS then
P_CRY_ITEMS = {}
local valid_pools = { "Joker", "Consumeables", "Voucher", "Booster" }
for _, id in ipairs(valid_pools) do
for k, v in pairs(G.P_CENTER_POOLS[id]) do
if not Cryptid.no(v, "doe", k) then
P_CRY_ITEMS[#P_CRY_ITEMS + 1] = v.key
end
end
end
for k, v in pairs(G.P_CARDS) do
if not Cryptid.no(v, "doe", k) then
P_CRY_ITEMS[#P_CRY_ITEMS + 1] = v.key
end
end
end
return G.P_CENTERS[pseudorandom_element(
P_CRY_ITEMS,
pseudoseed("cry_equipackbrium" .. G.GAME.round_resets.ante)
)]
end
return abc
end
-- get_currrent_pool hook for Deck of Equilibrium and Copies
local gcp = get_current_pool
function get_current_pool(_type, _rarity, _legendary, _append, override_equilibrium_effect)
if type == "Tag" then
for i = 1, #pool do
-- Copies: Turn Double tags into Triple Tags
if pool[i] == "tag_double" and G.GAME.used_vouchers.v_cry_copies then
pool[i] = "tag_cry_triple"
end
-- Tag Printer: Turn Double tags and Triple Tags into Quadruple Tags
if (pool[i] == "tag_double" or pool[i] == "tag_cry_triple") and G.GAME.used_vouchers.v_cry_tag_printer then
pool[i] = "tag_cry_quadruple"
end
-- Clone Machine: Turn Double tags and Triple Tags as well as Quadruple Tags into Quintuple Tags
if
(pool[i] == "tag_double" or pool[i] == "tag_cry_triple" or pool[i] == "tag_cry_quadruple")
and G.GAME.used_vouchers.v_cry_clone_machine
then
pool[i] = "tag_cry_quintuple"
end
end
-- Deck of Equilibrium stuff
elseif
G.GAME.modifiers.cry_equilibrium
and not override_equilibrium_effect
and (_append == "sho" or _type == "Voucher" or _type == "Booster")
then
if
_type ~= "Enhanced"
and _type ~= "Edition"
and _type ~= "Back"
and _type ~= "Tag"
and _type ~= "Seal"
and _type ~= "Stake"
then
-- we're regenerating the pool every time because of banned keys but it's fine tbh
P_CRY_ITEMS = {}
local valid_pools = { "Joker", "Consumeables", "Voucher", "Booster" }
for _, id in ipairs(valid_pools) do
for k, v in pairs(G.P_CENTER_POOLS[id]) do
if
v.unlocked == true
and not Cryptid.no(v, "doe", k)
and not (G.GAME.banned_keys[v.key] or G.GAME.cry_banished_keys[v.key])
then
P_CRY_ITEMS[#P_CRY_ITEMS + 1] = v.key
end
end
end
if #P_CRY_ITEMS <= 0 then
P_CRY_ITEMS[#P_CRY_ITEMS + 1] = "v_blank"
end
return P_CRY_ITEMS, "cry_equilibrium" .. G.GAME.round_resets.ante
end
end
return gcp(_type, _rarity, _legendary, _append)
end
local gnb = get_new_boss
function get_new_boss()
local bl = gnb()
--Fix an issue with adding bosses mid-run
for k, v in pairs(G.P_BLINDS) do
if not G.GAME.bosses_used[k] then
G.GAME.bosses_used[k] = 0
end
end
-- Force Clock and Lavender Loop for Rush hour
if G.GAME.modifiers.cry_rush_hour then
--Check if Clock and Lavender Loop are both enabled
if (Cryptid.enabled("bl_cry_clock") == true) and (Cryptid.enabled("bl_cry_lavender_loop") == true) then
return (G.GAME.round_resets.ante % G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2)
and "bl_cry_lavender_loop"
or "bl_cry_clock"
else
-- Note: code elsewhere will force losses until both blinds are enabled
return bl
end
end
-- Log
if G.GAME.LOG_BOSS then
local v = "" .. G.GAME.LOG_BOSS
if not G.GAME.USING_LOG then
G.GAME.LOG_BOSS = nil
end
return v
end
--This is how nostalgic deck replaces the boss blinds with Nostalgic versions
if G.GAME.modifiers.cry_beta then
local bl_key = string.sub(bl, 4)
local nostalgicblinds = {
arm = (Cryptid.enabled("bl_cry_oldarm") == true),
fish = (Cryptid.enabled("bl_cry_oldfish") == true),
flint = (Cryptid.enabled("bl_cry_oldflint") == true),
house = (Cryptid.enabled("bl_cry_oldhouse") == true),
manacle = (Cryptid.enabled("bl_cry_oldmanacle") == true),
mark = (Cryptid.enabled("bl_cry_oldmark") == true),
ox = (Cryptid.enabled("bl_cry_oldox") == true),
pillar = (Cryptid.enabled("bl_cry_oldpillar") == true),
serpent = (Cryptid.enabled("bl_cry_oldserpent") == true),
}
if nostalgicblinds[bl_key] then
return "bl_cry_old" .. bl_key
end
end
return bl
end
--Add context for after cards are played
local gfep = G.FUNCS.evaluate_play
function G.FUNCS.evaluate_play(e)
gfep(e)
G.GAME.blind:cry_after_play()
end
--Track defeated blinds for Obsidian Orb
local dft = Blind.defeat
function Blind:defeat(s)
dft(self, s)
local obj = self.config.blind
if
(obj.boss and obj.boss.yes_orb)
or (
obj.name == "The Hook"
or obj.name == "The Ox"
or obj.name == "The House"
or obj.name == "The Wall"
or obj.name == "The Wheel"
or obj.name == "The Arm"
or obj.name == "The Club"
or obj.name == "The Fish"
or obj.name == "The Psychic"
or obj.name == "The Goad"
or obj.name == "The Water"
or obj.name == "The Window"
or obj.name == "The Manacle"
or obj.name == "The Eye"
or obj.name == "The Mouth"
or obj.name == "The Plant"
or obj.name == "The Serpent"
or obj.name == "The Pillar"
or obj.name == "The Needle"
or obj.name == "The Head"
or obj.name == "The Tooth"
or obj.name == "The Flint"
or obj.name == "The Mark"
or obj.name == "Amber Acorn"
or obj.name == "Verdant Leaf"
or obj.name == "Violet Vessel"
or obj.name == "Crimsion Heart"
or obj.name == "Cerulean Bell"
)
then
else
return
end
if
--Stop impossible blind combinations from happening
(self.name ~= "cry-oldarm" or not G.GAME.defeated_blinds["bl_psychic"])
and (self.name ~= "The Psychic" or not G.GAME.defeated_blinds["bl_cry_oldarm"])
and (self.name ~= "cry-oldarm" or not G.GAME.defeated_blinds["bl_cry_scorch"])
and (self.name ~= "cry-scorch" or not G.GAME.defeated_blinds["bl_cry_oldarm"])
and (self.name ~= "The Eye" or not G.GAME.defeated_blinds["bl_mouth"])
and (self.name ~= "The Mouth" or not G.GAME.defeated_blinds["bl_eye"])
and (self.name ~= "cry-Lavender Loop" or not G.GAME.defeated_blinds["bl_cry_tax"])
and (self.name ~= "cry-Tax" or not G.GAME.defeated_blinds["bl_cry_lavender_loop"])
and (self.name ~= "The Needle" or not G.GAME.defeated_blinds["bl_cry_tax"])
and (self.name ~= "cry-Tax" or not G.GAME.defeated_blinds["bl_needle"])
then
G.GAME.defeated_blinds[self.config.blind.key or ""] = true
end
end
local sr = Game.start_run
function Game:start_run(args)
sr(self, args)
if G.P_BLINDS.bl_cry_clock then
G.P_BLINDS.bl_cry_clock.mult = 0
end
if not G.GAME.defeated_blinds then
G.GAME.defeated_blinds = {}
end
end
--patch for multiple Clocks to tick separately and load separately
local bsb = Blind.set_blind
function Blind:set_blind(blind, y, z)
local c = "Boss"
if string.sub(G.GAME.subhash or "", -1) == "S" then
c = "Small"
end
if string.sub(G.GAME.subhash or "", -1) == "B" then
c = "Big"
end
if G.GAME.CRY_BLINDS and G.GAME.CRY_BLINDS[c] and not y and blind and blind.mult and blind.cry_ante_base_mod then
blind.mult = G.GAME.CRY_BLINDS[c]
end
bsb(self, blind, y, z)
end
local rb = reset_blinds
function reset_blinds()
if G.GAME.round_resets.blind_states.Boss == "Defeated" then
G.GAME.CRY_BLINDS = {}
if G.P_BLINDS.bl_cry_clock then
G.P_BLINDS.bl_cry_clock.mult = 0
end
end
rb()
end
--Init stuff at the start of the game
local gigo = Game.init_game_object
function Game:init_game_object()
local g = gigo(self)
-- Add initial dropshot and number blocks card
g.current_round.cry_nb_card = { rank = "Ace" }
g.current_round.cry_dropshot_card = { suit = "Spades" }
g.monstermult = 1
g.neutronstarsusedinthisrun = 0
g.sunlevel = 1
g.sunnumber = { modest = 0, not_modest = 0 }
g.bonus_asc_power = 0
g.cry_oboe = 0
g.boostertag = 0
-- Create G.GAME.events when starting a run, so there's no errors
g.events = {}
g.jokers_sold = {}
g.cry_banished_keys = {}
g.cry_last_used_consumeables = {}
g.cry_function_stupid_workaround = {}
-- Added by IcyEthics: Converted the voucher-related modifiers for the tier 3
-- acclimator vouchers to be more generically accessible
g.cry_bonusvouchercount = 0
g.cry_bonusvouchersused = {}
-- Automatically sets up the cry_percrate for each consumable type and sets it to 100 as a default
g.cry_percrate = {}
for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do
g.cry_percrate[v:lower()] = 100
end
return g
end
-- reset_castle_card hook for things like Dropshot and Number Blocks
-- Also exclude specific ranks/suits (such as abstract cards)
local rcc = reset_castle_card
function reset_castle_card()
rcc()
G.GAME.current_round.cry_nb_card = { rank = "Ace" }
G.GAME.current_round.cry_dropshot_card = { suit = "Spades" }
local valid_castle_cards = {}
for k, v in ipairs(G.playing_cards) do
if not SMODS.has_no_suit(v) and not SMODS.has_enhancement(v, "m_cry_abstract") then
valid_castle_cards[#valid_castle_cards + 1] = v
end
end
if valid_castle_cards[1] then
--Dropshot
local castle_card = pseudorandom_element(valid_castle_cards, pseudoseed("cry_dro" .. G.GAME.round_resets.ante))
G.GAME.current_round.cry_dropshot_card.suit = castle_card.base.suit
--Number Blocks
local castle_card_two =
pseudorandom_element(valid_castle_cards, pseudoseed("cry_nb" .. G.GAME.round_resets.ante))
G.GAME.current_round.cry_nb_card.rank = castle_card_two.base.value
G.GAME.current_round.cry_nb_card.id = castle_card_two.base.id
end
end
-- Back.apply_to_run Hook for decks
local Backapply_to_runRef = Back.apply_to_run
function Back.apply_to_run(self)
Backapply_to_runRef(self)
if self.effect.config.cry_no_edition_price then
G.GAME.modifiers.cry_no_edition_price = true
end
end
--Game:update hook
local upd = Game.update
--init colors so they have references
G.C.CRY_TWILIGHT = { 0, 0, 0, 0 }
G.C.CRY_VERDANT = { 0, 0, 0, 0 }
G.C.CRY_EMBER = { 0, 0, 0, 0 }
G.C.CRY_DAWN = { 0, 0, 0, 0 }
G.C.CRY_HORIZON = { 0, 0, 0, 0 }
G.C.CRY_BLOSSOM = { 0, 0, 0, 0 }
G.C.CRY_AZURE = { 0, 0, 0, 0 }
G.C.CRY_ASCENDANT = { 0, 0, 0, 0 }
G.C.CRY_JOLLY = { 0, 0, 0, 0 }
G.C.CRY_GREENGRADIENT = { 0, 0, 0, 0 }
G.C.CRY_ALTGREENGRADIENT = { 0, 0, 0, 0 }
Cryptid.C = {
EXOTIC = { HEX("708b91"), HEX("1e9eba") },
TWILIGHT = { HEX("0800ff"), HEX("aa00ff") },
VERDANT = { HEX("00ff22"), HEX("f4ff57") },
EMBER = { HEX("ff0000"), HEX("ffae00") },
DAWN = { HEX("00aaff"), HEX("ff00e3") },
HORIZON = { HEX("c8fd09"), HEX("1ee7d9") },
BLOSSOM = { HEX("ff09da"), HEX("ffd121") },
AZURE = { HEX("0409ff"), HEX("63dcff") },
ASCENDANT = { HEX("2e00f5"), HEX("e5001d") },
JOLLY = { HEX("6ec1f5"), HEX("456b84") },
SELECTED = { HEX("e38039"), HEX("ccdd1b") },
GREENGRADIENT = { HEX("51e099"), HEX("1e523a") },
ALTGREENGRADIENT = { HEX("6bb565"), HEX("bd28bf") },
TAX_MULT = { HEX("FE5F55"), HEX("40ff40") },
TAX_CHIPS = { HEX("009dff"), HEX("40ff40") },
}
cry_pointer_dt = 0
cry_jimball_dt = 0
cry_glowing_dt = 0
cry_glowing_dt2 = 0
local none_eval = 0
function Game:update(dt)
upd(self, dt)
if not Cryptid.member_count_delay then
Cryptid.member_count_delay = 0
end
if (Cryptid.member_count_delay > 5) or not Cryptid.member_count then -- it doesn't need to update this frequently? but it also doesn't need to be higher tbh...
if Cryptid.update_member_count then
Cryptid.update_member_count()
end -- i honestly hate nil checks like this, wish there was a shorthand
Cryptid.member_count_delay = 0
else
Cryptid.member_count_delay = Cryptid.member_count_delay + dt
end
--Gradients based on Balatrostuck code
local anim_timer = self.TIMERS.REAL * 1.5
local p = 0.5 * (math.sin(anim_timer) + 1)
for k, c in pairs(Cryptid.C) do
if not G.C["CRY_" .. k] then
G.C["CRY_" .. k] = { 0, 0, 0, 0 }
end
for i = 1, 4 do
G.C["CRY_" .. k][i] = c[1][i] * p + c[2][i] * (1 - p)
end
end
G.C.RARITY["cry_exotic"] = G.C.CRY_EXOTIC
G.C.SECONDARY_SET["Content Set"] = G.C.CRY_ASCENDANT
-- Idk what this is for
if Incantation and not CryptidIncanCompat then
AllowStacking("Code")
AllowDividing("Code")
CryptidIncanCompat = true
end
if Cryptid.enabled("set_cry_timer") == true then
cry_pointer_dt = cry_pointer_dt + dt
cry_jimball_dt = cry_jimball_dt + dt
cry_glowing_dt = cry_glowing_dt + dt
cry_glowing_dt2 = cry_glowing_dt2 + dt
end
--Update sprite positions each frame on certain cards to give the illusion of an animated card
if G.P_CENTERS and G.P_CENTERS.c_cry_pointer and cry_pointer_dt > 0.5 then
cry_pointer_dt = 0
local pointerobj = G.P_CENTERS.c_cry_pointer
pointerobj.pos.x = (pointerobj.pos.x == 11) and 12 or 11
end
if G.P_CENTERS and G.P_CENTERS.j_cry_jimball and cry_jimball_dt > 0.1 then
cry_jimball_dt = 0
local jimballobj = G.P_CENTERS.j_cry_jimball
if jimballobj.pos.x == 5 and jimballobj.pos.y == 6 then
jimballobj.pos.x = 0
jimballobj.pos.y = 0
elseif jimballobj.pos.x < 8 then
jimballobj.pos.x = jimballobj.pos.x + 1
elseif jimballobj.pos.y < 6 then
jimballobj.pos.x = 0
jimballobj.pos.y = jimballobj.pos.y + 1
end
end
if G.P_CENTERS and G.P_CENTERS.b_cry_glowing and cry_glowing_dt > 0.1 then
cry_glowing_dt = 0
local glowingobj = G.P_CENTERS.b_cry_glowing
if glowingobj.pos.x == 1 and glowingobj.pos.y == 4 then
glowingobj.pos.x = 0
glowingobj.pos.y = 0
elseif glowingobj.pos.x < 4 then
glowingobj.pos.x = glowingobj.pos.x + 1
elseif glowingobj.pos.y < 6 then
glowingobj.pos.x = 0
glowingobj.pos.y = glowingobj.pos.y + 1
end
end
if G.P_CENTERS and G.P_CENTERS.sleeve_cry_glowing_sleeve and cry_glowing_dt2 > 0.1 then
cry_glowing_dt2 = 0
local glowingobj = G.P_CENTERS.sleeve_cry_glowing_sleeve
if glowingobj.pos.x == 1 and glowingobj.pos.y == 4 then
glowingobj.pos.x = 0
glowingobj.pos.y = 0
elseif glowingobj.pos.x < 4 then
glowingobj.pos.x = glowingobj.pos.x + 1
elseif glowingobj.pos.y < 6 then
glowingobj.pos.x = 0
glowingobj.pos.y = glowingobj.pos.y + 1
end
end
for k, v in pairs(G.I.CARD) do
if v.children.back and v.children.back.atlas.name == "cry_glowing" then
v.children.back:set_sprite_pos(G.P_CENTERS.b_cry_glowing.pos or G.P_CENTERS["b_red"].pos)
end
end
if not G.OVERLAY_MENU and G.GAME.CODE_DESTROY_CARD and not G.OVERLAY_MENU_POINTER then
G.FUNCS.exit_overlay_menu_code()
end
if not G.OVERLAY_MENU then
G.GAME.USING_POINTER = nil
else
G.OVERLAY_MENU_POINTER = nil
end
--Increase the blind size for The Clock and Lavender Loop
local choices = { "Small", "Big", "Boss" }
G.GAME.CRY_BLINDS = G.GAME.CRY_BLINDS or {}
for _, c in pairs(choices) do
if
G.GAME
and G.GAME.round_resets
and G.GAME.round_resets.blind_choices
and G.GAME.round_resets.blind_choices[c]
and G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].cry_ante_base_mod
then
if
G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult ~= 0
and G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult_ante ~= G.GAME.round_resets.ante
then
if G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].name == "cry-Obsidian Orb" then
for i = 1, #G.GAME.defeated_blinds do
G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult = G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult
* G.P_BLINDS[G.GAME.defeated_blinds[i]]
/ 2
end
else
G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult = 0
end
G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult_ante = G.GAME.round_resets.ante
end
if
G.GAME.round_resets.blind_states[c] ~= "Current"
and G.GAME.round_resets.blind_states[c] ~= "Defeated"
then
G.GAME.CRY_BLINDS[c] = (G.GAME.CRY_BLINDS[c] or G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult)
+ (
G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].cry_ante_base_mod
and G.P_BLINDS[G.GAME.round_resets.blind_choices[c]]:cry_ante_base_mod(dt)
or 0
)
--Update UI
--todo: in blinds screen, too
if G.blind_select_opts then
if (SMODS.Mods["StrangeLib"] or {}).can_load then
StrangeLib.dynablind.blind_choice_scores[c] = get_blind_amount(G.GAME.round_resets.blind_ante)
* G.GAME.starting_params.ante_scaling
* G.GAME.CRY_BLINDS[c]
StrangeLib.dynablind.blind_choice_score_texts[c] =
number_format(StrangeLib.dynablind.blind_choice_scores[c])
else
local blind_UI =
G.blind_select_opts[string.lower(c)].definition.nodes[1].nodes[1].nodes[1].nodes[1]
local chip_text_node = blind_UI.nodes[1].nodes[3].nodes[1].nodes[2].nodes[2].nodes[3]
if chip_text_node then
chip_text_node.config.text = number_format(
get_blind_amount(G.GAME.round_resets.blind_ante)
* G.GAME.starting_params.ante_scaling
* G.GAME.CRY_BLINDS[c]
)
chip_text_node.config.scale = score_number_scale(
0.9,
get_blind_amount(G.GAME.round_resets.blind_ante)
* G.GAME.starting_params.ante_scaling
* G.GAME.CRY_BLINDS[c]
)
end
G.blind_select_opts[string.lower(c)]:recalculate()
end
end
elseif
G.GAME.round_resets.blind_states[c] ~= "Defeated"
and not G.GAME.blind.disabled
and to_big(G.GAME.chips) < to_big(G.GAME.blind.chips)
then
G.GAME.blind.chips = G.GAME.blind.chips
+ G.GAME.blind:cry_ante_base_mod(dt)
* get_blind_amount(G.GAME.round_resets.ante)
* G.GAME.starting_params.ante_scaling
G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
end
end
if
G.GAME.round_resets.blind_states[c] == "Current"
and G.GAME
and G.GAME.blind
and not G.GAME.blind.disabled
and to_big(G.GAME.chips) < to_big(G.GAME.blind.chips)
then
G.GAME.blind.chips = G.GAME.blind.chips
* (G.GAME.blind.cry_round_base_mod and G.GAME.blind:cry_round_base_mod(dt) or 1)
G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
end
end
if
G.STATE == G.STATES.DRAW_TO_HAND
and not G.hand.cards[1]
and not G.deck.cards[1]
and G.PROFILES[G.SETTINGS.profile].cry_none
then
G.STATE = G.STATES.SELECTING_HAND
G.STATE_COMPLETE = false
end
if G.STATE == G.STATES.NEW_ROUND or G.STATE == G.STATES.HAND_PLAYED then
none_eval = none_eval + dt
else
none_eval = 0
end
end
-- All the scattered set_cost hooks from all the pre refactor files moved into one hook
local sc = Card.set_cost
function Card:set_cost()
-- Makes the edition cost increase usually present not apply if this variable is true
if self.edition and G.GAME.modifiers.cry_no_edition_price then
local m = Cryptid.deep_copy(self.edition)
self.edition = nil
sc(self)
self.edition = m
else
sc(self)
end
--Makes cube and Big Cube always cost a set amount
if self.ability.name == "cry-Cube" then
if Card.get_gameset(self) ~= "modest" then
self.cost = -27
else
self.cost = -12
end
end
if self.ability.name == "cry-Big Cube" then
self.cost = 27
end
--Make Tarots free if Tarot Acclimator is redeemed
--Make Planets free if Planet Acclimator is redeemed
if self.ability.set == "Tarot" and G.GAME.used_vouchers.v_cry_tacclimator then
self.cost = 0
end
if self.ability.set == "Planet" and G.GAME.used_vouchers.v_cry_pacclimator then
self.cost = 0
end
--Multiplies voucher cost by G.GAME.modifiers.cry_voucher_price_hike
--Used by bronze stake to make vouchers %50 more expensive
if self.ability.set == "Voucher" and G.GAME.modifiers.cry_voucher_price_hike then
self.cost = math.floor(self.cost * G.GAME.modifiers.cry_voucher_price_hike)
end
--Clone Tag
for i = 1, #G.GAME.tags do
if G.GAME.tags[i].key == "tag_cry_clone" then
self.cost = self.cost * 1.5
break
end
end
--Update related costs
self.sell_cost = math.max(1, math.floor(self.cost / 2)) + (self.ability.extra_value or 0)
if
self.area
and self.ability.couponed
and (self.area == G.shop_jokers or self.area == G.shop_booster)
and self.ability.name ~= "cry-Cube"
then
self.cost = 0
end
--Makes Cursed Jokers always sell for $0
if self.config and self.config.center and self.config.center.rarity == "cry_cursed" then
self.sell_cost = 0
--Rotten Egg
elseif G.GAME.cry_rotten_amount then
self.sell_cost = G.GAME.cry_rotten_amount
end
self.sell_cost_label = self.facing == "back" and "?" or self.sell_cost
end
local sell_card_stuff = Card.sell_card
function Card:sell_card()
if self.config.center.set == "Joker" then
if self.config.center.key ~= "j_cry_necromancer" then
local contained = false
for _, v in ipairs(G.GAME.jokers_sold) do
if v == self.config.center.key then
contained = true
break
end
end
if not contained then
table.insert(G.GAME.jokers_sold, self.config.center.key)
end
end
-- Add Jolly Joker to the pool if card was treated as Jolly Joker
if self:is_jolly() then
local contained = false
for _, v in ipairs(G.GAME.jokers_sold) do
if v == "j_jolly" then
contained = true
break
end
end
if not contained then
table.insert(G.GAME.jokers_sold, "j_jolly")
end
end
end
sell_card_stuff(self)
end
-- Modify to display badges for credits and some gameset badges
-- todo: make this optional
local smcmb = SMODS.create_mod_badges
function SMODS.create_mod_badges(obj, badges)
smcmb(obj, badges)
if not SMODS.config.no_mod_badges and obj and obj.cry_credits then
local function calc_scale_fac(text)
local size = 0.9
local font = G.LANG.font
local max_text_width = 2 - 2 * 0.05 - 4 * 0.03 * size - 2 * 0.03
local calced_text_width = 0
-- Math reproduced from DynaText:update_text
for _, c in utf8.chars(text) do
local tx = font.FONT:getWidth(c) * (0.33 * size) * G.TILESCALE * font.FONTSCALE
+ 2.7 * 1 * G.TILESCALE * font.FONTSCALE
calced_text_width = calced_text_width + tx / (G.TILESIZE * G.TILESCALE)
end
local scale_fac = calced_text_width > max_text_width and max_text_width / calced_text_width or 1
return scale_fac
end
if obj.cry_credits.art or obj.cry_credits.code or obj.cry_credits.idea then
local scale_fac = {}
local min_scale_fac = 1
local strings = { "Cryptid" }
for _, v in ipairs({ "idea", "art", "code" }) do
if obj.cry_credits[v] then
for i = 1, #obj.cry_credits[v] do
strings[#strings + 1] =
localize({ type = "variable", key = "cry_" .. v, vars = { obj.cry_credits[v][i] } })[1]
end
end
end
for i = 1, #strings do
scale_fac[i] = calc_scale_fac(strings[i])
min_scale_fac = math.min(min_scale_fac, scale_fac[i])
end
local ct = {}
for i = 1, #strings do
ct[i] = {
string = strings[i],
}
end
local cry_badge = {
n = G.UIT.R,
config = { align = "cm" },
nodes = {
{
n = G.UIT.R,
config = {
align = "cm",
colour = G.C.CRY_EXOTIC,
r = 0.1,
minw = 2 / min_scale_fac,
minh = 0.36,
emboss = 0.05,
padding = 0.03 * 0.9,
},
nodes = {
{ n = G.UIT.B, config = { h = 0.1, w = 0.03 } },
{
n = G.UIT.O,
config = {
object = DynaText({
string = ct or "ERROR",
colours = { obj.cry_credits and obj.cry_credits.text_colour or G.C.WHITE },
silent = true,
float = true,
shadow = true,
offset_y = -0.03,
spacing = 1,
scale = 0.33 * 0.9,
}),
},
},
{ n = G.UIT.B, config = { h = 0.1, w = 0.03 } },
},
},
},
}
local function eq_col(x, y)
for i = 1, 4 do
if x[1] ~= y[1] then
return false
end
end
return true
end
for i = 1, #badges do
if eq_col(badges[i].nodes[1].config.colour, HEX("708b91")) then
badges[i].nodes[1].nodes[2].config.object:remove()
badges[i] = cry_badge
break
end
end
end
if obj.cry_credits.jolly then
local scale_fac = {}
local min_scale_fac = 1
for i = 1, #obj.cry_credits.jolly do
scale_fac[i] = calc_scale_fac(obj.cry_credits.jolly[i])
min_scale_fac = math.min(min_scale_fac, scale_fac[i])
end
local ct = {}
for i = 1, #obj.cry_credits.jolly do
ct[i] = {
string = obj.cry_credits.jolly[i],
}
end
badges[#badges + 1] = {
n = G.UIT.R,
config = { align = "cm" },
nodes = {
{
n = G.UIT.R,
config = {
align = "cm",
colour = G.C.CRY_JOLLY,
r = 0.1,
minw = 2,
minh = 0.36,
emboss = 0.05,
padding = 0.03 * 0.9,
},
nodes = {
{ n = G.UIT.B, config = { h = 0.1, w = 0.03 } },
{
n = G.UIT.O,
config = {
object = DynaText({
string = ct or "ERROR",
colours = { obj.cry_credits and obj.cry_credits.text_colour_jolly or G.C.WHITE },
silent = true,
float = true,
shadow = true,
offset_y = -0.03,
spacing = 1,
scale = 0.33 * 0.9,
}),
},
},
{ n = G.UIT.B, config = { h = 0.1, w = 0.03 } },
},
},
},
}
end
end
local id = obj and obj.mod and obj.mod.id
if
Cryptid.safe_get(G, "ACTIVE_MOD_UI", "id") == id
and obj
and not obj.force_gameset
and Cryptid.mod_gameset_whitelist[id]
then
local set = Cryptid.gameset(obj)
if set == "disabled" or obj.set == "Content Set" then
return
end
local card_type = localize("cry_gameset_" .. Cryptid.gameset(obj))
if card_type == "ERROR" then
card_type = localize("cry_gameset_custom")
end
badges[#badges + 1] = create_badge(
card_type,
set == "modest" and G.C.GREEN
or set == "mainline" and G.C.RED
or set == "madness" and G.C.CRY_EXOTIC
or G.C.CRY_ASCENDANT
)
end
end
-- This is short enough that I'm fine overriding it
function calculate_reroll_cost(skip_increment)
local limit = G.GAME.reroll_limit_buffer or nil
if not limit then
if next(find_joker("cry-candybuttons")) then
limit = 1
elseif G.GAME.used_vouchers.v_cry_rerollexchange then
limit = 2
end
end
if not G.GAME.current_round.free_rerolls or G.GAME.current_round.free_rerolls < 0 then
G.GAME.current_round.free_rerolls = 0
end
if next(find_joker("cry-crustulum")) or G.GAME.current_round.free_rerolls > 0 then
G.GAME.current_round.reroll_cost = 0
return
end
if
limit
and (G.GAME.round_resets.temp_reroll_cost or G.GAME.round_resets.reroll_cost)
+ G.GAME.current_round.reroll_cost_increase
>= limit
then
G.GAME.current_round.reroll_cost_increase = 0
G.GAME.current_round.reroll_cost = limit
return
end
G.GAME.current_round.reroll_cost_increase = G.GAME.current_round.reroll_cost_increase or 0
if not skip_increment then
G.GAME.current_round.reroll_cost_increase = G.GAME.current_round.reroll_cost_increase
+ (G.GAME.modifiers.cry_reroll_scaling or 1)
end
G.GAME.current_round.reroll_cost = (G.GAME.round_resets.temp_reroll_cost or G.GAME.round_resets.reroll_cost)
+ G.GAME.current_round.reroll_cost_increase
end
local create_card_ref = create_card
function create_card(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
local area = area or G.jokers
local pseudo = function(x)
return pseudorandom(pseudoseed(x))
end
local ps = pseudoseed
if area == "ERROR" then
pseudo = function(x)
return pseudorandom(Cryptid.predict_pseudoseed(x))
end
ps = Cryptid.predict_pseudoseed
end
if (_type == "Joker" or _type == "Meme") and G.GAME and G.GAME.modifiers and G.GAME.modifiers.all_rnj then
forced_key = "j_cry_rnjoker"
end
local function aeqviable(center)
return center.unlocked and not Cryptid.no(center, "doe") and not (center.rarity == "cry_exotic")
end
if _type == "Joker" and not _rarity and not legendary then
if not G.GAME.aequilibriumkey then
G.GAME.aequilibriumkey = 1
end
local aeqactive = nil
if next(find_joker("Ace Aequilibrium")) and not forced_key then
while not aeqactive or not aeqviable(G.P_CENTER_POOLS.Joker[aeqactive]) do
if math.ceil(G.GAME.aequilibriumkey) > #G.P_CENTER_POOLS["Joker"] then
G.GAME.aequilibriumkey = 1
end
aeqactive = math.ceil(G.GAME.aequilibriumkey)
G.GAME.aequilibriumkey = math.ceil(G.GAME.aequilibriumkey + 1)
end
end
if aeqactive then
forced_key = G.P_CENTER_POOLS["Joker"][aeqactive].key
end
end
if _type == "Base" then
forced_key = "c_base"
end
if forced_key and not G.GAME.banned_keys[forced_key] then
_type = (G.P_CENTERS[forced_key].set ~= "Default" and G.P_CENTERS[forced_key].set or _type)
end
local front = (SMODS.set_create_card_front and (_type == "Base" or _type == "Enhanced")) or nil
if area == "ERROR" then
local ret = (front or G.P_CENTERS[forced_key] or G.P_CENTERS.b_red)
if not ret.config then
ret.config = {}
end
if not ret.config.center then
ret.config.center = {}
end
if not ret.config.center.key then
ret.config.center.key = ""
end
if not ret.ability then
ret.ability = {}
end
return ret --the config.center.key stuff prevents a crash with Jen's Almanac hook
end
local card = create_card_ref(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
local center = card and card.config and card.config.center or {}
if front and G.GAME.modifiers.cry_force_suit then
card:change_suit(G.GAME.modifiers.cry_force_suit)
end
if front and G.GAME.modifiers.cry_force_enhancement then
card:set_ability(G.P_CENTERS[G.GAME.modifiers.cry_force_enhancement])
end
if front and G.GAME.modifiers.cry_force_edition then
card:set_edition({ [G.GAME.modifiers.cry_force_edition] = true }, true, true)
card:add_to_deck()
end
if front and G.GAME.modifiers.cry_force_seal then
card:set_seal(G.GAME.modifiers.cry_force_seal)
end
if
G.GAME.modifiers.cry_force_sticker == "eternal"
or (
G.GAME.modifiers.cry_sticker_sheet_plus
and not (
(_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards))
)
)
then -- wow that is long
card:set_eternal(true)
card.ability.eternal = true
end
if
G.GAME.modifiers.cry_force_sticker == "perishable"
or (
G.GAME.modifiers.cry_sticker_sheet_plus
and not (
(_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards))
)
)
then
card:set_perishable(true)
card.ability.perish_tally = G.GAME.perishable_rounds -- set_perishable should be doing this? whatever
card.ability.perishable = true
end
if
G.GAME.modifiers.cry_force_sticker == "rental"
or (
G.GAME.modifiers.cry_sticker_sheet_plus
and not (
(_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards))
)
)
then
card:set_rental(true)
card.ability.rental = true
end
if
G.GAME.modifiers.cry_force_sticker == "pinned"
or (
G.GAME.modifiers.cry_sticker_sheet_plus
and not (
(_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards))
)
)
then
card.pinned = true
end
if
G.GAME.modifiers.cry_force_sticker == "banana"
or (
G.GAME.modifiers.cry_sticker_sheet_plus
and not (
(_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards))
)
)
then
card.ability.banana = true
end
if G.GAME.modifiers.cry_sticker_sheet_plus and not (_type == "Base" or _type == "Enhanced") then
for k, v in pairs(SMODS.Stickers) do
if v.apply and not v.no_sticker_sheet then
v:apply(card, true)
end
end
end
if card.ability.name == "cry-Cube" then
card:set_eternal(true)
end
if _type == "Joker" or (G.GAME.modifiers.cry_any_stickers and not G.GAME.modifiers.cry_sticker_sheet) then
if G.GAME.modifiers.all_eternal then
card:set_eternal(true)
end
if G.GAME.modifiers.cry_all_perishable then
card:set_perishable(true)
end
if G.GAME.modifiers.cry_all_rental then
card:set_rental(true)
end
if G.GAME.modifiers.cry_all_pinned then
card.pinned = true
end
if G.GAME.modifiers.cry_all_banana then
card.ability.banana = true
end
if (area == G.shop_jokers) or (area == G.pack_cards) then
local eternal_perishable_poll = pseudorandom("cry_et" .. (key_append or "") .. G.GAME.round_resets.ante)
if G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.7 then
card:set_eternal(true)
end
if G.GAME.modifiers.enable_perishables_in_shop then
if
not G.GAME.modifiers.cry_eternal_perishable_compat
and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7))
then
card:set_perishable(true)
end
if
G.GAME.modifiers.cry_eternal_perishable_compat
and pseudorandom("cry_per" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7
then
card:set_perishable(true)
end
end
if
G.GAME.modifiers.enable_rentals_in_shop
and pseudorandom("cry_ssjr" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7
then
card:set_rental(true)
end
if
G.GAME.modifiers.cry_enable_pinned_in_shop
and pseudorandom("cry_pin" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7
then
card.pinned = true
end
if
not G.GAME.modifiers.cry_eternal_perishable_compat
and G.GAME.modifiers.enable_banana
and (pseudorandom("cry_banana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7)
and (eternal_perishable_poll <= 0.7)
then
card.ability.banana = true
end
if
G.GAME.modifiers.cry_eternal_perishable_compat
and G.GAME.modifiers.enable_banana
and (pseudorandom("cry_banana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7)
then
card.ability.banana = true
end
if G.GAME.modifiers.cry_sticker_sheet then
for k, v in pairs(SMODS.Stickers) do
if v.apply and not v.no_sticker_sheet then
v:apply(card, true)
end
end
end
if
not SMODS.is_eternal(card)
and G.GAME.modifiers.cry_enable_flipped_in_shop
and pseudorandom("cry_flip" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7
then
card.cry_flipped = true
end
end
end
if (card.ability.set == "Code") and G.GAME.used_vouchers.v_cry_quantum_computing then
local tot = 0
for k, v in pairs(SMODS.find_card("v_cry_quantum_computing")) do
tot = tot + v.ability.extra
end
if card.ability.cry_multiuse then
card.ability.cry_multiuse = math.ceil((card.ability.cry_multiuse + tot))
else
card.ability.cry_multiuse = tot + 1
end
end
if
G.GAME.modifiers.cry_force_edition
and not G.GAME.modifiers.cry_force_random_edition
and area ~= G.pack_cards
then
card:set_edition(nil, true)
end
if G.GAME.modifiers.cry_force_random_edition and area ~= G.pack_cards then
local edition = Cryptid.poll_random_edition()
card:set_edition(edition, true)
end
if not (card.edition and (card.edition.cry_oversat or card.edition.cry_glitched)) then
Cryptid.manipulate(card)
end
if card.ability.set == "Joker" and G.GAME.modifiers.cry_common_value_quad then
if card.config.center.rarity == 1 then
Cryptid.manipulate(card, { value = 4 })
end
end
if card.ability.set == "Joker" and G.GAME.modifiers.cry_uncommon_value_quad then
if card.config.center.rarity == 2 then
Cryptid.manipulate(card, { value = 4 })
end
end
if card.ability.consumeable and card.pinned then -- counterpart is in Sticker.toml
G.GAME.cry_pinned_consumeables = G.GAME.cry_pinned_consumeables + 0
end
if next(find_joker("Cry-topGear")) and card.config.center.rarity == 1 then
if
card.ability.name ~= "cry-meteor"
and card.ability.name ~= "cry-exoplanet"
and card.ability.name ~= "cry-stardust"
and card.ability.name ~= "cry-universe"
then
card:set_edition("e_polychrome", true, nil, true)
end
end
if card.ability.name == "cry-meteor" then
card:set_edition("e_foil", true, nil, true)
end
if card.ability.name == "cry-exoplanet" then
card:set_edition("e_holo", true, nil, true)
end
if card.ability.name == "cry-stardust" then
card:set_edition("e_polychrome", true, nil, true)
end
if card.ability.name == "cry-universe" then
card:set_edition("e_cry_astral", true, nil, true)
end
-- Certain jokers such as Steel Joker and Driver's License depend on values set
-- during the update function. Cryptid can create jokers mid-scoring, meaning
-- those values will be unset during scoring unless update() is manually called.
card:update(0.016) -- dt is unused in the base game, but we're providing a realistic value anyway
return card
end
local at = add_tag
function add_tag(tag)
at(tag)
-- Update Costs for Clone Tag
if tag.name == "cry-Clone Tag" then
for k, v in pairs(G.I.CARD) do
if v.set_cost then
v:set_cost()
end
end
end
-- Make tags fit if there's more than 13 of them
-- This + Tag.remove Hook modify the offset to squeeze in more tags when needed
if #G.HUD_tags > 13 then
for i = 2, #G.HUD_tags do
G.HUD_tags[i].config.offset.y = 0.9 - 0.9 * 13 / #G.HUD_tags
end
end
end
local tr = Tag.remove
function Tag:remove()
tr(self)
if #G.HUD_tags >= 13 then
for i = 2, #G.HUD_tags do
G.HUD_tags[i].config.offset.y = 0.9 - 0.9 * 13 / #G.HUD_tags
end
end
end
local nr = new_round
function new_round()
-- I don't remember exactly what this patch was for, perhaps issues with syncing hand size with jokers like Effarcire?
G.hand:change_size(0)
nr()
-- Reset Semicolon
G.GAME.current_round.semicolon = false
-- Force losses if Rush hour is played with clock and lavender loop disabled
if G.GAME.modifiers.cry_rush_hour then
if not (Cryptid.enabled("bl_cry_clock") == true) or not (Cryptid.enabled("bl_cry_lavender_loop") == true) then
G.E_MANAGER:add_event(Event({
func = function()
if G.STAGE == G.STAGES.RUN then
G.STATE = G.STATES.GAME_OVER
G.STATE_COMPLETE = false
end
print(localize("rush_hour_reminder"))
return true
end,
}))
end
end
end
local stamp_can_play = G.FUNCS.can_play
G.FUNCS.can_play = function(e)
local value = 0
-- Allow 0 card hand to always be played if none is unlocked and poker hands aren't disabled
if Cryptid.enabled("set_cry_poker_hand_stuff") == true and G.PROFILES[G.SETTINGS.profile].cry_none then
value = -1
end
-- Prevent 1 card hand from being played if Sapphire Stamp is active and poker hands aren't enabled (would result in 0 card hand)
if G.GAME.stamp_mod and Cryptid.enabled("set_cry_poker_hand_stuff") ~= true then
value = 1
end
if value == 0 then
stamp_can_play(e)
else
if
#G.hand.highlighted <= value
or G.GAME.blind.block_play
or #G.hand.highlighted > math.max(G.GAME.starting_params.play_limit, 1)
then
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
else
e.config.colour = G.C.BLUE
e.config.button = "play_cards_from_highlighted"
end
end
end
local stamp_can_discard = G.FUNCS.can_discard
G.FUNCS.can_discard = function(e)
local value = 0
-- Allow 0 card hand to always be discarded if none is unlocked and poker hands aren't disabled
if Cryptid.enabled("set_cry_poker_hand_stuff") == true and G.PROFILES[G.SETTINGS.profile].cry_none then
value = -1
end
if value == 0 then
stamp_can_discard(e)
else
if
G.GAME.current_round.discards_left <= 0
or #G.hand.highlighted <= value
or #G.hand.highlighted > math.max(G.GAME.starting_params.discard_limit, 0)
then
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
else
e.config.colour = G.C.RED
e.config.button = "discard_cards_from_highlighted"
end
end
end
-- These allow jokers that add joker slots to be obtained even without room, like with Negative Jokers in vanilla
local gfcfbs = G.FUNCS.check_for_buy_space
G.FUNCS.check_for_buy_space = function(card)
if
(card.ability.name == "cry-Negative Joker" and card.ability.extra.slots >= 1)
or (card.ability.name == "cry-soccer" and card.ability.extra.holygrail >= 1)
or (card.ability.name == "cry-Tenebris" and card.ability.extra.slots >= 1)
then
return true
end
return gfcfbs(card)
end
local gfcsc = G.FUNCS.can_select_card
G.FUNCS.can_select_card = function(e)
if
(e.config.ref_table.ability.name == "cry-Negative Joker" and e.config.ref_table.ability.extra.slots >= 1)
or (e.config.ref_table.ability.name == "cry-soccer" and e.config.ref_table.ability.extra.holygrail >= 1)
or (e.config.ref_table.ability.name == "cry-Tenebris" and e.config.ref_table.ability.extra.slots >= 1)
then
e.config.colour = G.C.GREEN
e.config.button = "use_card"
else
gfcsc(e)
end
end
--Cryptid (THE MOD) localization
local function parse_loc_txt(center)
center.text_parsed = {}
if center.text then
for _, line in ipairs(center.text) do
center.text_parsed[#center.text_parsed + 1] = loc_parse_string(line)
end
center.name_parsed = {}
for _, line in ipairs(type(center.name) == "table" and center.name or { center.name }) do
center.name_parsed[#center.name_parsed + 1] = loc_parse_string(line)
end
if center.unlock then
center.unlock_parsed = {}
for _, line in ipairs(center.unlock) do
center.unlock_parsed[#center.unlock_parsed + 1] = loc_parse_string(line)
end
end
end
end
local il = init_localization
function init_localization()
if G.SETTINGS.language == "en-us" then
G.localization.descriptions.Spectral.c_cryptid.text[2] = "{C:attention}#2#{} selected card"
G.localization.descriptions.Spectral.c_talisman.text[2] = "to {C:attention}#1#{} selected"
G.localization.descriptions.Spectral.c_trance.text[2] = "to {C:attention}#1#{} selected"
G.localization.descriptions.Spectral.c_medium.text[2] = "to {C:attention}#1#{} selected"
G.localization.descriptions.Spectral.c_deja_vu.text[2] = "to {C:attention}#1#{} selected"
G.localization.descriptions.Spectral.c_deja_vu.text[2] = "to {C:attention}#1#{} selected"
G.localization.descriptions.Spectral.c_deja_vu.text[2] = "to {C:attention}#1#{} selected" -- why is this done THREE times???
G.localization.descriptions.Voucher.v_antimatter.text[1] = "{C:dark_edition}+#1#{} Joker Slot"
G.localization.descriptions.Voucher.v_overstock_norm.text[1] = "{C:attention}+#1#{} card slot"
G.localization.descriptions.Voucher.v_overstock_plus.text[1] = "{C:attention}+#1#{} card slot"
G.localization.descriptions.Voucher.v_crystal_ball.text[1] = "{C:attention}+#1#{} consumable slot"
G.localization.descriptions.Joker.j_seance.text[1] = "If {C:attention}played hand{} contains a" -- damnit seance
end
il()
if Cryptid.object_buffer and Cryptid.object_buffer.Stake then
for i = 1, #Cryptid.object_buffer.Stake do
local key = Cryptid.object_buffer.Stake[i].key
local color = G.localization.descriptions.Stake[key] and G.localization.descriptions.Stake[key].colour
if color then
local sticker_key = key:sub(7) .. "_sticker"
if not G.localization.descriptions.Other[sticker_key] then
G.localization.descriptions.Other[sticker_key] = {
name = localize({ type = "variable", key = "cry_sticker_name", vars = { color } })[1],
text = localize({
type = "variable",
key = "cry_sticker_desc",
vars = {
color,
"{C:attention}",
"{}",
},
}),
}
parse_loc_txt(G.localization.descriptions.Other[sticker_key])
end
end
end
end
for _, group in pairs(G.localization.descriptions) do
if
_ ~= "Back"
and _ ~= "Content Set"
and _ ~= "Edition"
and _ ~= "Enhanced"
and _ ~= "Stake"
and _ ~= "Other"
then
for key, card in pairs(group) do
if G.P_CENTERS[key] then
Cryptid.pointeraliasify(key, card.name, true)
end
end
end
end
Cryptid.inject_pointer_aliases()
end
--Fix a corrupted game state
function Controller:queue_L_cursor_press(x, y)
if self.locks.frame then
return
end
if G.STATE == G.STATES.SPLASH then
if not G.HUD then
self:key_press("escape")
else
G.STATE = G.STATES.BLIND_SELECT
end
end
self.L_cursor_queue = { x = x, y = y }
end
-- Lemon Trophy's effect
local trophy_mod_mult = mod_mult
function mod_mult(_mult)
hand_chips = hand_chips or 0
if G.GAME.trophymod then
_mult = math.min(_mult, math.max(hand_chips, 0))
end
return trophy_mod_mult(_mult)
end
-- Fix a CCD-related crash
local cuc = Card.can_use_consumeable
function Card:can_use_consumeable(any_state, skip_check)
if not self.ability.consumeable then
return false
end
return cuc(self, any_state, skip_check)
end
-- add second back button to create_UIBox_generic_options
local cuigo = create_UIBox_generic_options
function create_UIBox_generic_options(args)
local ret = cuigo(args)
if args.back2 then
local mainUI = ret.nodes[1].nodes[1].nodes
mainUI[#mainUI + 1] = {
n = G.UIT.R,
config = {
id = args.back2_id or "overlay_menu_back2_button",
align = "cm",
minw = 2.5,
button_delay = args.back2_delay,
padding = 0.1,
r = 0.1,
hover = true,
colour = args.back2_colour or G.C.ORANGE,
button = args.back2_func or "exit_overlay_menu",
shadow = true,
focus_args = { nav = "wide", button = "b", snap_to = args.snap_back2 },
},
nodes = {
{
n = G.UIT.R,
config = { align = "cm", padding = 0, no_fill = true },
nodes = {
{
n = G.UIT.T,
config = {
id = args.back2_id or nil,
text = args.back2_label or localize("b_back"),
scale = 0.5,
colour = G.C.UI.TEXT_LIGHT,
shadow = true,
func = not args.no_pip and "set_button_pip" or nil,
focus_args = not args.no_pip and { button = args.back2_button or "b" } or nil,
},
},
},
},
},
}
end
return ret
end
local scuref = set_consumeable_usage
function set_consumeable_usage(card)
if not G.GAME.cry_last_used_consumeables then
G.GAME.cry_last_used_consumeables = {}
end
for i = 1, #G.GAME.cry_last_used_consumeables do
if not G.GAME.cry_function_stupid_workaround then
G.GAME.cry_function_stupid_workaround = {}
end
G.GAME.cry_function_stupid_workaround[i] = G.GAME.cry_last_used_consumeables[i]
end
local nextindex = #G.GAME.cry_last_used_consumeables + 1
G.GAME.cry_last_used_consumeables[nextindex] = card.config.center_key
if nextindex > 3 then
table.remove(G.GAME.cry_last_used_consumeables, 1)
end
scuref(card)
end
--Abstract cards: Fix to avoid "ghost cards", as aresult of destroying discarded cards by adding a flag checcking its not destroyed
G.FUNCS.draw_from_discard_to_deck = function(e)
G.E_MANAGER:add_event(Event({
trigger = "immediate",
func = function()
local discard_count = #G.discard.cards
for i = 1, discard_count do --draw cards from deck
local card = G.discard.cards[i]
if not card.shattered and not card.destroyed then
draw_card(
G.discard,
G.deck,
i * 100 / discard_count,
"up",
nil,
card,
0.005,
i % 2 == 0,
nil,
math.max((21 - i) / 20, 0.7)
)
end
end
return true
end,
}))
end
--Add a hook to getID for abstracts (and to conditionally enable the check)
local getIDenhance = Card.get_id
function Card:get_id()
--Force suit to be suit X if specified in enhancement, only if not vampired
if Cryptid.cry_enhancement_has_specific_rank(self) and not self.vampired then
--Get the max value + 1, to always be the last at the list
return SMODS.Rank.max_id.value + 1
end
local vars = getIDenhance(self)
return vars
end
--override shatter function to adjust volume (it has been requested that at end of deck, abstract cards should shatter a bit quieter)
function Card:shatter(volume)
local dissolve_time = 0.7
self.shattered = true
self.dissolve = 0
self.dissolve_colours = { { 1, 1, 1, 0.8 } }
self:juice_up()
local childParts = Particles(0, 0, 0, 0, {
timer_type = "TOTAL",
timer = 0.007 * dissolve_time,
scale = 0.3,
speed = 4,
lifespan = 0.5 * dissolve_time,
attach = self,
colours = self.dissolve_colours,
fill = true,
})
G.E_MANAGER:add_event(Event({
trigger = "after",
blockable = false,
delay = 0.5 * dissolve_time,
func = function()
childParts:fade(0.15 * dissolve_time)
return true
end,
}))
G.E_MANAGER:add_event(Event({
blockable = false,
func = function()
play_sound("glass" .. math.random(1, 6), math.random() * 0.2 + 0.9, volume or 0.5)
play_sound("generic1", math.random() * 0.2 + 0.9, volume or 0.5)
return true
end,
}))
G.E_MANAGER:add_event(Event({
trigger = "ease",
blockable = false,
ref_table = self,
ref_value = "dissolve",
ease_to = 1,
delay = 0.5 * dissolve_time,
func = function(t)
return t
end,
}))
G.E_MANAGER:add_event(Event({
trigger = "after",
blockable = false,
delay = 0.55 * dissolve_time,
func = function()
self:remove()
return true
end,
}))
G.E_MANAGER:add_event(Event({
trigger = "after",
blockable = false,
delay = 0.51 * dissolve_time,
}))
end
-- Buttercup's store joker mechanic, creates a specified joker
local ccfs = create_card_for_shop
function create_card_for_shop(area)
local guaranteed_card = Card(
area.x,
area.y,
G.CARD_W,
G.CARD_H,
nil,
G.P_CENTERS.j_jolly,
{ bypass_discovery_center = true, bypass_discovery_ui = true }
)
local areas_to_check = {
shop_jokers = G.shop_jokers,
shop_vouchers = G.shop_vouchers,
shop_booster = G.shop_booster,
}
local loaded_card_data = nil
local loaded_card_pos = -1
-- check if there's a card for `area` within `next_shop_cards`,
-- then put its data in `loaded_card_data` and its index in the table in `loaded_card_pos`
if G.GAME.next_shop_cards and #G.GAME.next_shop_cards > 0 then
for i, card in ipairs(G.GAME.next_shop_cards) do
if not card.cry_from_shop then
card.cry_from_shop = "shop_jokers"
end -- failsafe :3
if areas_to_check[card.cry_from_shop] == area and loaded_card_pos == -1 then
loaded_card_data = card
loaded_card_pos = i
break
elseif areas_to_check[card.cry_from_shop] ~= G.shop_jokers then
local other_card = Card(
area.x,
area.y,
G.CARD_W,
G.CARD_H,
nil,
G.P_CENTERS.j_jolly,
{ bypass_discovery_center = true, bypass_discovery_ui = true }
)
other_card:load(card, nil)
other_card.VT.h = other_card.T.h
table.remove(G.GAME.next_shop_cards, i)
create_shop_card_ui(
other_card,
G.P_CENTERS[card.save_fields.center],
set,
areas_to_check[card.cry_from_shop]
)
areas_to_check[card.cry_from_shop]:emplace(other_card)
other_card.states.visible = false
G.E_MANAGER:add_event(Event({
delay = 0.4,
trigger = "after",
func = function()
other_card:start_materialize()
other_card:set_cost()
return true
end,
}))
other_card:set_cost()
end
end
end
if loaded_card_data then
-- guaranteed_card.T.h = G.CARD_H
guaranteed_card:load(loaded_card_data, nil)
guaranteed_card.VT.h = guaranteed_card.T.h
table.remove(G.GAME.next_shop_cards, loaded_card_pos)
create_shop_card_ui(guaranteed_card, "Joker", area)
guaranteed_card.states.visible = false
G.E_MANAGER:add_event(Event({
delay = 0.4,
trigger = "after",
func = function()
guaranteed_card:start_materialize()
guaranteed_card:set_cost()
return true
end,
}))
guaranteed_card:set_cost()
return guaranteed_card
else
guaranteed_card:remove()
end
return ccfs(area)
end
-- Again, buttercup, making sure you can savescum safely :gjumbsup:
local carsv = Card.save
function Card:save()
local saved_table = carsv(self)
if self.cry_storage then
saved_table.cry_storage = self.cry_storage:save()
end
if self.cry_from_shop then
saved_table.cry_from_shop = self.cry_from_shop
end
return saved_table
end
local carld = Card.load
function Card:load(cardTable, other_card)
carld(self, cardTable, other_card)
local storage_area_config = {
type = "play",
card_w = G.CARD_W,
}
if cardTable.cry_storage then
self.cry_storage = CardArea(self.T.x, 2, 1, 1, storage_area_config)
self.cry_storage:load(cardTable.cry_storage)
for i, card in ipairs(self.cry_storage.cards) do
card.T.orig = { w = card.T.w, h = card.T.h }
card.T.w = card.T.w * 0.5
card.T.h = card.T.h * 0.5
end
end
if cardTable.cry_from_shop then
self.cry_from_shop = cardTable.cry_from_shop
end
end
-- Attach Buttercup's stored cards card area
local carmv = Card.move
function Card:move(dt)
carmv(self, dt)
if self.cry_storage ~= nil and self.cry_storage.cards ~= nil then
self.cry_storage.config.card_limit = #self.cry_storage.cards + 1
self.cry_storage.T.w = G.CARD_W * 2
self.cry_storage.T.x = self.T.x - (G.CARD_W * 0.5)
self.cry_storage.T.y = self.T.y
self.cry_storage.VT.x = self.VT.x
self.cry_storage.VT.y = self.VT.y
end
end
--Hook for booster skip to automatically destroy and banish the rightmost Joker, regardless of eternal
local banefulSkipPenalty = G.FUNCS.skip_booster
G.FUNCS.skip_booster = function(e)
--Imported from my Epic Decision and also works in Polterworx and with unpleasant card, in the event youc an still skip with all eternals/cursed jokers
local obj = SMODS.OPENED_BOOSTER.config.center
-- local obj2 = G.P_BLINDS[G.GAME.round_resets.blind_choices.Boss]
if obj.unskippable and type(obj.unskippable) == "function" and obj:unskippable() == true then
if G.GAME.blind then
--Unplesant card will continously spam, so that will do for now without patching that; it is "unpleasant" after all;
-- play_sound('cancel', 0.8, 1)
-- local text = localize('k_nope_ex')
-- attention_text({
-- scale = 0.9, text = text, hold = 0.75, align = 'cm', offset = {x = 0,y = -2.7},major = G.play,colour = obj2.boss_colour or G.C.RED
-- })
G.GAME.blind:wiggle()
G.GAME.blind.triggered = true
end
if e and e.disable_button then
e.disable_button = nil
-- print("disble")
end
else
if SMODS.OPENED_BOOSTER.config.center.cry_baneful_punishment then
if not G.GAME.banned_keys then
G.GAME.banned_keys = {}
end -- i have no idea if this is always initialised already tbh
if not G.GAME.cry_banned_pcards then
G.GAME.cry_banished_keys = {}
end
local c = nil
c = G.jokers.cards[#G.jokers.cards] --fallback to rightmost if somehow, you skipped without disabling and its unskippable.
--Iterate backwards to get the rightmost valid (non eternal or cursed) Joker
if G.jokers and G.jokers.cards then
for i = #G.jokers.cards, 1, -1 do
if
not (
SMODS.is_eternal(G.jokers.cards[i])
or G.jokers.cards[i].config.center.rarity == "cry_cursed"
)
then
c = G.jokers.cards[i]
break
end
end
end
if c.config.center.rarity == "cry_exotic" then
check_for_unlock({ type = "what_have_you_done" })
end
G.GAME.cry_banished_keys[c.config.center.key] = true
if G.GAME.blind then
G.GAME.blind:wiggle()
G.GAME.blind.triggered = true
end
c:start_dissolve()
end
banefulSkipPenalty(e)
end
end
--Overriding the skip booster function.
G.FUNCS.can_skip_booster = function(e)
if
G.pack_cards
and not (G.GAME.STOP_USE and G.GAME.STOP_USE > 0)
and (
G.STATE == G.STATES.SMODS_BOOSTER_OPENED
or G.STATE == G.STATES.PLANET_PACK
or G.STATE == G.STATES.STANDARD_PACK
or G.STATE == G.STATES.BUFFOON_PACK
or G.hand
)
then
--if a booster is unskippable (when its unskippable conditionsa re fulfilled), unhighlight it
local obj = SMODS.OPENED_BOOSTER.config.center
if obj.unskippable and type(obj.unskippable) == "function" then
if obj:unskippable() == true then
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
else
e.config.colour = G.C.GREY
e.config.button = "skip_booster"
end
else
e.config.colour = G.C.GREY
e.config.button = "skip_booster"
end
else
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
end
end
--none stuff
local set_blindref = Blind.set_blind
function Blind:set_blind(blind, reset, silent)
set_blindref(self, blind, reset, silent)
if
G.GAME.hands["cry_None"].visible
and (G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.DRAW_TO_HAND)
and #G.hand.highlighted == 0
then
G.E_MANAGER:add_event(Event({
trigger = "after",
func = function()
Cryptid.reset_to_none()
return true
end,
}))
end
end
local end_roundref = end_round
function end_round()
if
((#G.hand.cards < 1 and #G.deck.cards < 1 and #G.play.cards < 1) or (#G.hand.cards < 1 and #G.deck.cards < 1))
and G.STATE == G.STATES.SELECTING_HAND
then
if
Cryptid.enabled("set_cry_poker_hand_stuff") == true
and not Cryptid.safe_get(G.PROFILES, G.SETTINGS.profile, "cry_none")
then
G.PROFILES[G.SETTINGS.profile].cry_none = true
end
if not Cryptid.enabled("set_cry_poker_hand_stuff") then
end_roundref()
end
else
end_roundref()
end
G.E_MANAGER:add_event(Event({
trigger = "after",
func = function()
update_hand_text({ delay = 0 }, { mult = 0, chips = 0, handname = "", level = "" })
return true
end,
}))
end
local after_ref = evaluate_play_after
function evaluate_play_after(text, disp_text, poker_hands, scoring_hand, non_loc_disp_text, percent, percent_delta)
local ret = after_ref(text, disp_text, poker_hands, scoring_hand, non_loc_disp_text, percent, percent_delta)
if G.GAME.hands["cry_None"].visible then
G.reset_to_none = true
end
return ret
end
local update_handref = Game.update_selecting_hand
function Game:update_selecting_hand(dt)
local ret = update_handref(self, dt)
if G.reset_to_none then
G.E_MANAGER:add_event(Event({
trigger = "after",
func = function()
Cryptid.reset_to_none()
return true
end,
}))
G.reset_to_none = nil
end
return ret
end
local blind_loadref = Blind.load
function Blind:load(blindTable)
blind_loadref(self, blindTable)
if
G.GAME.hands["cry_None"].visible
and self.blind_set
and (G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.DRAW_TO_HAND)
then
G.E_MANAGER:add_event(Event({
trigger = "after",
func = function()
Cryptid.reset_to_none()
return true
end,
}))
end
end
local evaluate_ref = G.FUNCS.evaluate_round
G.FUNCS.evaluate_round = function()
evaluate_ref()
update_hand_text({ delay = 0 }, { mult = 0, chips = 0, handname = "", level = "" })
G.E_MANAGER:add_event(Event({
trigger = "after",
func = function()
update_hand_text({ delay = 0 }, { mult = 0, chips = 0, handname = "", level = "" })
return true
end,
}))
end
local discard_ref = G.FUNCS.discard_cards_from_highlighted
G.FUNCS.discard_cards_from_highlighted = function(e, hook)
--Labyrinth: set current_round_discards_used to 0 for effects
G.GAME.current_round.discards_used2 = G.GAME.current_round.discards_used
if next(find_joker("cry-maze")) then
G.GAME.current_round.discards_used = 0
end
discard_ref(e, hook)
local highlighted_count = math.min(#G.hand.highlighted, G.discard.config.card_limit - #G.play.cards)
if highlighted_count <= 0 then
table.sort(G.hand.highlighted, function(a, b)
return a.T.x < b.T.x
end)
check_for_unlock({ type = "discard_custom", cards = {} })
for j = 1, #G.jokers.cards do
G.jokers.cards[j]:calculate_joker({ pre_discard = true, full_hand = G.hand.highlighted, hook = hook })
end
if not hook then
if G.GAME.modifiers.discard_cost then
ease_dollars(-G.GAME.modifiers.discard_cost)
end
ease_discard(-1)
G.GAME.current_round.discards_used = G.GAME.current_round.discards_used + 1
G.STATE = G.STATES.DRAW_TO_HAND
G.E_MANAGER:add_event(Event({
trigger = "immediate",
func = function()
G.STATE_COMPLETE = false
return true
end,
}))
end
end
if G.GAME.hands["cry_None"].visible then
G.E_MANAGER:add_event(Event({
trigger = "after",
func = function()
Cryptid.reset_to_none()
return true
end,
}))
end
--Labyrinth: return current_round_discards_used back to the amount it is supposed to be after
G.GAME.current_round.discards_used = G.GAME.current_round.discards_used2 + 1
end
local play_ref = G.FUNCS.play_cards_from_highlighted
G.FUNCS.play_cards_from_highlighted = function(e)
--Labyrinth: set current_round_hands played to 0 for effects
G.E_MANAGER:add_event(Event({
trigger = "immediate",
func = function()
G.GAME.current_round.hands_played2 = G.GAME.current_round.hands_played
if next(find_joker("cry-maze")) then
G.GAME.current_round.hands_played = 0
end
return true
end,
}))
G.GAME.before_play_buffer = true
-- None Stuff
if G.GAME.stamp_mod and not G.PROFILES[G.SETTINGS.profile].cry_none and #G.hand.highlighted == 1 then
G.PROFILES[G.SETTINGS.profile].cry_none = true
print("nonelock stuff here")
G.GAME.hands["cry_None"].visible = true
end
if G.PROFILES[G.SETTINGS.profile].cry_none and #G.hand.highlighted == 0 then
G.GAME.hands["cry_None"].visible = true
end
--Add blind context for Just before cards are played
G.GAME.blind:cry_before_play()
play_ref(e)
--Labyrinth: return current_round_hands played to the amount it is supposed to be at after
G.E_MANAGER:add_event(Event({
trigger = "immediate",
func = function()
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.1,
func = function()
G.GAME.current_round.hands_played = G.GAME.current_round.hands_played2 + 1
return true
end,
}))
return true
end,
}))
G.GAME.before_play_buffer = nil
end
local use_cardref = G.FUNCS.use_card
G.FUNCS.use_card = function(e, mute, nosave)
use_cardref(e, mute, nosave)
if G.STATE == G.STATES.SELECTING_HAND then
G.E_MANAGER:add_event(Event({
trigger = "after",
func = function()
G.hand:parse_highlighted()
return true
end,
}))
else
update_hand_text({ delay = 0 }, { mult = 0, chips = 0, handname = "", level = "" })
end
end
local emplace_ref = CardArea.emplace
function CardArea:emplace(card, location, stay_flipped)
return emplace_ref(self, card or {}, location, stay_flipped)
end
-- Added by IcyEthics: Adding a hook to the shuffle function so that there can be a context to modify randomization
-- Any card using this will most likely want to use cry_post_shuffle.
-- added cry_pre_shuffle for posterity
local o_ca_shuffle = CardArea.shuffle
function CardArea:shuffle(_seed)
SMODS.calculate_context({ cry_shuffling_area = true, cardarea = self, cry_pre_shuffle = true })
o_ca_shuffle(self, _seed)
SMODS.calculate_context({ cry_shuffling_area = true, cardarea = self, cry_post_shuffle = true })
end
local smods_four_fingers = SMODS.four_fingers
function SMODS.four_fingers()
return smods_four_fingers() - Cryptid.get_paved_joker()
end
function Cryptid.create_dummy_from_stone(rank)
local r = tostring(rank)
rank = SMODS.Ranks[r].id
return {
get_id = function()
return rank
end,
config = {
center = {},
},
ability = {},
base = {
id = rank,
value = rank >= 11 and "Queen" or "10",
},
}
end
function Cryptid.next_ranks(key, start, recurse)
key = ({
["14"] = "Ace",
["13"] = "King",
["12"] = "Queen",
["11"] = "Jack",
})[tostring(key)] or key
local rank = SMODS.Ranks[tostring(key)]
local ret = {}
if not rank or (not start and not wrap and rank.straight_edge) then
return ret
end
for _, v in ipairs(rank.next) do
ret[#ret + 1] = v
local curr = #ret
if recurse and recurse > 0 then
for i, v in pairs(Cryptid.next_ranks(ret[#ret], start, recurse - 1)) do
ret[#ret + 1] = v
end
end
end
return ret
end
local function append(t, new)
local clone = {}
for _, item in ipairs(t) do
clone[#clone + 1] = item
end
clone[#clone + 1] = new
return clone
end
function Cryptid.unique_combinations(tbl, sub, min)
sub = sub or {}
min = min or 1
local wrap, yield = coroutine.wrap, coroutine.yield
return wrap(function()
if #sub > 0 then
yield(sub) -- yield short combination.
end
if #sub < #tbl then
for i = min, #tbl do -- iterate over longer combinations.
for combo in Cryptid.unique_combinations(tbl, append(sub, tbl[i]), i + 1) do
yield(combo)
end
end
end
end)
end
get_straight_ref = get_straight
function get_straight(hand, min_length, skip, wrap)
local permutations = {}
local ranks = {}
local cards = {}
local stones = Cryptid.get_paved_joker()
if stones > 0 then
for i, v in pairs(hand) do
if v.config.center.key ~= "m_stone" then
cards[#cards + 1] = v
for i, v in pairs(Cryptid.next_ranks(v:get_id(), nil, stones)) do --this means its inaccurate in some situations like K S S S S but its fine there isnt a better way
ranks[v] = true
end
end
if v:get_id() >= 11 then
new_ranks = {
"Ace",
"King",
"Queen",
"Jack",
10,
}
for i, v in pairs(new_ranks) do
ranks[v] = true
end
end
end
local rranks = {}
for i, v in pairs(ranks) do
rranks[#rranks + 1] = i
end
for i, v in Cryptid.unique_combinations(rranks) do
if #i == stones then
permutations[#permutations + 1] = i
end
end
for i, v in ipairs(permutations) do
local actual = {}
local ranks = {}
for i, v in pairs(cards) do
actual[#actual + 1] = v
ranks[v:get_id()] = true
end
for i, p in pairs(v) do
local d = Cryptid.create_dummy_from_stone(p)
if not ranks[d:get_id()] then
actual[#actual + 1] = d
end
end
local ret = get_straight_ref(actual, min_length + stones, skip, true)
if ret and #ret > 0 then
return ret
end
end
end
return get_straight_ref(hand, min_length + stones, skip, wrap)
end
local get_prob_vars_ref = SMODS.get_probability_vars
function SMODS.get_probability_vars(trigger_obj, base_numerator, base_denominator, identifier, from_roll)
local mod = trigger_obj and trigger_obj.ability and trigger_obj.ability.cry_prob or 1
local numerator = base_numerator * mod
if trigger_obj and trigger_obj.ability and trigger_obj.ability.cry_rigged then
numerator = base_denominator
end
return get_prob_vars_ref(trigger_obj, numerator, base_denominator, identifier, from_roll)
end
local pseudorandom_probability_ref = SMODS.pseudorandom_probability
function SMODS.pseudorandom_probability(trigger_obj, seed, base_numerator, base_denominator, identifier)
local mod = trigger_obj and trigger_obj.ability and trigger_obj.ability.cry_prob or 1
local numerator = base_numerator * mod
if trigger_obj and trigger_obj.ability and trigger_obj.ability.cry_rigged then
SMODS.post_prob = SMODS.post_prob or {}
SMODS.post_prob[#SMODS.post_prob + 1] = {
pseudorandom_result = true,
result = true,
trigger_obj = trigger_obj,
numerator = base_denominator,
denominator = base_denominator,
identifier = identifier or seed,
}
return true
end
return pseudorandom_probability_ref(trigger_obj, seed, numerator, base_denominator, identifier)
end
local is_eternalref = SMODS.is_eternal
function SMODS.is_eternal(card)
if Cryptid.safe_get(card, "ability", "cry_absolute") then
return true
end
return is_eternalref(card)
end
local unlock_allref = G.FUNCS.unlock_all
G.FUNCS.unlock_all = function(e)
unlock_allref(e)
G.PROFILES[G.SETTINGS.profile].cry_none = (Cryptid.enabled("set_cry_poker_hand_stuff") == true)
end
-- Calc ante gain for The Joke (Scuffed)
local smods_calc = SMODS.calculate_context
function SMODS.calculate_context(context, return_table, no_resolve)
local aaa = smods_calc(context, return_table, no_resolve)
if context.modify_ante and context.ante_end then
if G.GAME.blind then
aaa.modify = G.GAME.blind:cry_calc_ante_gain() - 1
end
end
return aaa
end