SMODS.Sound({key = "enlightened", path = "enlightened.ogg"}) SMODS.Sound({key = "warning_heartbeat", path = "warning_heartbeat.ogg"}) for i = 1, 8 do SMODS.Sound({key = "gore" .. i, path = "gore" .. i .. ".ogg"}) end SMODS.Sound({ key = "music_attuned", path = "music_attuned.ogg", volume = 1, select_music_track = function() for _, v in pairs(SMODS.find_card("j_jane_saint")) do if v.ability.extra.is_attuned then return 201 end end return -1 / 0 end, }) SMODS.Sound({ key = "music_attuned_sinister", path = "music_attuned_sinister.ogg", volume = 1, select_music_track = function() if not Jane.sinister then return -1 / 0 end for _, v in pairs(SMODS.find_card("j_jane_saint")) do if v.ability.extra.is_attuned then return 202 end end return -1 / 0 end, }) for _, v in pairs({ "artificer", "hunter", "gourmand", "monk", "rivulet", "rot", "saint", "spearmaster", "survivor", }) do SMODS.Atlas { key = "jane" .. v, px = 71, py = 95, path = Jane.config.texture_pack .. "/j_jane_" .. v .. ".png", } end local epic = Jane.cry and "cry_epic" or 3 local exotic = Jane.cry and "cry_exotic" or 4 SMODS.Rarity { key = "junk", default_weight = 0.05, pools = {Joker = true}, loc_txt = {name = "Junk"}, badge_colour = G.C.JOKER_GREY, } local monk_limit = 25 SMODS.Joker { key = "monk", atlas = "janemonk", loc_txt = { name = "The Monk", text = { "{C:attention}Retrigger {}scored", "cards {C:attention}#1# time#2# {}if", "hand contains {C:attention}#3#", "or fewer card#4#", }, }, config = {extra = {retriggers = 2, requirement = 4}}, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, cost = 20, rarity = 4, blueprint_compat = true, loc_vars = function(_, _, card) local extra = card.ability.extra local retriggers = extra.retriggers local requirement = extra.requirement return { vars = { retriggers, retriggers == 1 and "" or "s", requirement, requirement == 1 and "" or "s", }, } end, calculate = function(_, card, context) if not context.repetition or context.cardarea ~= G.play or not context.other_card or #G.play.cards > card.ability.extra.requirement then return end local min = math.min(card.ability.extra.retriggers, monk_limit) card.ability.extra.retriggers = min return { card = card, repetitions = min, colour = G.C.ORANGE, message = localize("k_again_ex"), }, true end, } SMODS.Joker { key = "survivor", atlas = "janesurvivor", loc_txt = { name = "The Survivor", text = { "All cards held in", "hand also {C:attention}score" .. (Jane.cry and " {}and" or ""), Jane.cry and "considered as the" or nil, Jane.cry and "{C:attention}first {}played card" or nil, }, }, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, cost = 20, rarity = 4, } local hunter = {5, 4, 3, 2, 1} SMODS.Joker { key = "hunter", atlas = "janehunter", loc_txt = { name = "The Hunter", text = { "{E:1}Succumbs to the {X:black,C:white,E:1}Rot{},", (Jane.cry and "creating an {C:spectral}Empowered" or "creating a {C:dark_edition}Negative"), (Jane.cry and "{C:spectral}Tag" or "{C:spectral}Soul") .. " {}after #1#", }, }, config = {extra = {rounds_left = hunter[1]}}, pos = {x = 0, y = 0}, eternal_compat = false, blueprint_compat = false, perishable_compat = false, soul_pos = {x = 1, y = 0}, cost = Jane.cry and 15 or 8, rarity = Jane.cry and epic or 3, loc_vars = function(_, info_queue, card) local function rounds(amount) return " round" .. ((math.abs(amount) > 1 or math.abs(amount) == 0) and "s" or "") end info_queue[#info_queue + 1] = G.P_CENTERS.j_jane_rot info_queue[#info_queue + 1] = Jane.cry and G.P_CENTERS.c_cry_empowered or G.P_CENTERS.c_soul local rounds_left = card.ability.extra.rounds_left local sold = rounds_left - hunter[5] return { vars = { rounds_left .. rounds(rounds_left) .. (rounds_left <= 0 and "...?" or ""), sold <= 0 and "" or " after " .. sold .. rounds(sold), sold <= 0 and " into" or "", sold <= 0 and "" or "into ", }, } end, update = function(_, card, _) if card.added_to_deck and card.children.center and card.children.floating_sprite then for k, v in ipairs(hunter) do if card.ability.extra.rounds_left <= v then card.children.center:set_sprite_pos({x = 0, y = k - 1}) card.children.floating_sprite:set_sprite_pos({x = 1, y = k - 1}) else break end end end end, calculate = function(_, card, context) local function spawn_rot() card:flip() card:juice_up(2, 0.8) card.getting_sliced = true Jane.card_status_text(card, "Dead!", nil, 0.05 * card.T.h, G.C.BLACK, 2, 0, 0, nil, "bm", "jane_gore6") Jane.q(function() local rot = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jane_rot", "hunter_rot_death") rot:add_to_deck() G.jokers:emplace(rot) card:set_eternal(nil) rot:set_eternal(true) rot:set_edition(card.edition) play_sound("jane_gore5") end) Jane.q(function() card:start_dissolve() end, 1) end local function die() Jane.q(function() Jane.empowered() end, 0.1) spawn_rot() end if context.blueprint or context.individual or context.repetition or context.retrigger_joker or not context.end_of_round then return end card.ability.extra.rounds_left = card.ability.extra.rounds_left - 1 local rl = card.ability.extra.rounds_left Jane.card_status_text(card, tostring(card.ability.extra.rounds_left), nil, nil, G.C.RED, nil, nil, nil, nil, nil, "generic1") if rl > hunter[2] then card:juice_up(0.6, 0.1) elseif rl > hunter[3] then if rl == hunter[2] then Jane.play_sound("jane_gore1") end card:juice_up(0.6, 0.1) elseif rl > hunter[4] then if rl == hunter[3] then Jane.play_sound("jane_gore3") end card:juice_up(0.6, 0.1) elseif rl > hunter[5] then if rl == hunter[4] then Jane.play_sound("jane_gore8") end card:juice_up(0.6, 0.1) Jane.play_sound("jane_warning_heartbeat") elseif rl > 0 then if rl == hunter[5] then Jane.play_sound("jane_gore4") end card:juice_up(1.8, 0.3) Jane.play_sound("jane_warning_heartbeat") else card:juice_up(2, 0.8) Jane.play_sound("jane_warning_heartbeat") Jane.q(die, 0) end end, } SMODS.Joker { key = "gourmand", atlas = "janegourmand", loc_txt = { name = "The Gourmand", text = {"All cards are {C:dark_edition}Jumbo"}, }, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, blueprint_compat = false, cost = 20, rarity = 4, loc_vars = function(_, info_queue, card) info_queue[#info_queue + 1] = (not card.edition or not card.edition.e_jane_jumbo) and G.P_CENTERS.e_jane_jumbo or nil end, } local function add_to_consumable_ability_by(n) local f = SMODS.Mods.Roland.qol[1] local function new(v) return type(v) == "number" and v + n or type(v) == "table" and f(v):map(new):table() or v end ---@param card Card return function(card) local ability = card.ability or {} local function go(key) ---@type { [string]: number }|number local value = ability[key] --print(key) if not value then return end if type(value) == "number" then ability[key] = value + n return end if type(value) ~= "table" then return end local new_value = f(value):map(new):table() f(new_value):each(function(v, k) ability[k] = v end) ability[key] = new_value end local center_key = (card.config or {}).center_key if center_key and center_key:sub(1, 2) == "c_" then f {"extra", "consumeable"}:each(go) end end end local orig_set_ability = Card.set_ability ---@diagnostic disable-next-line: duplicate-set-field function Card:set_ability(center, initial, delay_sprites) orig_set_ability(self, center, initial, delay_sprites) if next(SMODS.find_card("j_jane_rivulet")) and self.gc and self:gc().key ~= "c_base" and string.sub(self:gc().key, 1, 2) == "c_" then if Jane.cry or not ((SMODS.Mods.Roland or {}).qol or {})[1] then local mod = 1 for _, v in pairs(SMODS.find_card("j_jane_rivulet")) do mod = mod * v.ability.modifier end Jane.misprintize(self, {min = mod, max = mod}, nil, true) else local mod = 0 for _, v in pairs(SMODS.find_card("j_jane_rivulet")) do mod = mod + v.ability.modifier end add_to_consumable_ability_by(mod)(self) end end end SMODS.Joker { key = "artificer", atlas = "janeartificer", loc_txt = { name = "The Artificer", text = { "Use to {C:red}destroy", "selected {C:attention}playing cards", not Jane.cry and "{C:red,E:1}Self-destructs" or nil, not Jane.cry and "after {C:attention}#1# #2#" or nil, }, }, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, config = {extra = {uses = 2}}, eternal_compat = false, blueprint_compat = false, cost = Jane.cry and 50 or 7, rarity = Jane.cry and exotic or 3, loc_vars = function(_, _, card) card.ability = card.ability or {} card.ability.extra = card.ability.extra or {} card.ability.extra.uses = card.ability.extra.uses or 2 local uses = card.ability.extra.uses return {vars = {uses, uses == 1 and "use" or "uses"}} end, Bakery_use_button_text = function(_, _) return "DESTROY" end, Bakery_can_use = function(_, card) return not card.debuff and Jane.can_use() and next(G.hand.highlighted) end, Bakery_use_joker = function(_, card) for _, v in pairs(SMODS.find_card("j_jane_oxy")) do Jane.oxy(v, G.hand.highlighted) end for _, v in pairs(G.hand.highlighted) do v:start_dissolve() end if Jane.cry then return end card.ability.extra.uses = card.ability.extra.uses - 1 local _ = card.ability.extra.uses <= 0 and card:start_dissolve() local _ = card.ability.extra.uses == 1 and juice_card_until(card, function() return card.area == G.jokers end, true) end, } local spearmaster_limit = 25 SMODS.Joker { key = "spearmaster", atlas = "janespearmaster", loc_txt = { name = "The Spearmaster", text = { "You can choose {C:attention}any", "number of cards", "in {C:attention}Booster Packs", "{C:attention}Booster Packs{} have", "{C:green}+#1#{} additional cards", }, }, config = {extra = {choices = 1}}, loc_vars = function(_, _, card) return {vars = {card.ability.extra.choices}} end, calculate = function(_, card, _) card.ability.extra.choices = math.min(card.ability.extra.choices, spearmaster_limit) end, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, blueprint_compat = false, cost = Jane.cry and 12 or 20, rarity = Jane.cry and epic or 4, Bakery_can_use = function(_, card) if card.debuff or not Jane.can_use() or not ((G.pack_cards or {}).cards or {})[1] then return false end local ret = false for _, v in pairs(G.pack_cards.cards) do if v.jane_spearmaster then return false elseif ((v.ability or {}).consumeable or {}).hand_type then ret = true end end return ret end, Bakery_use_button_text = function() return "USE ALL" end, Bakery_use_joker = function(self, card) if not self:Bakery_can_use(card) then return end local c = G.pack_cards.cards for i = #c, 1, -1 do if ((c[i].ability or {}).consumeable or {}).hand_type then c[i].jane_spearmaster = true c[i]:use_consumeable() SMODS.calculate_context {using_consumeable = true, consumeable = c[i], area = c[i].from_area} local normal = c[i].area ~= G.pack_cards c[i]:start_dissolve() if not normal then G.GAME.pack_choices = G.GAME.pack_choices - 1 local _ = G.GAME.pack_choices <= 0 and G.FUNCS.end_consumeable() end end end end, } local orig_open = Card.open ---@diagnostic disable-next-line: duplicate-set-field function Card:open() local orig = self.ability.extra or 1 local spearmasters = SMODS.find_card("j_jane_spearmaster") if next(spearmasters) then for _, v in pairs(spearmasters) do orig = orig + v.ability.extra.choices end self.config.choose = math.floor(orig) self.ability.extra = math.floor(orig) end orig_open(self) Jane.q(function() if next(spearmasters) then G.GAME.pack_choices = math.floor(self.ability.extra) end end, 0.5, "REAL") end SMODS.Joker { key = "rivulet", atlas = "janerivulet", loc_txt = { name = "The Rivulet", text = { "Values on {C:attention}consumables", "are {C:attention}" .. (Jane.cry and "multiplied" or "added") .. "{} by {C:attention}#1#" .. (Jane.cry and "" or "{} when"), (Jane.cry and "when " or "") .. "they are created", "{C:inactive}(If possible)", }, }, config = {modifier = Jane.cry and 2 or 1}, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, blueprint_compat = false, cost = 8, rarity = 3, loc_vars = function(_, _, center) return {vars = {center.ability.modifier}} end, } local orig_draw = Card.draw function Card:draw(layer) local cen = self.gc and self:gc() if self.children.floating_sprite and cen.sinis then if Jane.sinister and not self.shows_sinister then self.shows_sinister = true self.children.floating_sprite:set_sprite_pos(cen.sinis) elseif not Jane.sinister and self.shows_sinister then self.shows_sinister = nil self.children.floating_sprite:set_sprite_pos(cen.soul_pos) end if self.shows_sinister then self:juice_up(0, math.random()) end end if cen and self.facing == "front" and self.config and (self.added_to_deck or (self.area and self.area == G.hand)) and not self.edition and (self.area == G.consumeables or cen.set ~= "Booster") and next(SMODS.find_card("j_jane_gourmand")) then self:set_edition("e_jane_jumbo", true, true) end orig_draw(self, layer) end local orig_debuff = Card.set_debuff function Card:set_debuff(should_debuff) if should_debuff and ((self.config or {}).center or {}).debuff_immune then Jane.card_status_text(self, "Immune!", nil, 0.05 * self.T.h, G.C.RED, nil, 0.6, nil, nil, "bm", "cancel", 1, 0.9) return false else orig_debuff(self, should_debuff) end end SMODS.Joker { key = "saint", atlas = "janesaint", loc_txt = { name = {"{}", "#1#{C:jane_RGB,s:0.8}#2#", "{C:jane_RGB,s:1.6}#3#"}, text = { "{C:attention}Use {}to toggle itself or", "other Jokers to its", "left's {C:legendary}Eternal{} sticker", "{C:attention}#4#{}#5#{C:spectral}#6#{C:planet}#7#{}#8#", "{C:inactive,s:1.25}#9#{C:attention,s:1.25}#10#{C:inactive,s:1.25}#11#", Jane.cry and "{C:inactive}(Gateway has no downside)" or nil, }, }, config = {extra = {changes_editions = false, jokers = 1, karma = -10}}, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, cost = 20, rarity = 4, debuff_immune = true, eternal_compat = true, blueprint_compat = false, perishable_compat = true, loc_vars = function(self, info_queue, card) info_queue[#info_queue + 1] = Jane.cry and G.P_CENTERS.c_cry_gateway or nil local extra = card.ability.extra local attuned = extra.karma >= 0 return { vars = { attuned and "" or "The Saint", attuned and "The" or "", attuned and "Attuned" or "", attuned and "" or "Attune ", attuned and "" or "at ", attuned and (Jane.cry and "Gateways " or "Spectrals ") or (Jane.cry and -self.config.extra.karma .. " Gateways" or ""), (attuned or Jane.cry) and "" or -self.config.extra.karma .. " Planets", attuned and "add Joker slots" or "", attuned and "[Currently " or "[", attuned and "+" .. extra.karma or extra.karma - self.config.extra.karma, attuned and "]" or " / " .. -self.config.extra.karma .. "]", }, } end, update = function(_, card, _) if card.edition and card.edition.Roland_frozen then card:set_edition("e_base") elseif card.children.floating_sprite then card.children.floating_sprite:set_sprite_pos({x = card.ability.extra.is_attuned and 2 or 1, y = 0}) end end, calculate = function(self, card, context) local extra = card.ability.extra extra.is_attuned = extra.is_attuned and extra.karma >= 0 local function ascend() if extra.is_attuning then return end extra.is_attuning = true Jane.card_status_text( card, "!!!", nil, 0.05 * card.T.h, G.C.DARK_EDITION, 0.6, 0.6, 2, 2, "bm", "jane_enlightened" ) Jane.q(function() card:flip() play_sound("card1") end, 0.1) Jane.q(function() card:flip() card:juice_up(1, 1) play_sound("card1") extra.is_attuned = true end, 1) Jane.q(function() extra.karma = extra.karma + extra.jokers G.jokers:change_size(extra.jokers) end, 1) end if extra.karma >= 0 then local karma = extra.is_attuning and 0 or (extra.is_attuned and 1 or extra.karma) G.jokers:change_size(karma * extra.jokers) ascend() end if not (not context.blueprint and (not context.retrigger_joker_check and not context.retrigger_joker) and context.using_consumeable and context.consumeable) then return end local gc = context.consumeable:gc() if Jane.cry and gc.key ~= "c_cry_gateway" or not Jane.cry and gc.set ~= (extra.karma > 0 and "Spectral" or "Planet") then return end local quota = context.consumeable.getEvalQty and context.consumeable:getEvalQty() or 1 extra.karma = extra.karma + quota card_eval_status_text( card, "extra", nil, nil, nil, {message = "+" .. quota .. " Karma", colour = G.C.PALE_GREEN} ) local _ = extra.karma <= 0 and card_eval_status_text( card, "extra", nil, nil, nil, { message = (extra.karma - self.config.extra.karma) .. " / " .. -self.config.extra.karma, colour = G.C.GREEN, } ) if extra.karma < 0 then return end G.jokers:change_size((extra.is_attuned and quota or extra.karma) * extra.jokers) ascend() end, add_to_deck = function(_, card) local extra = card.ability.extra local _ = extra.karma > 0 and (extra.is_attuning or extra.is_attuned) and G.jokers:change_size(extra.karma * extra.jokers) end, remove_from_deck = function(_, card) local extra = card.ability.extra local _ = extra.karma > 0 and (extra.is_attuning or extra.is_attuned) and G.jokers:change_size(-extra.karma * extra.jokers) end, Bakery_can_use = function(_, card) return Jane.can_use() and not card.debuff end, Bakery_use_joker = function(_, card) for joker_index = 1, math.max(card.rank - 1, 1) do local joker = G.jokers.cards[joker_index] joker:set_eternal(not joker.ability.eternal) if joker.Roland_frozen_ability then joker.Roland_frozen_ability.eternal = joker.ability.eternal end if not card.ability.extra.changes_editions or not card.ability.extra.is_attuned or joker.ability.eternal then joker:juice_up() goto continue end local edition = 1 for ii, vv in ipairs(joker.edition and G.P_CENTER_POOLS.Edition or {}) do if vv.key == joker.edition.key then edition = ii break end end local next = edition < #G.P_CENTER_POOLS.Edition and edition + 1 or 1 joker:set_edition(G.P_CENTER_POOLS.Edition[next].key) ::continue:: end end, } if Cryptid and Cryptid.aliases then Cryptid.aliases["saint"] = "j_jane_saint" Cryptid.aliases["attuned"] = "j_jane_saint" Cryptid.aliases["the saint"] = "j_jane_saint" Cryptid.aliases["the attuned"] = "j_jane_saint" end SMODS.Joker { key = "rot", atlas = "janerot", loc_txt = { name = "The Rot", text = { Jane.cry and "{C:attention}Duplicates itself{} at the" or "{C:inactive,E:1}Does nothing", Jane.cry and "end of {C:attention}every ante" or nil, }, }, pos = {x = 0, y = 0}, soul_pos = {x = 1, y = 0}, blueprint_compat = true, cost = 1, rarity = "jane_junk", in_pool = function(_, _) if next(SMODS.find_card("j_jane_rot")) then return true, {allow_duplicates = true} end for _, v in pairs(SMODS.find_card("j_jane_honey")) do if tonumber(v.ability.extra.level) == #Jane.rarity_ids - 1 then return true, {allow_duplicates = true} end end end, calculate = Jane.cry and function(_, card, context) local function has_room() return G.jokers.config.card_count < G.jokers.config.card_limit end local function spawn() if card.cloned then return end local rot = copy_card(card) rot.cloned = true rot:add_to_deck() G.jokers:emplace(rot) Jane.card_status_text(rot, "...", nil, 0.05 * card.T.h, G.C.BLACK, 3, 0, 0, nil, "bm") end if has_room() and not card.cloned and Jane.is_end_of_ante(context, card) then Jane.q(spawn, 0.5) else card.cloned = false end end or nil, }