balatro-mods/Cryptid/items/epic.lua

2653 lines
64 KiB
Lua

--[[
gameset_config = {
modest = {extra = {chips = 1}, center = {rarity = 1, blueprint_compat = false, immutable = true, no_dbl = false}},
mainline = {center = {rarity = 2, blueprint_compat = true, immutable = true, no_dbl = true}},
madness = {extra = {chips = 100}, center = {rarity = 3}},
cryptid_in_2025 = {extra = {chips = 1e308}, center = {rarity = "cry_exotic"}},
},
-- Card.get_gameset(card) ~= "modest"
--TODO Modest descriptions for
Supercell
Old Membership card
Canvas
Nostalgic Googol Play Card
One For all
--]]
-- Supercell
-- +15 Chips, +15 Mult, X2 Chips, X2 Mult, earn $3 at end of round
local supercell = {
object_type = "Joker",
name = "cry-supercell",
key = "supercell",
config = {
extra = {
stat1 = 15,
stat2 = 2,
money = 3,
},
},
dependencies = {
items = {
"set_cry_epic",
},
},
pos = { x = 5, y = 1 },
rarity = "cry_epic",
cost = 14,
order = 64,
blueprint_compat = true,
demicoloncompat = true,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
return {
key = Cryptid.gameset_loc(self, { modest = "balanced" }),
vars = {
number_format(center.ability.extra.stat1),
number_format(center.ability.extra.stat2),
number_format(center.ability.extra.money),
},
}
end,
calculate = function(self, card, context)
if context.joker_main then
if to_big(card.ability.extra.stat2) > to_big(1) then --misprint deck moment
if Card.get_gameset(card) ~= "modest" then
return {
message = localize("cry_gaming_ex"),
chip_mod = lenient_bignum(card.ability.extra.stat1),
mult_mod = lenient_bignum(card.ability.extra.stat1),
Xchip_mod = lenient_bignum(card.ability.extra.stat2),
Xmult_mod = lenient_bignum(card.ability.extra.stat2),
}
else
return {
message = localize("cry_gaming_ex"),
Xchip_mod = lenient_bignum(card.ability.extra.stat2),
Xmult_mod = lenient_bignum(card.ability.extra.stat2),
}
end
end
end
if context.forcetrigger then
ease_dollars(lenient_bignum(card.ability.extra.money))
return {
message = localize("cry_gaming_ex"),
chip_mod = lenient_bignum(card.ability.extra.stat1),
mult_mod = lenient_bignum(card.ability.extra.stat1),
Xchip_mod = lenient_bignum(card.ability.extra.stat2),
Xmult_mod = lenient_bignum(card.ability.extra.stat2),
}
end
end,
calc_dollar_bonus = function(self, card)
if to_big(card.ability.extra.money) > to_big(0) then
return lenient_bignum(card.ability.extra.money)
end
end,
add_to_deck = function(self, card, from_debuff)
if not from_debuff then
play_sound("cry_studiofromhelsinki")
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
}
-- Old Membership Card
-- +1 Chip for each member in the Cryptid Discord
local membershipcardtwo = {
object_type = "Joker",
name = "cry-membershipcardtwo",
key = "membershipcardtwo",
config = {
extra = { chips = 1 },
immutable = { chips_mod = 1 },
},
gameset_config = {
modest = {
cost = 20,
center = { rarity = 4 },
immutable = { chips_mod = 8 },
},
},
dependencies = {
items = {
"set_cry_epic",
},
},
pos = { x = 5, y = 4 },
rarity = "cry_epic",
cost = 17,
order = 50,
blueprint_compat = true,
demicoloncompat = true,
atlas = "atlasepic",
loc_vars = function(self, info_queue, card)
local aaa
if not Cryptid_config.HTTPS then
if G.localization.descriptions.Other.cry_https_disabled then
aaa = {}
localize({ type = "other", key = "cry_https_disabled", nodes = aaa, vars = {} })
aaa = aaa[1]
end
end
return {
key = Cryptid.gameset_loc(self, { modest = "balanced" }),
vars = {
number_format(card.ability.extra.chips),
number_format(
lenient_bignum(
to_big(card.ability.extra.chips)
* math.floor(Cryptid.member_count / card.ability.immutable.chips_mod)
)
),
},
main_end = aaa,
}
end,
calculate = function(self, card, context)
if (context.joker_main and to_big(card.ability.extra.chips) > to_big(0)) or context.forcetrigger then
return {
message = localize({
type = "variable",
key = "a_chips",
vars = {
number_format(
lenient_bignum(
to_big(card.ability.extra.chips)
* math.floor(Cryptid.member_count / card.ability.immutable.chips_mod)
)
),
},
}),
chip_mod = lenient_bignum(
to_big(card.ability.extra.chips)
* math.floor(Cryptid.member_count / card.ability.immutable.chips_mod)
),
}
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Linus Goof Balls",
},
code = {
"Jevonn",
},
},
}
-- Googol Play Card
-- 1 in 8 chance for X1e100 Mult
local googol_play = {
object_type = "Joker",
name = "cry-Googol Play Card",
key = "googol_play",
config = {
extra = {
Xmult = 1e100,
odds = 8,
},
},
dependencies = {
items = {
"set_cry_epic",
},
},
gameset_config = {
modest = { extra = { Xmult = 9, odds = 8 } },
},
pos = { x = 3, y = 0 },
rarity = "cry_epic",
cost = 10,
order = 14,
blueprint_compat = true,
demicoloncompat = true,
atlas = "atlasepic",
soul_pos = { x = 10, y = 0, extra = { x = 4, y = 0 } },
loc_vars = function(self, info_queue, card)
local aaa, bbb = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "Googol Play Card")
return {
vars = {
aaa,
bbb,
number_format(card.ability.extra.Xmult),
},
}
end,
calculate = function(self, card, context)
if
context.joker_main
and SMODS.pseudorandom_probability(card, "cry_googol_play", 1, card.ability.extra.odds, "Googol Play Card")
then
return {
message = localize({
type = "variable",
key = "a_xmult",
vars = { number_format(card.ability.extra.Xmult) },
}),
Xmult_mod = lenient_bignum(card.ability.extra.Xmult),
}
end
if context.forcetrigger then
return {
message = localize({
type = "variable",
key = "a_xmult",
vars = { number_format(card.ability.extra.Xmult) },
}),
Xmult_mod = lenient_bignum(card.ability.extra.Xmult),
}
end
end,
cry_credits = {
idea = {
".asdom",
},
art = {
"Linus Goof Balls",
},
code = {
"Math",
},
},
unlocked = false,
check_for_unlock = function(self, args)
if args.type == "chip_score" and to_big(args.chips) >= to_big(1e100) then
unlock_card(self)
end
if args.type == "cry_lock_all" then
lock_card(self)
end
if args.type == "cry_unlock_all" then
unlock_card(self)
end
end,
}
-- Sync Catalyst
-- Balances Chips and Mult
local sync_catalyst = {
object_type = "Joker",
name = "cry-Sync Catalyst",
key = "sync_catalyst",
dependencies = {
items = {
"set_cry_epic",
},
},
gameset_config = {
modest = {
cost = 20,
center = { rarity = 4 },
},
},
pos = { x = 5, y = 2 },
rarity = "cry_epic",
cost = 12,
order = 54,
blueprint_compat = true,
demicoloncompat = true,
immutable = true,
atlas = "atlasepic",
calculate = function(self, card, context)
if (context.joker_main and not context.debuffed_hand) or context.forcetrigger then
return {
balance = true,
}
end
end,
cry_credits = {
idea = {
"Project666",
},
art = {
"Ein13",
"George The Rat",
},
code = {
"Math",
},
},
unlocked = true,
}
-- Negative Joker
-- +4 Joker slots
local negative = {
object_type = "Joker",
name = "cry-Negative Joker",
key = "negative",
pos = { x = 1, y = 3 },
config = {
extra = {
slots = 4,
},
immutable = {
max_slots = 100,
},
},
dependencies = {
items = {
"set_cry_epic",
},
},
gameset_config = {
modest = { cost = 16 },
},
rarity = "cry_epic",
cost = 10,
order = 70,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
return { vars = { number_format(center.ability.extra.slots) } }
end,
add_to_deck = function(self, card, from_debuff)
if card.ability.extra.slots > card.ability.immutable.max_slots then
card.ability.extra.slots = card.ability.immutable.max_slots
end
G.jokers.config.card_limit = lenient_bignum(G.jokers.config.card_limit + to_big(card.ability.extra.slots))
end,
remove_from_deck = function(self, card, from_debuff)
G.jokers.config.card_limit = lenient_bignum(G.jokers.config.card_limit - to_big(card.ability.extra.slots))
end,
cry_credits = {
idea = {
"Xero01",
},
art = {
"Linus Goof Balls",
},
code = {
"Math",
},
},
}
-- Canvas
-- Retrigger all Jokers to the left once for every non-Common Joker to the right of this Joker
-- Still considering moving to Legendary - Jevonn
local canvas = {
object_type = "Joker",
name = "cry-Canvas",
key = "canvas",
immutable = true,
order = 4,
pos = { x = 2, y = 1 },
dependencies = {
items = {
"set_cry_epic",
},
},
rarity = "cry_epic",
cost = 18,
blueprint_compat = true,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
return { key = Cryptid.gameset_loc(self, { modest = "balanced" }) }
end,
calculate = function(self, card, context)
if context.retrigger_joker_check and not context.retrigger_joker then
local num_retriggers = 0
for i = 1, #G.jokers.cards do
if
card.T.x + card.T.w / 2 < G.jokers.cards[i].T.x + G.jokers.cards[i].T.w / 2
and G.jokers.cards[i].config.center.rarity ~= 1
and (G.jokers.cards[i].config.center.rarity ~= "cry_candy" or Card.get_gameset(card) ~= "modest")
then
num_retriggers = num_retriggers + 1
end
end
if
card.T
and context.other_card.T
and (card.T.x + card.T.w / 2 > context.other_card.T.x + context.other_card.T.w / 2)
then
return {
message = localize("k_again_ex"),
repetitions = Card.get_gameset(card) ~= "modest" and num_retriggers or math.min(2, num_retriggers),
card = card,
}
end
end
end,
cry_credits = {
idea = {
"Mystic Misclick",
},
art = {
"Lil. Mr. Slipstream",
},
code = {
"Math",
},
},
}
-- ERROR
-- Displays a glitched message
-- While in shop, all cards are Glitched (fallback: Foil)
-- After a random number of rounds, duplicates all Jokers when sold
-- The glitched message can tell you what the next card rolled in the shop will be (similar to the Misprint easter egg)
local error_joker = {
object_type = "Joker",
name = "cry-Error",
key = "error",
pos = { x = 4, y = 2 },
config = {
extra = {
sell_rounds = 0,
active = false,
},
},
dependencies = {
items = {
-- Note: This currently does not have a dependency on Glitched because there's a fallback.
-- However I think this should be changed later...
"set_cry_epic",
},
},
immutable = true,
rarity = "cry_epic",
cost = 1,
order = 72,
blueprint_compat = false,
eternal_compat = false,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
local ok, ret = pcall(Cryptid.predict_card_for_shop)
if Cryptid.safe_get(G.GAME, "pseudorandom") and G.STAGE == G.STAGES.RUN and ok then
cry_error_msgs[#cry_error_msgs].string = "%%" .. ret
else
cry_error_msgs[#cry_error_msgs].string = "%%J6"
end
return {
main_start = {
{
n = G.UIT.O,
config = {
object = DynaText({
string = cry_error_operators,
colours = { G.C.DARK_EDITION },
pop_in_rate = 9999999,
silent = true,
random_element = true,
pop_delay = 0.30,
scale = 0.32,
min_cycle_time = 0,
}),
},
},
{
n = G.UIT.O,
config = {
object = DynaText({
string = cry_error_numbers,
colours = { G.C.DARK_EDITION },
pop_in_rate = 9999999,
silent = true,
random_element = true,
pop_delay = 0.33,
scale = 0.32,
min_cycle_time = 0,
}),
},
},
{
n = G.UIT.O,
config = {
object = DynaText({
string = cry_error_msgs,
colours = { G.C.UI.TEXT_DARK },
pop_in_rate = 9999999,
silent = true,
random_element = true,
pop_delay = 0.4011,
scale = 0.32,
min_cycle_time = 0,
}),
},
},
},
}
end,
add_to_deck = function(self, card, from_debuff)
if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_edition_from_deck then
G.GAME.modifiers.cry_force_edition_from_deck = G.GAME.modifiers.cry_force_edition
elseif not G.GAME.modifiers.cry_force_edition_from_deck then
if G.P_CENTERS.e_cry_glitched then
G.GAME.modifiers.cry_force_edition = "cry_glitched"
else
G.GAME.modifiers.cry_force_edition = "foil"
end
G.GAME.modifiers.cry_force_edition_from_deck = "Nope!"
end
end,
remove_from_deck = function(self, card, from_debuff)
if G.GAME.modifiers.cry_force_edition_from_deck ~= "Nope!" then
G.GAME.modifiers.cry_force_edition = G.GAME.modifiers.cry_force_edition_from_deck
else
G.GAME.modifiers.cry_force_edition = nil
end
end,
calculate = function(self, card, context)
if
context.end_of_round
and not context.blueprint
and not context.repetition
and not context.individual
and not card.ability.extra.active
then
if card.ability.extra.sell_rounds == 0 then
card.ability.extra.sell_rounds = math.floor(pseudorandom(pseudoseed("cry_error")) * 10 + 1)
end
card.ability.extra.sell_rounds = card.ability.extra.sell_rounds - 1
if card.ability.extra.sell_rounds == 0 then
card.ability.extra.active = true
local eval = function(card)
return not card.REMOVED
end
juice_card_until(card, eval, true)
else
return {
message = "???",
colour = G.C.BLACK,
}
end
end
if
context.selling_self
and card.ability.extra.active
and not context.retrigger_joker
and not context.blueprint
then
local eval = function(card)
return (Cryptid.safe_get(card, "ability", "loyalty_remaining") == 0) and not G.RESET_JIGGLES
end
juice_card_until(card, eval, true)
local jokers = {}
for i = 1, #G.jokers.cards do
if G.jokers.cards[i].ability.name ~= "cry-Error" then
jokers[#jokers + 1] = G.jokers.cards[i]
end
end
for i = 1, #jokers do
local card = copy_card(jokers[i])
card:add_to_deck()
G.jokers:emplace(card)
end
return nil, true
end
end,
cry_credits = {
idea = {
"Coronacht",
"Fetch",
},
art = {
"mold spores",
},
code = {
"Math",
},
},
init = function(self)
cry_error_operators = { "+", "-", "X", "/", "^", "=", ">", "<", "m" }
cry_error_numbers = {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"69",
"404",
"420",
"-1",
"0.5",
"m",
"nan",
"inf",
"nil",
"pi",
"1e9",
"???",
"114",
"leet",
"666",
"eee6",
"21",
"365",
"2024",
}
cry_error_msgs = {
{ string = "rand()", colour = G.C.RARITY["cry_exotic"] },
{ string = "m", colour = G.C.UI.TEXT_DARK },
{ string = "Chips", colour = G.C.CHIPS },
{ string = "Mult", colour = G.C.MULT },
{ string = "Jokers", colour = G.C.FILTER },
{ string = "dollars", colour = G.C.FILTER },
{ string = "hands", colour = G.C.FILTER },
{ string = "slots", colour = G.C.FILTER },
{ string = "Antes", colour = G.C.FILTER },
{ string = "ERROR", colour = G.C.UI.TEXT_INACTIVE },
{ string = "Tarots", colour = G.C.SECONDARY_SET.Tarot },
{ string = "Planets", colour = G.C.SECONDARY_SET.Planet },
{ string = "Codes", colour = G.C.SECONDARY_SET.Code },
{ string = "Specls", colour = G.C.SECONDARY_SET.Spectral },
{ string = "Jolly", colour = G.C.CRY_JOLLY },
{ string = "Tags", colour = G.C.RED },
{ string = "Cryptids", colour = G.C.SECONDARY_SET.Spectral },
{ string = "Glop", colour = G.C.CRY_ALTGREENGRADIENT },
{ string = "%%ERROR", colour = G.C.CRY_ASCENDANT }, --temp string, this will be modified
}
function Cryptid.predict_pseudoseed(key)
local M = G.GAME.pseudorandom[key] or pseudohash(key .. (G.GAME.pseudorandom.seed or ""))
local m = math.abs(tonumber(string.format("%.13f", (2.134453429141 + M * 1.72431234) % 1)))
return (m + (G.GAME.pseudorandom.hashed_seed or 0)) / 2
end
function Cryptid.predict_card_for_shop()
local total_rate = G.GAME.joker_rate + G.GAME.playing_card_rate
for _, v in ipairs(SMODS.ConsumableType.obj_buffer) do
total_rate = total_rate + (G.GAME[v:lower() .. "_rate"] or 0)
end
local polled_rate = pseudorandom(Cryptid.predict_pseudoseed("cdt" .. G.GAME.round_resets.ante)) * total_rate
local check_rate = 0
-- need to preserve order to leave RNG unchanged
local rates =
{
{ type = "Joker", val = G.GAME.joker_rate },
{ type = "Tarot", val = G.GAME.tarot_rate },
{ type = "Planet", val = G.GAME.planet_rate },
{
type = (G.GAME.used_vouchers["v_illusion"] and pseudorandom(
Cryptid.predict_pseudoseed("illusion")
) > 0.6) and "Enhanced" or "Base",
val = G.GAME.playing_card_rate,
},
{ type = "Spectral", val = G.GAME.spectral_rate },
}
for _, v in ipairs(SMODS.ConsumableType.obj_buffer) do
if not (v == "Tarot" or v == "Planet" or v == "Spectral") then
table.insert(rates, { type = v, val = G.GAME[v:lower() .. "_rate"] })
end
end
for _, v in ipairs(rates) do
if polled_rate > check_rate and polled_rate <= check_rate + v.val then
local c = create_card(v.type, "ERROR", nil, nil, nil, nil, nil, "sho")
if not c.set then
return v.type:sub(1, 1) .. c.suit:sub(1, 1) .. c.value:sub(1, 2)
else
for i = 1, #G.P_CENTER_POOLS[c.set] do
if G.P_CENTER_POOLS[c.set][i].key == c.key then
return c.set:sub(1, 1) .. i
end
end
end
end
check_rate = check_rate + v.val
end
end
end,
}
-- m
-- This Joker gains X13 Mult when Jolly Joker is sold
local m = {
object_type = "Joker",
discovered = true,
name = "cry-m",
key = "m",
pos = { x = 3, y = 1 },
config = {
extra = {
extra = 13,
x_mult = 1,
},
},
gameset_config = {
modest = {
extra = {
extra = 1,
x_mult = 1,
},
},
},
dependencies = {
items = {
"set_cry_epic",
"set_cry_meme",
-- Note: This isn't in the M Joker content set due to being added separately
},
},
pools = { ["Meme"] = true, ["M"] = true },
rarity = "cry_epic",
order = 1,
cost = 13,
effect = "M Joker",
perishable_compat = false,
blueprint_compat = true,
demicoloncompat = true,
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = G.P_CENTERS.j_jolly
return {
vars = {
number_format(center.ability.extra.extra),
number_format(center.ability.extra.x_mult),
},
}
end,
atlas = "atlasepic",
calculate = function(self, card, context)
if context.joker_main and (to_big(card.ability.extra.x_mult) > to_big(1)) then
return {
message = localize({
type = "variable",
key = "a_xmult",
vars = { number_format(card.ability.extra.x_mult) },
}),
Xmult_mod = card.ability.extra.x_mult,
}
end
if context.selling_card and context.card:is_jolly() and not context.blueprint then
SMODS.scale_card(card, {
ref_table = card.ability.extra,
ref_value = "x_mult",
scalar_value = "extra",
message_key = "a_xmult",
message_colour = G.C.RED,
})
return nil, true
end
if context.forcetrigger then
SMODS.scale_card(card, {
ref_table = card.ability.extra,
ref_value = "x_mult",
scalar_value = "extra",
message_key = "a_xmult",
message_colour = G.C.RED,
})
return {
Xmult_mod = card.ability.extra.x_mult,
}
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Math",
},
},
}
-- M
-- Create a Negative Jolly Joker when Blind is selected
local M = {
object_type = "Joker",
name = "cry-M",
key = "M",
pos = { x = 0, y = 0 },
order = 250,
dependencies = {
items = {
"set_cry_epic",
--Note: This isn't in the M Joker content set due to being added separately
},
},
rarity = "cry_epic",
effect = "M Joker",
cost = 13,
immutable = true,
blueprint_compat = true,
demicoloncompat = true,
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = G.P_CENTERS.j_jolly
if not center.edition or (center.edition and not center.edition.negative) then
info_queue[#info_queue + 1] = G.P_CENTERS.e_negative
end
end,
atlas = "atlasepic",
calculate = function(self, card, context)
if (context.setting_blind and not (context.blueprint_card or self).getting_sliced) or context.forcetrigger then
local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly")
card:set_edition({
negative = true,
})
card:add_to_deck()
G.jokers:emplace(card)
return nil, true
end
end,
pools = { ["M"] = true },
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Math",
},
},
}
-- Boredom
-- 1 in 2 chance to retrigger each Joker or played card
local boredom = {
object_type = "Joker",
name = "cry-Boredom",
key = "boredom",
pos = { x = 1, y = 0 },
config = { extra = { odds = 2 } },
dependencies = {
items = {
"set_cry_epic",
"set_cry_meme",
},
},
gameset_config = {
modest = { extra = { odds = 3 } },
},
pools = { ["Meme"] = true },
rarity = "cry_epic",
order = 32,
cost = 14,
blueprint_compat = true,
loc_vars = function(self, info_queue, card)
local num, denom = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "Boredom")
return {
vars = {
num,
denom,
},
}
end,
atlas = "atlasepic",
calculate = function(self, card, context)
if
context.retrigger_joker_check
and not context.retrigger_joker
and not (context.other_card.ability and context.other_card.ability.name == "cry-Boredom")
then
if SMODS.pseudorandom_probability(card, "cry_boredom_joker", 1, card.ability.extra.odds, "Boredom") then
return {
message = localize("k_again_ex"),
repetitions = 1,
card = card,
}
else
return nil, true
end
end
if
context.repetition
and context.cardarea == G.play
and SMODS.pseudorandom_probability(card, "cry_boredom_joker", 1, card.ability.extra.odds, "Boredom")
then
return {
message = localize("k_again_ex"),
repetitions = 1,
card = card,
}
end
end,
cry_credits = {
idea = {
"Math",
},
art = {
"Saturn",
},
code = {
"Math",
},
},
}
-- Number Blocks
-- Earn $1 at end of round; Increase payout by $1 for each [rank] held in hand (changes every round)
local number_blocks = {
object_type = "Joker",
name = "cry-Number Blocks",
key = "number_blocks",
config = {
extra = {
money_mod = 1,
money = 1,
},
},
dependencies = {
items = {
"set_cry_epic",
},
},
pos = { x = 0, y = 2 },
rarity = "cry_epic",
cost = 14,
order = 12,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
return {
vars = {
number_format(center.ability.extra.money),
number_format(center.ability.extra.money_mod),
localize(Cryptid.safe_get(G.GAME, "current_round", "cry_nb_card", "rank") or "Ace", "ranks"),
},
}
end,
calculate = function(self, card, context)
if
context.individual
and not context.end_of_round
and context.cardarea == G.hand
and not context.blueprint
and not context.before
and not context.after
and context.other_card:get_id() == G.GAME.current_round.cry_nb_card.id
then
if context.other_card.debuff then
return {
message = localize("k_debuffed"),
colour = G.C.RED,
card = card,
}
else
SMODS.scale_card(card, {
ref_table = card.ability.extra,
ref_value = "money",
scalar_value = "money_mod",
})
return nil, true
end
end
end,
calc_dollar_bonus = function(self, card)
if to_big(card.ability.extra.money) > to_big(0) then
return lenient_bignum(card.ability.extra.money)
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"George The Rat",
},
code = {
"Math",
},
},
}
-- Double Scale
-- Scaling jokers scale quadratically
-- Most of the code for this lies in Card:cry_double_scale_calc in lib/calculate.lua
local double_scale = {
object_type = "Joker",
name = "cry-Double Scale",
key = "Double Scale",
pos = { x = 0, y = 3 },
dependencies = {
items = {
"set_cry_epic",
},
},
gameset_config = {
modest = {
cost = 20,
center = { rarity = 4 },
},
exp_modest = { cost = 11 },
},
extra_gamesets = { "exp_modest" },
loc_vars = function(self, info_queue, center)
return { key = Cryptid.gameset_loc(self, { exp_modest = "modest" }) }
end,
order = 6,
rarity = "cry_epic",
cost = 18,
immutable = true,
atlas = "atlasepic",
calc_scaling = function(self, card, other, current_scaling, current_scalar, args)
if not G.GAME.cryptid_base_scales then
G.GAME.cryptid_base_scales = {}
end
if not G.GAME.cryptid_base_scales[other.config.center.key] then
G.GAME.cryptid_base_scales[other.config.center.key] = {}
end
if not G.GAME.cryptid_base_scales[other.config.center.key][args.scalar_value] then
G.GAME.cryptid_base_scales[other.config.center.key][args.scalar_value] = current_scalar
end
local true_base = G.GAME.cryptid_base_scales[other.config.center.key][args.scalar_value]
local orig_scale_scale = current_scaling
if Cryptid.gameset(self) == "exp_modest" then
return {
scalar_value = lenient_bignum(to_big(true_base) * 2),
message = localize("k_upgrade_ex"),
}
end
args.scalar_table[args.scalar_value] = new_scale
return {
message = localize("k_upgrade_ex"),
}
end,
cry_credits = {
idea = {
"Mystic Misclick",
},
art = {
"lord.ruby",
},
code = {
"Math",
"Mathguy",
},
},
}
-- Nostalgic Candy
-- Sell this card to permanently gain +3 hand size
local oldcandy = {
object_type = "Joker",
name = "cry_oldcandy",
key = "oldcandy",
pos = { x = 4, y = 1 },
order = 43,
config = {
extra = { hand_size = 3 },
immutable = { max_hand_size_mod = 1000 },
},
gameset_config = {
modest = { extra = { hand_size = 1 } },
},
dependencies = {
items = {
"set_cry_epic",
},
},
pools = { ["Food"] = true },
loc_vars = function(self, info_queue, center)
return {
vars = {
math.min(
center.ability.immutable.max_hand_size_mod,
math.max(1, math.floor(center.ability.extra.hand_size))
),
},
}
end,
rarity = "cry_epic",
cost = 9,
blueprint_compat = true,
eternal_compat = false,
demicoloncompat = true,
atlas = "atlasepic",
calculate = function(self, card, context)
if context.selling_self or context.forcetrigger then
G.hand:change_size(
math.min(
card.ability.immutable.max_hand_size_mod,
math.max(1, math.floor(card.ability.extra.hand_size))
)
)
return nil, true
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
}
-- Circus
-- Rare/Epic/Legendary/Exotic Jokers give X2/3/4/20 Mult
local circus = {
object_type = "Joker",
name = "cry-circus",
key = "circus",
pos = { x = 4, y = 4 },
config = {},
dependencies = {
items = {
"set_cry_epic",
},
},
atlas = "atlasepic",
order = 33,
loc_vars = function(self, info_queue, center)
local extra_rarities = {}
local mults = {}
Cryptid.circus_rarities["exotic"].colour = G.C.CRY_EXOTIC
for i, v in pairs(Cryptid.circus_rarities) do
extra_rarities[#extra_rarities + 1] = v
end
table.sort(extra_rarities, function(a, b)
return a.order < b.order
end)
mults.colours = {}
for i, v in pairs(extra_rarities) do
if not v.hidden then
mults[#mults + 1] = number_format(center.ability.extra[tostring(v.rarity) .. "_mult_mod"])
mults.colours[#mults.colours + 1] = v.colour
end
end
return {
vars = mults,
}
end,
set_ability = function(self, center)
local extra_rarities = {}
local mults = {}
local mult_numbers = {}
for i, v in pairs(Cryptid.circus_rarities) do
extra_rarities[#extra_rarities + 1] = v
end
table.sort(extra_rarities, function(a, b)
return a.order < b.order
end)
for i, v in pairs(extra_rarities) do
mult_numbers[tostring(v.rarity) .. "_mult_mod"] = v.base_mult
mults[v.rarity] = tostring(v.rarity) .. "_mult_mod"
end
if not self.config.extra then
self.config.extra = mult_numbers
center.ability.extra = mult_numbers
self.config.immutable = {
rarity_map = mults,
}
center.ability.immutable = {
rarity_map = mults,
}
end
end,
rarity = "cry_epic",
cost = 16,
blueprint_compat = true,
demicoloncompat = true,
calculate = function(self, card, context)
if context.other_joker and card ~= context.other_joker then
local mod_key = card.ability.immutable.rarity_map[context.other_joker.config.center.rarity]
if mod_key and card.ability.extra[mod_key] and to_big(card.ability.extra[mod_key]) > to_big(1) then
if not Talisman.config_file.disable_anims then
G.E_MANAGER:add_event(Event({
func = function()
context.other_joker:juice_up(0.5, 0.5)
return true
end,
}))
end
local xmult = card.ability.extra[mod_key]
return {
message = localize({
type = "variable",
key = "a_xmult",
vars = { number_format(xmult) },
}),
Xmult_mod = xmult,
}
end
end
if context.forcetrigger then
local total = 1
for i, v in pairs(card.ability.extra) do
if type(v) == "number" or (type(v) == "table" and v.tetrate) then
total = total * v
end
end
return {
Xmult_mod = total,
}
end
end,
cry_credits = {
idea = {
"Ein13",
},
art = {
"Ein13",
},
code = {
"Jevonn",
"BobJoe400",
},
},
}
-- Caramel
-- Each played card gives X1.75 Mult when scored for the next 11 rounds
local caramel = {
object_type = "Joker",
name = "cry-caramel",
key = "caramel",
config = {
extra = {
x_mult = 1.75,
rounds_remaining = 11,
},
},
dependencies = {
items = {
"set_cry_epic",
},
},
pos = { x = 0, y = 1 },
rarity = "cry_epic",
cost = 12,
order = 106,
gameset_config = {
modest = {
extra = {
x_mult = 1.5,
rounds_remaining = 6,
},
},
},
pools = { ["Food"] = true },
blueprint_compat = true,
eternal_compat = false,
demicoloncompat = true,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
return {
vars = {
number_format(center.ability.extra.x_mult),
number_format(center.ability.extra.rounds_remaining),
},
}
end,
calculate = function(self, card, context)
if context.individual then
if context.cardarea == G.play then
return {
x_mult = lenient_bignum(card.ability.extra.x_mult),
colour = G.C.RED,
card = card,
}
end
end
if
context.end_of_round
and not context.blueprint
and not context.individual
and not context.repetition
and not context.retrigger_joker
then
card.ability.extra.rounds_remaining = lenient_bignum(to_big(card.ability.extra.rounds_remaining) - 1)
if to_big(card.ability.extra.rounds_remaining) > to_big(0) then
return {
message = { localize("cry_minus_round") },
colour = G.C.FILTER,
}
else
G.E_MANAGER:add_event(Event({
func = function()
play_sound("tarot1")
card.T.r = -0.2
card:juice_up(0.3, 0.4)
card.states.drag.is = true
card.children.center.pinch.x = true
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.3,
blockable = false,
func = function()
G.jokers:remove_card(card)
card:remove()
card = nil
return true
end,
}))
return true
end,
}))
return {
message = localize("k_eaten_ex"),
colour = G.C.FILTER,
}
end
end
if context.forcetrigger then
card.ability.extra.rounds_remaining = lenient_bignum(to_big(card.ability.extra.rounds_remaining) - 1)
card.ability.extra.rounds_remaining = math.max(card.ability.extra.rounds_remaining, 0)
return {
Xmult_mod = lenient_bignum(card.ability.extra.x_mult),
colour = G.C.RED,
card = card,
}
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
}
-- Sob
-- you cannot run... you cannot hide... you cannot escape...
-- Creates a Negative Absolute Eternal Obelisk when added
-- Creates Obelisks (if room) when doing just about anything
-- Still planning on making one more change to this later - Jevonn
local curse_sob = {
object_type = "Joker",
name = "cry_curse_sob",
key = "curse_sob",
pos = { x = 1, y = 1 },
pools = { ["Meme"] = true },
dependencies = {
items = {
"set_cry_epic",
"set_cry_meme",
},
},
gameset_config = {
modest = {
cost = 20,
center = { rarity = 4 },
},
},
rarity = "cry_epic",
cost = 9,
order = 82,
immutable = true,
perishable_compat = true,
demicoloncompat = true,
atlas = "atlasepic",
calculate = function(self, card, context)
if
context.selling_card
and context.card.ability.name == "Obelisk"
and not context.retrigger_joker
and not context.blueprint
then
return {}
elseif
( -- Compacting all the elseifs into one block for space and readability also maintablity
context.selling_self
or context.discard
or context.reroll_shop --Yes
or context.buying_card
or context.skip_blind
or context.using_consumeable
or context.selling_card
or context.setting_blind
or context.skipping_booster
or context.open_booster
or context.forcetrigger
)
and (#G.jokers.cards + G.GAME.joker_buffer < (context.selling_self and (G.jokers.config.card_limit + 1) or G.jokers.config.card_limit))
or context.forcetrigger and not context.retrigger_joker and not context.blueprint
then
local createjoker = math.min(1, G.jokers.config.card_limit - (#G.jokers.cards + G.GAME.joker_buffer))
G.GAME.joker_buffer = G.GAME.joker_buffer + createjoker
local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_obelisk")
card:add_to_deck()
G.jokers:emplace(card)
G.GAME.joker_buffer = 0
return {
card_eval_status_text(card, "extra", nil, nil, nil, {
message = localize("cry_curse_ex"),
colour = G.C.FILTER,
}),
}
end
end,
add_to_deck = function(self, card, from_debuff)
if not from_debuff then
local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_obelisk")
card:set_edition("e_negative", true, nil, true)
card.ability.cry_absolute = true
card:add_to_deck()
G.jokers:emplace(card)
return {
card_eval_status_text(card, "extra", nil, nil, nil, {
message = localize("cry_curse_ex"),
colour = G.C.DARK_EDITION,
}),
}
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
unlocked = false,
check_for_unlock = function(self, args)
if Cryptid.safe_get(G, "jokers") then
for i = 1, #G.jokers.cards do
if G.jokers.cards[i].config.center.key == "j_obelisk" and SMODS.is_eternal(G.jokers.cards[i]) then
unlock_card(self)
end
end
end
if args.type == "cry_lock_all" then
lock_card(self)
end
if args.type == "cry_unlock_all" then
unlock_card(self)
end
end,
}
-- Bonus Joker
-- 1 in 8 chance for each played Bonus Card to increase Joker or Consumable slots by 1 when scored
-- Works twice per round
local bonusjoker = {
object_type = "Joker",
name = "cry-Bonus Joker",
key = "bonusjoker",
pos = { x = 3, y = 2 },
config = {
extra = {
odds = 8,
add = 1,
},
immutable = { check = 0, max = 100 },
},
dependencies = {
items = {
"set_cry_epic",
},
},
immutable = false,
rarity = "cry_epic",
cost = 11,
order = 75,
blueprint_compat = true,
demicoloncompat = true,
enhancement_gate = "m_bonus",
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.m_bonus
local aaa, bbb = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "Bonus Joker")
return {
vars = {
aaa,
bbb,
number_format(math.min(card.ability.extra.add, card.ability.immutable.max)),
},
}
end,
atlas = "atlasepic",
calculate = function(self, card, context)
if context.individual and context.cardarea == G.play then
if SMODS.has_enhancement(context.other_card, "m_bonus") then
if
SMODS.pseudorandom_probability(card, "bonusjoker", 1, card.ability.extra.odds, "Bonus Joker")
and card.ability.immutable.check < 2
and not context.retrigger_joker
then
local option = pseudorandom_element({ 1, 2 }, pseudoseed("bonusjoker"))
if option == 1 then
if not context.blueprint then
card.ability.immutable.check = lenient_bignum(card.ability.immutable.check + 1)
end
G.jokers.config.card_limit = lenient_bignum(
G.jokers.config.card_limit + math.min(card.ability.extra.add, card.ability.immutable.max)
)
else
if not context.blueprint then
card.ability.immutable.check = lenient_bignum(card.ability.immutable.check + 1)
end
G.consumeables.config.card_limit = lenient_bignum(
G.consumeables.config.card_limit
+ to_big(math.min(card.ability.extra.add, card.ability.immutable.max))
)
end
return {
extra = { focus = card, message = localize("k_upgrade_ex") },
card = card,
colour = G.C.MONEY,
}
end
end
end
if
context.end_of_round
and card.ability.immutable.check ~= 0
and not context.blueprint
and not context.retrigger_joker
and not context.individual
and not context.repetition
then
card.ability.immutable.check = 0
return {
message = localize("k_reset"),
card = card,
}
end
if context.forcetrigger then
local option = pseudorandom_element({ 1, 2 }, pseudoseed("bonusjoker"))
if option == 1 then
if not context.blueprint then
card.ability.immutable.check = lenient_bignum(card.ability.immutable.check + 1)
end
G.jokers.config.card_limit = lenient_bignum(
G.jokers.config.card_limit + cmath.min(card.ability.extra.add, card.ability.immutable.max)
)
else
if not context.blueprint then
card.ability.immutable.check = lenient_bignum(card.ability.immutable.check + 1)
end
G.consumeables.config.card_limit = lenient_bignum(
G.consumeables.config.card_limit
+ to_big(math.min(card.ability.extra.add, card.ability.immutable.max))
)
end
return {
extra = { focus = card, message = localize("k_upgrade_ex") },
card = card,
colour = G.C.MONEY,
}
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
}
-- Mult Joker
-- 1 in 3 chance for each played Mult Card to create a Cryptid when scored (Must have room)
local multjoker = {
object_type = "Joker",
name = "cry-Mult Joker",
key = "multjoker",
pos = { x = 2, y = 3 },
config = { extra = { odds = 3 } },
dependencies = {
items = {
"set_cry_epic",
},
},
rarity = "cry_epic",
order = 99,
cost = 11,
blueprint_compat = true,
demicoloncompat = true,
enhancement_gate = "m_mult",
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.m_mult
info_queue[#info_queue + 1] = G.P_CENTERS.c_cryptid
return {
vars = { SMODS.get_probability_vars(card, 1, card.ability.extra.odds, "Mult Joker") },
}
end,
atlas = "atlasepic",
calculate = function(self, card, context)
if context.individual and context.cardarea == G.play then
if
SMODS.has_enhancement(context.other_card, "m_mult")
and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit
then
if SMODS.pseudorandom_probability(card, "multjoker", 1, card.ability.extra.odds, "Mult Joker") then
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
G.E_MANAGER:add_event(Event({
func = function()
local new_card =
create_card("Spectral", G.consumeables, nil, nil, nil, nil, "c_cryptid", "multjoker")
new_card:add_to_deck()
G.consumeables:emplace(new_card)
G.GAME.consumeable_buffer = 0
return true
end,
}))
card_eval_status_text(
context.blueprint_card or card,
"extra",
nil,
nil,
nil,
{ message = localize("cry_plus_cryptid"), colour = G.C.SECONDARY_SET.Spectral }
)
return nil, true
end
end
end
if context.forcetrigger then
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
G.E_MANAGER:add_event(Event({
func = function()
local new_card =
create_card("Spectral", G.consumeables, nil, nil, nil, nil, "c_cryptid", "multjoker")
new_card:add_to_deck()
G.consumeables:emplace(new_card)
G.GAME.consumeable_buffer = 0
return true
end,
}))
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
}
-- Gold Joker
-- Earn +2% at end of round when a Gold Card is scored
local goldjoker = {
object_type = "Joker",
name = "cry-gold Joker",
key = "goldjoker",
config = {
extra = {
percent_mod = 2,
percent = 0,
},
},
dependencies = {
items = {
"set_cry_epic",
},
},
pos = { x = 0, y = 4 },
rarity = "cry_epic",
cost = 14,
order = 81,
enhancement_gate = "m_gold",
perishable_compat = false,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = G.P_CENTERS.m_gold
return {
vars = {
number_format(center.ability.extra.percent),
number_format(center.ability.extra.percent_mod),
},
}
end,
calculate = function(self, card, context)
if context.cardarea == G.play and context.individual and not context.blueprint then
if SMODS.has_enhancement(context.other_card, "m_gold") then
SMODS.scale_card(card, {
ref_table = card.ability.extra,
ref_value = "percent",
scalar_value = "percent_mod",
})
end
end
if context.individual and context.cardarea == G.play then
if SMODS.has_enhancement(context.other_card, "m_gold") then
SMODS.scale_card(card, {
ref_table = card.ability.extra,
ref_value = "percent",
scalar_value = "percent_mod",
})
end
end
end,
calc_dollar_bonus = function(self, card)
local bonus =
lenient_bignum(math.max(0, math.floor(0.01 * to_big(card.ability.extra.percent) * (G.GAME.dollars or 0))))
if to_big(bonus) > to_big(0) then
return bonus
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
}
-- Nostalgic Googol Play Card
-- Sell this card to create 2 copies of the leftmost Joker
-- Still needs updated description
local altgoogol = {
object_type = "Joker",
name = "cry-altgoogol",
key = "altgoogol",
pos = { x = 4, y = 3 },
dependencies = {
items = {
"set_cry_epic",
},
},
immutable = true,
rarity = "cry_epic",
cost = 10,
order = 60,
blueprint_compat = false,
eternal_compat = false,
demicoloncompat = true,
atlas = "atlasepic",
soul_pos = { x = 10, y = 0, extra = { x = 5, y = 3 } },
config = { copies = 2 },
gameset_config = {
modest = {
cost = 15,
copies = 1,
},
mainline = { copies = 2 },
madness = {
center = { blueprint_compat = true },
copies = 2,
},
},
loc_vars = function(self, info_queue, center)
return { key = Cryptid.gameset_loc(self, { modest = "balanced" }), vars = { center.ability.copies } }
end,
calculate = function(self, card, context)
local gameset = Card.get_gameset(card)
if
(context.selling_self and not context.retrigger_joker and (gameset == "madness" or not context.blueprint))
or context.forcetrigger
then
local jokers = {}
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] ~= card then
jokers[#jokers + 1] = G.jokers.cards[i]
end
end
if #jokers > 0 then
if not gameset == "modest" or #G.jokers.cards <= G.jokers.config.card_limit then
if G.jokers.cards[1].ability.name ~= "cry-altgoogol" then
G.E_MANAGER:add_event(Event({
func = function()
for i = 1, card.ability.copies do
local chosen_joker = G.jokers.cards[1]
local card = copy_card(
chosen_joker,
nil,
nil,
nil,
(
gameset == "modest"
and (Cryptid.safe_get(chosen_joker, "edition", "negative"))
or nil
)
)
card:add_to_deck()
G.jokers:emplace(card)
end
return true
end,
}))
card_eval_status_text(context.blueprint_card or card, "extra", nil, nil, nil, {
message = localize("k_duplicated_ex"),
colour = G.C.RARITY.cry_epic,
})
return nil, true
else
card_eval_status_text(context.blueprint_card or card, "extra", nil, nil, nil, {
message = localize("k_nope_ex"),
colour = G.C.RARITY.cry_epic,
})
return nil, true
end
else
card_eval_status_text(context.blueprint_card or card, "extra", nil, nil, nil, {
message = localize("k_no_room_ex"),
colour = G.C.RARITY.cry_epic,
})
return nil, true
end
else
card_eval_status_text(context.blueprint_card or card, "extra", nil, nil, nil, {
message = localize("k_no_other_jokers"),
colour = G.C.RARITY.cry_epic,
})
return nil, true
end
end
end,
cry_credits = {
idea = {
"Jevonn",
},
art = {
"Jevonn",
},
code = {
"Jevonn",
},
},
}
-- One For All
-- +1 Joker slot, Booster Pack slot, hand size, consumable slot, shop slot
local soccer = {
object_type = "Joker",
name = "cry-soccer",
key = "soccer",
pos = { x = 1, y = 4 },
config = { extra = { holygrail = 1 } },
dependencies = {
items = {
"set_cry_epic",
},
},
immutable = true, -- i swear i changed this... whatever
rarity = "cry_epic",
order = 58,
cost = 20,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
return { key = Cryptid.gameset_loc(self, { modest = "balanced" }), vars = { center.ability.extra.holygrail } }
end,
add_to_deck = function(self, card, from_debuff)
card.ability.extra.holygrail = math.floor(card.ability.extra.holygrail)
local mod = card.ability.extra.holygrail
G.jokers.config.card_limit = G.jokers.config.card_limit + ((Card.get_gameset(card) == "modest") and 0 or mod)
G.consumeables.config.card_limit = G.consumeables.config.card_limit + mod
G.hand:change_size(mod)
SMODS.change_booster_limit(mod)
SMODS.change_voucher_limit(mod)
end,
remove_from_deck = function(self, card, from_debuff)
card.ability.extra.holygrail = math.floor(card.ability.extra.holygrail)
local mod = card.ability.extra.holygrail
G.jokers.config.card_limit = G.jokers.config.card_limit + ((Card.get_gameset(card) == "modest") and 0 or -mod)
G.consumeables.config.card_limit = G.consumeables.config.card_limit - mod
G.hand:change_size(-mod)
SMODS.change_booster_limit(-mod)
SMODS.change_voucher_limit(-mod)
end,
cry_credits = {
idea = {
"Mjiojio",
},
art = {
"Ein13",
"George The Rat",
},
code = {
"Jevonn",
},
},
unlocked = false,
check_for_unlock = function(self, args)
if args.type == "win" then
for k, v in pairs(G.GAME.hands) do
if k ~= "High Card" and G.GAME.hands[k].played ~= 0 then
return
end
end
return true
end
if args.type == "cry_lock_all" then
lock_card(self)
end
if args.type == "cry_unlock_all" then
unlock_card(self)
end
end,
}
-- Flesh Panopticon
-- X20 Boss Blind size; When Boss Blind defeated, self destructs and creates a Negative Gateway
local fleshpanopticon = {
object_type = "Joker",
name = "cry-fleshpanopticon",
key = "fleshpanopticon",
pos = { x = 0, y = 5 },
config = { extra = { boss_size = 20 } },
dependencies = {
items = {
"set_cry_epic",
"c_cry_gateway",
},
},
immutable = true,
rarity = "cry_epic",
cost = 15,
order = 146,
atlas = "atlasepic",
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = { set = "Spectral", key = "c_cry_gateway" }
if not center.edition or (center.edition and not center.edition.negative) then
info_queue[#info_queue + 1] = G.P_CENTERS.e_negative
end
return { vars = { center.ability.extra.boss_size } }
end,
calculate = function(self, card, context)
if context.setting_blind and not context.blueprint and context.blind.boss and not card.getting_sliced then
local eval = function(card)
return not card.REMOVED and not G.RESET_JIGGLES
end
juice_card_until(card, eval, true)
card.gone = false
G.GAME.blind.chips = G.GAME.blind.chips * card.ability.extra.boss_size
G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
G.HUD_blind:recalculate()
G.E_MANAGER:add_event(Event({
func = function()
G.E_MANAGER:add_event(Event({
func = function()
play_sound("timpani")
delay(0.4)
return true
end,
}))
card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("cry_good_luck_ex") })
return true
end,
}))
end
if
context.end_of_round
and not context.individual
and not context.repetition
and not context.blueprint
and G.GAME.blind.boss
and not card.gone
then
G.E_MANAGER:add_event(Event({
trigger = "before",
delay = 0.0,
func = function()
local card = create_card(
nil,
G.consumeables,
nil,
nil,
nil,
nil,
Cryptid.enabled("c_cry_gateway") and "c_cry_gateway" or "c_soul",
"sup"
)
card:set_edition({ negative = true }, true)
card:add_to_deck()
G.consumeables:emplace(card)
return true
end,
}))
if not SMODS.is_eternal(card) then
G.E_MANAGER:add_event(Event({
func = function()
play_sound("tarot1")
card.T.r = -0.2
card:juice_up(0.3, 0.4)
card.states.drag.is = true
card.children.center.pinch.x = true
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.3,
blockable = false,
func = function()
G.jokers:remove_card(card)
card:remove()
card = nil
return true
end,
}))
return true
end,
}))
end
card.gone = true
end
end,
cry_credits = {
idea = {
"notmario",
},
art = {
"notmario",
},
code = {
"notmario",
},
},
}
-- Spectrogram
-- Retrigger rightmost Joker once for every Echo Card played and scored
local spectrogram = {
object_type = "Joker",
name = "cry-Spectrogram",
key = "spectrogram",
pos = { x = 1, y = 5 },
config = {
extra = {},
immutable = { echonum = 0 },
},
rarity = "cry_epic",
cost = 9,
order = 133,
atlas = "atlasepic",
dependencies = {
items = {
"set_cry_epic",
"m_cry_echo",
},
},
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = G.P_CENTERS.m_cry_echo
return { vars = {} }
end,
calculate = function(self, card, context)
if context.before and context.cardarea == G.jokers then
card.ability.immutable.echonum = 0
for i, v in pairs(context.scoring_hand) do
if v.config.center_key == "m_cry_echo" and not v.debuff then
card.ability.immutable.echonum = card.ability.immutable.echonum + 1
end
end
end
if
context.retrigger_joker_check
and not context.retrigger_joker
and context.other_card == G.jokers.cards[#G.jokers.cards]
and context.other_card ~= self
then
if card.ability.immutable.echonum and card.ability.immutable.echonum > 0 then
return {
message = localize("k_again_ex"),
repetitions = card.ability.immutable.echonum,
card = card,
}
end
end
end,
cry_credits = {
idea = {
"AlexZGreat",
},
art = {
"Ein13",
},
code = {
"AlexZGreat",
},
},
}
local jtron = {
object_type = "Joker",
dependencies = {
items = {
"set_cry_epic",
},
},
name = "cry-jtron",
key = "jtron",
config = {
extra = { bonus = 1 },
immutable = { current = 0 },
},
rarity = "cry_epic",
cost = 14,
order = 64,
blueprint_compat = true,
demicoloncompat = true,
atlas = "atlasepic",
pos = { x = 2, y = 5 },
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = G.P_CENTERS.j_joker
center.ability.immutable.current =
lenient_bignum(1 + to_big(center.ability.extra.bonus) * #SMODS.find_card("j_joker"))
return {
vars = {
number_format(center.ability.extra.bonus),
number_format(center.ability.immutable.current),
},
}
end,
calculate = function(self, card, context)
card.ability.immutable.current =
lenient_bignum(1 + to_big(card.ability.extra.bonus) * #SMODS.find_card("j_joker"))
if context.cardarea == G.jokers and context.joker_main then
return {
message = localize({
type = "variable",
key = "a_powmult",
vars = {
number_format(card.ability.immutable.current),
},
}),
Emult_mod = lenient_bignum(card.ability.immutable.current),
colour = G.C.DARK_EDITION,
}
end
if context.forcetrigger then
return {
message = localize({
type = "variable",
key = "a_powmult",
vars = {
number_format(1 + to_big(card.ability.extra.bonus)),
},
}),
Emult_mod = lenient_bignum(1 + to_big(card.ability.extra.bonus)),
colour = G.C.DARK_EDITION,
}
end
end,
cry_credits = {
idea = { "AlexZGreat" },
art = { "Darren_The_Frog" },
code = { "candycanearter" },
},
}
-- Retriggers steels every 2nd hand, scaling xmult every 3rd hand, first card to steel every 5th hand, stronger steels every 7th hand
local clockwork = { -- Steel Support: The Joker
object_type = "Joker",
dependencies = {
items = {
"set_cry_epic",
},
},
name = "cry-clockwork",
key = "clockwork",
pos = { x = 5, y = 5 },
config = {
extra = {
xmult = 1,
xmult_mod = 0.25,
steelenhc = 1,
steel_mod = 0.1,
},
immutable = {
limits = {
l1 = 2,
l2 = 3,
l3 = 5,
l4 = 7,
},
counters = {
c1 = 0,
c2 = 0,
c3 = 0,
c4 = 0,
},
},
},
order = 135,
immutable = false,
rarity = "cry_epic",
cost = 12,
blueprint_compat = true,
atlas = "atlasone",
enhancement_gate = "m_steel", -- lucky joker uses this? hopefully it works
loc_vars = function(self, info_queue, center)
local function process_var(m, cap)
if m >= cap - 1 then
return localize("k_active_ex")
end
return cap - m - 1
end
return {
vars = {
process_var(center.ability.immutable.counters.c1, center.ability.immutable.limits.l1),
process_var(center.ability.immutable.counters.c2, center.ability.immutable.limits.l2),
process_var(center.ability.immutable.counters.c3, center.ability.immutable.limits.l3),
process_var(center.ability.immutable.counters.c4, center.ability.immutable.limits.l4),
number_format(center.ability.extra.xmult),
number_format(center.ability.extra.xmult_mod),
number_format(center.ability.extra.steelenhc),
number_format(center.ability.extra.steel_mod),
center.ability.immutable.limits.l1,
center.ability.immutable.limits.l2,
center.ability.immutable.limits.l3,
center.ability.immutable.limits.l4,
},
}
end,
calculate = function(self, card, context)
if context.before and context.cardarea == G.jokers and not context.blueprint and not context.retrigger then
local function clamp(c, l)
local m = c + 1
if c + 1 >= l then
return 0
end
return m
end
card.ability.immutable.counters.c1 =
clamp(card.ability.immutable.counters.c1, card.ability.immutable.limits.l1) -- ticker 1
card.ability.immutable.counters.c2 =
clamp(card.ability.immutable.counters.c2, card.ability.immutable.limits.l2) -- ticker 2
if card.ability.immutable.counters.c2 == 0 then
card.ability.extra.xmult =
lenient_bignum(to_big(card.ability.extra.xmult) + card.ability.extra.xmult_mod)
end
card.ability.immutable.counters.c3 =
clamp(card.ability.immutable.counters.c3, card.ability.immutable.limits.l3) -- ticker 3
card.ability.immutable.counters.c4 =
clamp(card.ability.immutable.counters.c4, card.ability.immutable.limits.l4) -- ticker 4
if card.ability.immutable.counters.c4 == 0 then
card.ability.extra.steelenhc =
lenient_bignum(to_big(card.ability.extra.steelenhc) + card.ability.extra.steel_mod)
end
end
if context.repetition and context.cardarea == G.hand and card.ability.immutable.counters.c1 == 0 then -- effect 1
if SMODS.has_enhancement(context.other_card, "m_steel") then
return {
message = localize("k_again_ex"),
repetitions = 1,
card = card,
}
end
end
if
context.joker_main and context.cardarea == G.jokers -- effect 2
then
return { xmult = card.ability.extra.xmult }
end
if
context.before
and context.cardarea == G.jokers
and not context.blueprint_card
and not context.retrigger_joker
and card.ability.immutable.counters.c3 == 0
and context.full_hand[1]
then -- effect 3
context.full_hand[1]:set_ability(G.P_CENTERS["m_steel"], nil, true)
end
if
context.individual
and context.cardarea == G.hand
and not context.end_of_round
and SMODS.has_enhancement(context.other_card, "m_steel")
and to_big(card.ability.extra.steelenhc) ~= to_big(1)
then
if context.other_card.debuff then
return {
message = localize("k_debuffed"),
colour = G.C.RED,
card = card,
}
else -- effect 4
return { xmult = lenient_bignum(card.ability.extra.steelenhc) }
end
end
--imo this secret effect can be madness only -Math
if
context.after
and context.cardarea == G.jokers
and not context.blueprint_card
and not context.retrigger_joker
then -- The Clockwork Joker is canonically a non-binary self-replicating machine amoeba, that self replicates every 21 minutes. Their pronouns are any/all; they are several billion tiny jokers
if -- in a trench coat, constantly ticking in an almost perfect yet flawed mechanism. Its only purpose is the strengthening and spreading of the steel world; everything else is meaningless to it.
card.ability.immutable.counters.c1 == 0 -- lore by nova :3
and card.ability.immutable.counters.c2 == 0
and card.ability.immutable.counters.c3 == 0
and card.ability.immutable.counters.c4 == 0
then
G.E_MANAGER:add_event(Event({
func = function()
local m = copy_card(card)
m:add_to_deck()
G.jokers:emplace(m)
return true
end,
}))
return {
message = localize("k_duplicated_ex"),
card = card,
}
end
end
end,
set_ability = function(self, card, initial, delay_sprites)
card.ability.immutable.counters.c1 =
math.floor(pseudorandom("Clockwork1") * (card.ability.immutable.limits.l1 - 1) + 0.5)
card.ability.immutable.counters.c2 =
math.floor(pseudorandom("Clockwork2") * (card.ability.immutable.limits.l2 - 1) + 0.5)
card.ability.immutable.counters.c3 =
math.floor(pseudorandom("Clockwork3") * (card.ability.immutable.limits.l3 - 1) + 0.5)
card.ability.immutable.counters.c4 =
math.floor(pseudorandom("Clockwork4") * (card.ability.immutable.limits.l4 - 1) + 0.5)
end,
cry_credits = {
idea = {
"cassknows",
},
code = {
"Nova",
"Math",
},
art = {
"unexian",
},
},
}
-- Force-triggers the rightmost joker during context.joker_main
local demicolon = {
object_type = "Joker",
gameset_config = {
modest = { disabled = true },
mainline = { disabled = true },
madness = { disabled = false },
experimental = { disabled = false },
},
dependencies = {
items = {
"set_cry_epic",
},
},
name = "cry-demicolon",
key = "demicolon",
rarity = "cry_epic",
cost = 14,
order = 299,
blueprint_compat = false,
demicoloncompat = true,
atlas = "atlasepic",
pos = { x = 3, y = 5 },
config = { check = nil },
immutable = true,
loc_vars = function(self, info_queue, card)
card.ability.demicoloncompat_ui = card.ability.demicoloncompat_ui or ""
card.ability.demicoloncompat_ui_check = nil
local check = card.ability.check
return {
main_end = (card.area and card.area == G.jokers)
and {
{
n = G.UIT.C,
config = { align = "bm", minh = 0.4 },
nodes = {
{
n = G.UIT.C,
config = {
ref_table = card,
align = "m",
-- colour = (check and G.C.cry_epic or G.C.JOKER_GREY),
colour = card.ability.colour,
r = 0.05,
padding = 0.08,
func = "blueprint_compat",
},
nodes = {
{
n = G.UIT.T,
config = {
ref_table = card.ability,
ref_value = "demicoloncompat",
colour = G.C.UI.TEXT_LIGHT,
scale = 0.32 * 0.8,
},
},
},
},
},
},
}
or nil,
}
end,
update = function(self, card, front)
local other_joker = nil
if G.STAGE == G.STAGES.RUN then
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then
other_joker = G.jokers.cards[i + 1]
end
end
local m = Cryptid.demicolonGetTriggerable(other_joker)
if m[1] and not m[2] then
card.ability.demicoloncompat = "Compatible"
card.ability.check = true
card.ability.colour = G.C.SECONDARY_SET.Enhanced
elseif m[2] then
card.ability.demicoloncompat = "Dangerous!"
card.ability.check = true
card.ability.colour = G.C.MULT
else
card.ability.demicoloncompat = "Incompatible"
card.ability.check = false
card.ability.colour = G.C.SUITS.Spades
end
end
end,
calculate = function(self, card, context)
if context.joker_main and not context.blueprint then
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then
if Cryptid.demicolonGetTriggerable(G.jokers.cards[i + 1])[1] then
local results = Cryptid.forcetrigger(G.jokers.cards[i + 1], context)
if results and results.jokers then
results.jokers.message = localize("cry_demicolon")
results.jokers.colour = G.C.RARITY.cry_epic
results.jokers.sound = "cry_demitrigger"
return results.jokers
end
return {
message = localize("cry_demicolon"),
colour = G.C.RARITY.cry_epic,
sound = "cry_demitrigger",
}
end
end
end
end
end,
cry_credits = {
idea = { "HexaCryonic" },
art = { "HexaCryonic" },
code = { "Nova" },
},
}
local starfruit = {
object_type = "Joker",
dependencies = {
items = {
"set_cry_epic",
},
},
name = "cry-starfruit",
key = "starfruit",
rarity = "cry_epic",
cost = 14,
order = 300,
blueprint_compat = true,
demicoloncompat = true,
atlas = "atlasepic",
pos = { x = 4, y = 5 },
config = { emult = 2, emult_mod = 0.2 },
pools = { ["Food"] = true },
calculate = function(self, card, context)
if context.joker_main or context.forcetrigger then
return {
message = localize({
type = "variable",
key = "a_powmult",
vars = {
number_format(card.ability.emult),
},
}),
Emult_mod = lenient_bignum(card.ability.emult),
colour = G.C.DARK_EDITION,
}
end
if context.reroll_shop or context.forcetrigger then
SMODS.scale_card(card, {
ref_table = card.ability,
ref_value = "emult",
scalar_value = "emult_mod",
operation = "-",
no_message = true,
})
--floating point precision can kiss my ass istg
if to_number(card.ability.emult) <= 1.00000001 then
G.E_MANAGER:add_event(Event({
func = function()
play_sound("tarot1")
card.T.r = -0.2
card:juice_up(0.3, 0.4)
card.states.drag.is = true
card.children.center.pinch.x = true
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.3,
blockable = false,
func = function()
G.jokers:remove_card(card)
card:remove()
card = nil
return true
end,
}))
return true
end,
}))
return {
message = localize("k_eaten_ex"),
colour = G.C.RARITY.cry_epic,
}
else
if not msg or type(msg) == "string" then
return {
message = msg or localize({
type = "variable",
key = "a_powmult_minus",
vars = {
number_format(card.ability.emult_mod),
},
}),
colour = G.C.RARITY.cry_epic,
}
end
end
end
end,
loc_vars = function(self, queue, card)
return {
vars = {
number_format(card.ability.emult),
number_format(card.ability.emult_mod),
},
}
end,
cry_credits = {
art = { "lord.ruby" },
code = { "lord.ruby" },
idea = { "NinjaBanana" },
},
check_for_unlock = function(self, args)
if args.type == "foods_destroyed" and to_big(args.destroyed) >= 2 then
unlock_card(self)
end
if args.type == "cry_lock_all" then
lock_card(self)
end
if args.type == "cry_unlock_all" then
unlock_card(self)
end
end,
init = function()
local card_remove_ref = Card.remove
function Card:remove(...)
if self:is_food() and self.area == G.jokers and not G.SETTINGS.paused then
G.cry_foods_eaten = (G.cry_foods_eaten or 0) + 1
check_for_unlock({ type = "foods_destroyed", destroyed = G.cry_foods_eaten })
G.E_MANAGER:add_event(Event({
func = function()
G.cry_foods_eaten = nil
return true
end,
}))
end
return card_remove_ref(self, ...)
end
end,
}
return {
name = "Epic Jokers",
items = {
supercell,
membershipcardtwo,
googol_play,
sync_catalyst,
negative,
canvas,
error_joker,
M,
m,
boredom,
double_scale,
number_blocks,
oldcandy,
circus,
caramel,
curse_sob,
bonusjoker,
multjoker,
goldjoker,
altgoogol,
soccer,
fleshpanopticon,
spectrogram,
jtron,
clockwork,
demicolon,
starfruit,
},
}