-- overrides.lua - Adds hooks and overrides used by multiple features. -- 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 center_no(v, "doe", k) and not G.GAME.banned_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() --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 --This is how nostalgic deck replaces the boss blinds with Nostalgic versions local bl = gnb() if G.GAME.modifiers.cry_beta then local bl_key = string.sub(bl, 4) local nostalgicblinds = { arm = (cry_card_enabled("bl_cry_oldarm") == true), fish = (cry_card_enabled("bl_cry_oldfish") == true), flint = (cry_card_enabled("bl_cry_oldflint") == true), house = (cry_card_enabled("bl_cry_oldhouse") == true), manacle = (cry_card_enabled("bl_cry_oldmanacle") == true), mark = (cry_card_enabled("bl_cry_oldmark") == true), ox = (cry_card_enabled("bl_cry_oldox") == true), pillar = (cry_card_enabled("bl_cry_oldpillar") == true), serpent = (cry_card_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 --Add context for Just before cards are played local pcfh = G.FUNCS.play_cards_from_highlighted function G.FUNCS.play_cards_from_highlighted(e) G.GAME.before_play_buffer = true G.GAME.blind:cry_before_play() pcfh(e) G.GAME.before_play_buffer = nil end --Track defeated blinds for Obsidian Orb local dft = Blind.defeat function Blind:defeat(s) dft(self, s) local obj = self.config.blind -- Ignore blinds with loc_vars because orb does not properly work with them yet if obj.boss and (obj.boss.no_orb or obj.boss.epic or obj.loc_vars) then return end if self.name ~= "cry-Obsidian Orb" --Stop impossible blind combinations from happening and self.name ~= "The Sink" and (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 ~= "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] = 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" } -- Create G.GAME.events when starting a run, so there's no errors g.events = {} return g end -- reset_castle_card hook for things like Dropshot and Number Blocks local rcc = reset_castle_card function reset_castle_card() rcc() G.GAME.current_round.cry_nb_card = { rank = "Ace" } if not G.GAME.current_round.cry_dropshot_card then G.GAME.current_round.cry_dropshot_card = {} end G.GAME.current_round.cry_dropshot_card.suit = "Spades" local valid_castle_cards = {} for k, v in ipairs(G.playing_cards) do if v.ability.effect ~= "Stone Card" 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)) if not G.GAME.current_round.cry_dropshot_card then G.GAME.current_round.cry_dropshot_card = {} end 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)) if not G.GAME.current_round.cry_nb_card then G.GAME.current_round.cry_nb_card = {} end 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 } 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") }, } cry_pointer_dt = 0 cry_jimball_dt = 0 cry_glowing_dt = 0 function Game:update(dt) upd(self, dt) --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 cry_card_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 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 == 4) and 5 or 4 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 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 not G.CHOOSE_CARD and G.GAME.USING_POINTER then G.CHOOSE_CARD = UIBox({ definition = create_UIBox_pointer(card), config = { align = "cm", offset = { x = 0, y = 10 }, major = G.ROOM_ATTACH, bond = "Weak", instance_type = "POPUP", }, }) G.CHOOSE_CARD.alignment.offset.y = 0 G.ROOM.jiggle = G.ROOM.jiggle + 1 G.CHOOSE_CARD:align_to_major() 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 * (G.GAME.modifiers.cry_rush_hour_iii and 2 or 1) ) or 0 ) --Update UI --todo: in blinds screen, too if G.blind_select_opts then 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 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 * (G.GAME.modifiers.cry_rush_hour_iii and 2 or 1)) * 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 * (G.GAME.modifiers.cry_rush_hour_iii and 2 or 1)) or 1 ) G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) end 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 -- Used for some of the Jen's almanac edition decks because having the price increase apply was "unfun" if self.edition and G.GAME.modifiers.cry_no_edition_price then local m = cry_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 --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) --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) then self.cost = 0 end self.sell_cost_label = self.facing == "back" and "?" or self.sell_cost 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 self.sell_cost_label = 0 end 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 if safe_get(G, "ACTIVE_MOD_UI", "id") == "Cryptid" and obj and not obj.force_gameset then local set = cry_get_gameset(obj) if set == "disabled" or obj.set == "Content Set" then return end local card_type = localize("cry_gameset_" .. cry_get_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) if 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 next(find_joker("cry-candybuttons")) then G.GAME.current_round.reroll_cost = 1 return end if G.GAME.used_vouchers.v_cry_rerollexchange then G.GAME.current_round.reroll_cost = 2 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 -- We're modifying so much of this for Brown and Yellow Stake, Equilibrium Deck, etc. that it's fine to override... 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(predict_pseudoseed(x)) end ps = predict_pseudoseed end local center = G.P_CENTERS.b_red if (_type == "Joker") 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 center_no(center, "doe") and not center_no(center, "aeq") and not (center.rarity == 6 or 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 --should pool be skipped with a forced key if not forced_key and soulable and not G.GAME.banned_keys["c_soul"] then for _, v in ipairs(SMODS.Consumable.legendaries) do if (_type == v.type.key or _type == v.soul_set) and not (G.GAME.used_jokers[v.key] and not next(find_joker("Showman")) and not v.can_repeat_soul) then if pseudo("soul_" .. v.key .. _type .. G.GAME.round_resets.ante) > (1 - v.soul_rate) then forced_key = v.key end end end if (_type == "Tarot" or _type == "Spectral" or _type == "Tarot_Planet") and not (G.GAME.used_jokers["c_soul"] and not next(find_joker("Showman"))) then if pseudo("soul_" .. _type .. G.GAME.round_resets.ante) > 0.997 then forced_key = "c_soul" end end if (_type == "Planet" or _type == "Spectral") and not (G.GAME.used_jokers["c_black_hole"] and not next(find_joker("Showman"))) then if pseudo("soul_" .. _type .. G.GAME.round_resets.ante) > 0.997 then forced_key = "c_black_hole" end end end if _type == "Base" then forced_key = "c_base" end if forced_key then --vanilla behavior change, mainly for M Joker reasons center = G.P_CENTERS[forced_key] _type = (center.set ~= "Default" and center.set or _type) else gcparea = area local _pool, _pool_key = get_current_pool(_type, _rarity, legendary, key_append) gcparea = nil center = pseudorandom_element(_pool, ps(_pool_key)) local it = 1 while center == "UNAVAILABLE" do it = it + 1 center = pseudorandom_element(_pool, ps(_pool_key .. "_resample" .. it)) end center = G.P_CENTERS[center] end local front = ( (_type == "Base" or _type == "Enhanced") and pseudorandom_element(G.P_CARDS, ps("front" .. (key_append or "") .. G.GAME.round_resets.ante)) ) or nil if area == "ERROR" then local ret = (front or center) 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 = Card( area and (area.T.x + area.T.w / 2) or 0, area and area.T.y or 0, G.CARD_W * (center and center.set == "Booster" and 1.27 or 1), G.CARD_H * (center and center.set == "Booster" and 1.27 or 1), front, center, { bypass_discovery_center = area == G.shop_jokers or area == G.pack_cards or area == G.shop_vouchers or (G.shop_demo and area == G.shop_demo) or area == G.jokers or area == G.consumeables, bypass_discovery_ui = area == G.shop_jokers or area == G.pack_cards or area == G.shop_vouchers or (G.shop_demo and area == G.shop_demo), discover = area == G.jokers or area == G.consumeables, bypass_back = G.GAME.selected_back.pos, } ) 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) end if front and G.GAME.modifiers.cry_force_seal then card:set_seal(G.GAME.modifiers.cry_force_seal) end if card.ability.consumeable and not skip_materialize then card:start_materialize() end for k, v in ipairs(SMODS.Sticker.obj_buffer) do local sticker = SMODS.Stickers[v] if sticker.should_apply and type(sticker.should_apply) == "function" and sticker:should_apply(card, center, area) then sticker:apply(card, true) end 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 card.ability.eternal 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 if _type == "Joker" and not G.GAME.modifiers.cry_force_edition then local edition = poll_edition("edi" .. (key_append or "") .. G.GAME.round_resets.ante) card:set_edition(edition) check_for_unlock({ type = "have_edition" }) end end if (card.ability.set == "Code") and G.GAME.used_vouchers.v_cry_quantum_computing and pseudorandom("cry_quantum_computing") > 0.7 then card:set_edition({ negative = true }) 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 = cry_poll_random_edition() card:set_edition(edition, true) end if not (card.edition and (card.edition.cry_oversat or card.edition.cry_glitched)) then cry_misprintize(card) end if _type == "Joker" and G.GAME.modifiers.cry_common_value_quad then if card.config.center.rarity == 1 then cry_misprintize(card, { min = 4, max = 4 }, nil, true) 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 --Debuff jokers if certain boss blinds are active if _type == "Joker" and G.GAME and G.GAME.blind and not G.GAME.blind.disabled then if G.GAME.blind.name == "cry-box" or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_box"] == true) then if card.config.center.rarity == 1 and not card.debuff then card.debuff = true card.debuffed_by_blind = true end end if G.GAME.blind.name == "cry-windmill" or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_windmill"] == true) then if card.config.center.rarity == 2 and not card.debuff then card.debuff = true card.debuffed_by_blind = true end end if G.GAME.blind.name == "cry-striker" or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_striker"] == true) then if card.config.center.rarity == 3 and not card.debuff then card.debuff = true card.debuffed_by_blind = true end end if G.GAME.blind.name == "cry-shackle" or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_shackle"] == true) then if (card.edition and card.edition.negative == true) and not card.debuff then card.debuff = true card.debuffed_by_blind = true end end if G.GAME.blind.name == "cry-pin" or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_pin"] == true) then if card.config.center.rarity ~= 3 and card.config.center.rarity ~= 2 and card.config.center.rarity ~= 1 and card.config.center.rarity ~= 5 then card.debuff = true card.debuffed_by_blind = true end end end return card end -- Make tags fit if there's more than 13 of them -- These two overrides modify the offset to squeeze in more tags when needed local at = add_tag function add_tag(tag) at(tag) 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 --add calculation context and callback to tag function --used for Energia, etc. local at2 = add_tag function add_tag(tag, from_skip, no_copy) if no_copy then at2(tag) return end local added_tags = 1 local ret = {} SMODS.calculate_context({ cry_add_tag = true }, ret) for i = 1, #ret do if ret[i].jokers then added_tags = added_tags + (ret[i].jokers.tags or 0) end end if added_tags >= 1 then at2(tag) end for i = 2, added_tags do local ab = copy_table(G.GAME.tags[#G.GAME.tags].ability) local new_tag = Tag(tag.key) at2(new_tag) new_tag.ability = ab end end -- I don't remember exactly what this patch was for, perhaps issues with syncing hand size with jokers like Effarcire? local nr = new_round function new_round() G.hand:change_size(0) nr() 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 >= 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 >= 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 not center.text then else 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 if Cryptid.obj_buffer and Cryptid.obj_buffer.Stake then for i = 1, #Cryptid.obj_buffer.Stake do local key = Cryptid.obj_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 il() 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