local unstbex = SMODS.current_mod local filesystem = NFS or love.filesystem local path = unstbex.path local unstbex_config = unstbex.config --Global Table unstbex_global = {} unstbex_global.config = unstbex.config unstbex_lib = {} --Library SMODS.load_file("/lib/suit_compat.lua")() --Localization Messages --local loc = filesystem.load(path..'localization.lua')() -- Debug message local function print(message) sendDebugMessage('[UnstableEX] - '..(tostring(message) or '???')) end print("Starting UnstableEX") --Compat List unstbex_global.compat = { Bunco = (SMODS.Mods["Bunco"] or {}).can_load, Familiar = (SMODS.Mods["familiar"] or {}).can_load, Ortalab = (SMODS.Mods["ortalab"] or {}).can_load, Six_Suit = (SMODS.Mods["SixSuits"] or {}).can_load, Inks_Color = (SMODS.Mods["InkAndColor"] or {}).can_load, Cryptid = (SMODS.Mods["Cryptid"] or {}).can_load, CustomCards = (SMODS.Mods["CustomCards"] or {}).can_load, Pokermon = (SMODS.Mods["Pokermon"] or {}).can_load, KCVanilla = (SMODS.Mods["kcvanilla"] or {}).can_load, DnDJ = (SMODS.Mods["dndj"] or {}).can_load, MtJ = (SMODS.Mods["magic_the_jokering"] or {}).can_load, Minty = (SMODS.Mods["MintysSillyMod"] or {}).can_load, Cardsauce = (SMODS.Mods["Cardsauce"] or {}).can_load, Showdown = (SMODS.Mods["showdown"] or {}).can_load, } local function check_mod_active(mod_id) return unstbex_global.compat[mod_id] end --Config Stuff function unstbex.save_config(self) SMODS.save_mod_config(self) end local unstbex_config_tab = function() --Dynamically Building Config Based on loaded mods --I'll figure out how to make paginations later local mod_config_ui_tables = {} local function create_mod_config(mod_name, conf_list) if not check_mod_active(mod_name.key) then return end local ui_nodes = {} ui_nodes[1] = {n=G.UIT.R, config={align = "cm"}, nodes={{n = G.UIT.T, config = {text = mod_name.name or mod_name.key, colour = G.C.ORANGE, scale = 0.5}}}} for k,v in pairs(conf_list) do ui_nodes[#ui_nodes+1] = create_toggle({label = v.label, ref_table = unstbex.config[v.ref_table], ref_value = k, callback = function() unstbex:save_config() end}) end mod_config_ui_tables[#mod_config_ui_tables+1] = {n=G.UIT.R, config={align = "cl"}, nodes = ui_nodes } end local function mod_config_ui_init() --List of ALL possible mod config create_mod_config({key= "DnDJ", name = "DnDJ"}, { keep_sprite = {label = "Keep Rank Sprites", ref_table = "dndj"}, }) create_mod_config({key= "Showdown", name = "Showdown"}, { use_decimal = {label = "Use Decimal Mechanics", ref_table = "showdown"}, replace_zero = {label = "Replace Rank 0", ref_table = "showdown"}, }) end mod_config_ui_init(); --Rendering local render_table = {} local restart_header = nil if #mod_config_ui_tables > 0 then for i=1, #mod_config_ui_tables do render_table[#render_table+1] = mod_config_ui_tables[i] end restart_header = {n=G.UIT.R, config={align = "cm"}, nodes={{n = G.UIT.T, config = {text = localize("unstb_config_requires_restart"), colour = G.C.RED, scale = 0.4}}}} else render_table = {{n=G.UIT.R, config={align = "cl"}, nodes={ {n=G.UIT.R, config={align = "cm"}, nodes={{n = G.UIT.T, config = {text = "No configurable options found for the current modlist", scale = 0.5}}}}, }}} end return{ { label = "Config", chosen = true, tab_definition_function = function() return { n = G.UIT.ROOT, config = { emboss = 0.05, minh = 6, r = 0.1, minw = 10, align = "cm", padding = 0.2, colour = G.C.BLACK, }, nodes = { {n=G.UIT.R, config={align = "cm"}, nodes= {restart_header} }, {n=G.UIT.R, config={align = "cm"}, nodes={ --Base Box containing everything -- Left Side Column {n=G.UIT.C, config={align = "cl", padding = 0.2}, nodes = render_table }, -- Right Side Column --{n=G.UIT.C, config={align = "cl"}, nodes = render_table}, }} }, } end }, --[[{ --Reserved Tab, in case the settings are expended in the future label = localize("unstb_config_header_joker_settings"), tab_definition_function = function() return { n = G.UIT.ROOT, config = { emboss = 0.05, minh = 6, r = 0.1, minw = 10, align = "cm", padding = 0.2, colour = G.C.BLACK, }, nodes = { }, } end }]] } end unstbex.extra_tabs = unstbex_config_tab --Just so the gear icon shows up unstbex.config_tab = true --Map config value with a single string keyword local config_value = { --["rank_21"] = unstb_config.rank.rank_21, } --Utility --Auto event scheduler, based on Bunco local function event(config) local e = Event(config) G.E_MANAGER:add_event(e) return e end local function big_juice(card) card:juice_up(0.7) end local function extra_juice(card) card:juice_up(0.6, 0.1) end local function play_nope_sound() --Copied from Wheel of Fortune lol event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function() play_sound('tarot2', 0.76, 0.4);return true end}) play_sound('tarot2', 1, 0.4) end local function forced_message(message, card, color, delay, juice) if delay == true then delay = 0.7 * 1.25 elseif delay == nil then delay = 0 end event({trigger = 'before', delay = delay, func = function() if juice then big_juice(juice) end card_eval_status_text( card, 'extra', nil, nil, nil, {message = message, colour = color, instant = true} ) return true end}) end -- Index-based coordinates generation local function get_coordinates(position, width) if width == nil then width = 10 end -- 10 is default for Jokers return {x = (position) % width, y = math.floor((position) / width)} end --Mod Icon SMODS.Atlas { -- Key for code to find it with key = "modicon", -- The name of the file, for the code to pull the atlas from path = "modicon.png", -- Width of each sprite in 1x size px = 32, -- Height of each sprite in 1x size py = 32 } --Creates an atlas for cards to use --[[SMODS.Atlas { -- Key for code to find it with key = "unstbEX_jokers", -- The name of the file, for the code to pull the atlas from path = "unstbEX_jokers.png", -- Width of each sprite in 1x size px = 71, -- Height of each sprite in 1x size py = 95 }]] --Updated Enhancement atli to include modded suits SMODS.Atlas { key = "enh_slop", path = "enh_slop.png", px = 71, py = 95 } SMODS.Atlas { key = "enh_slop_hc", path = "enh_slop_hc.png", px = 71, py = 95 } SMODS.Atlas { key = "enh_res", path = "enh_res.png", px = 71, py = 95 } --Fallback atlas for extra ranks SMODS.Atlas { key = "rank_ex_default", path = "rank_ex/default/rank_ex.png", px = 71, py = 95 } SMODS.Atlas { key = "rank_ex2_default", path = "rank_ex/default/rank_ex2.png", px = 71, py = 95 } --Bunco's resprites suit colours SMODS.Atlas { key = "rank_ex_hc_b", path = "rank_ex/bunco/rank_ex_hc_b.png", px = 71, py = 95 } SMODS.Atlas { key = "rank_ex2_hc_b", path = "rank_ex/bunco/rank_ex2_hc_b.png", px = 71, py = 95 } SMODS.Atlas { key = "enh_slop_hc_b", path = "enh_slop_hc_b.png", px = 71, py = 95 } --Cardsauce Skin local use_cardsauce_skin = false if check_mod_active("Cardsauce") then use_cardsauce_skin = csau_enabled['enableSkins'] SMODS.Atlas { key = "rank_ex_cs", path = "rank_ex/cardsauce/rank_ex_cs.png", px = 71, py = 95 } SMODS.Atlas { key = "rank_ex2_cs", path = "rank_ex/cardsauce/rank_ex2_cs.png", px = 71, py = 95 } end --Familiar's Multi-Suit Cards Fallback --(I don't think it is possible to make all combinations by myself, especially to account for modded suits) SMODS.Atlas { key = "rank_ex_multi", path = "rank_ex_multi.png", px = 71, py = 95 } SMODS.Atlas { key = "rank_ex2_multi", path = "rank_ex2_multi.png", px = 71, py = 95 } --Map new atlas to the rank - new ranks now used more than 1 atlas (split off for maintenance reason) local rank_atlas_map = {['unstb_0'] = 1, ['unstb_0.5'] = 1, ['unstb_1'] = 1, ['unstb_r2'] = 1, ['unstb_e'] = 1, ['unstb_Pi'] = 1, ['unstb_21'] = 1, ['unstb_???'] = 1, ['unstb_11'] = 2, ['unstb_12'] = 2, ['unstb_13'] = 2, ['unstb_25'] = 2, ['unstb_161'] = 2,} local rank_atlas_name = {'unstbex_rank_ex', 'unstbex_rank_ex2'} --Swap the hc atli with bunco resprites version to match suit colours local using_bunco_resprites = false if check_mod_active("Bunco") then if BUNCOMOD and BUNCOMOD.content and BUNCOMOD.content.config then using_bunco_resprites = BUNCOMOD.content.config.fixed_sprites end end local vanilla_suits = {Hearts = true, Clubs = true, Diamonds = true, Spades = true, } unstbex_lib.extra_suits = {} --Init extra suits information based on loaded mod if check_mod_active("Bunco") then unstbex_lib.init_suit_compat('bunc_Fleurons', 'bunco') unstbex_lib.init_suit_compat('bunc_Halberds', 'bunco') end if check_mod_active("Six_Suit") then unstbex_lib.init_suit_compat('six_Stars', 'sixsuits', true) unstbex_lib.init_suit_compat('six_Moons', 'sixsuits', true) end if check_mod_active("Inks_Color") then unstbex_lib.init_suit_compat('ink_Inks', 'inkscolor', true) unstbex_lib.init_suit_compat('ink_Colors', 'inkscolor', true) end if check_mod_active("MtJ") then unstbex_lib.init_suit_compat('mtg_Clovers', 'mtj') end if check_mod_active("Minty") then unstbex_lib.init_suit_compat('minty_3s', 'minty', true) end --Suit injection code based on Showdown by Mistyk__ local function inject_p_card_suit_compat(suit, rank) local card = { name = rank.key .. ' of ' .. suit.key, value = rank.key, suit = suit.key, pos = { x = rank.pos.x, y = rank.suit_map[suit.key] or suit.pos.y }, lc_atlas = rank.suit_map[suit.key] and rank.lc_atlas or suit.lc_atlas, hc_atlas = rank.suit_map[suit.key] and rank.hc_atlas or suit.hc_atlas, } if not vanilla_suits[card.suit] then if not unstbex_lib.extra_suits[card.suit] then print("Warning: Unknown suit for "..card.name) card.lc_atlas = rank_atlas_name[rank_atlas_map[rank.key]]..'_default' card.hc_atlas = rank_atlas_name[rank_atlas_map[rank.key]]..'_default' card.pos.y = 0 else card.lc_atlas = unstbex_lib.extra_suits[card.suit].lc_atlas[rank_atlas_map[rank.key]] card.hc_atlas = unstbex_lib.extra_suits[card.suit].hc_atlas[rank_atlas_map[rank.key]] end end G.P_CARDS[suit.card_key .. '_' .. rank.card_key] = card end local function rank_injection(self) print("Performing extra rank injection") for _, suit in pairs(SMODS.Suits) do inject_p_card_suit_compat(suit, self) end end local function inject_rank_atlas(prefix) for k,v in pairs(SMODS.Ranks) do if k:find(prefix) then local rank = SMODS.Ranks[k] rank.inject = rank_injection if use_cardsauce_skin then rank.lc_atlas = rank_atlas_name[rank_atlas_map[k]]..'_cs' rank.hc_atlas = rank_atlas_name[rank_atlas_map[k]]..'_cs' end if using_bunco_resprites then rank.hc_atlas = rank_atlas_name[rank_atlas_map[k]]..'_hc_b' end --[[rank.lc_atlas = rank_atlas_map[k] rank.hc_atlas = using_bunco_resprites and rank_atlas_map[k]..'_hc_b' or rank_atlas_map[k]..'_hc' rank.suit_map = unstbex_global.rank_suit_map]] print("Injecting the graphic for rank "..rank.key) end end end inject_rank_atlas('unstb_') --Register Suits for UnStable suit system --Modded Suits Code in UnStableEX --Bunco --[[register_suit_group("suit_black", "bunc_Halberds") register_suit_group("suit_red", "bunc_Fleurons") register_suit_group("suit_black", "six_Moons") register_suit_group("suit_red", "six_Stars") register_suit_group("no_smear", "Inks_Inks") register_suit_group("no_smear", "Inks_Color")]] --Update extended atlas for Slop and Resource Cards --Separated from rank suit map now local enhancement_suit_map = { Hearts = 0, Clubs = 1, Diamonds = 2, Spades = 3, bunc_Fleurons = 4, bunc_Halberds = 5, six_Stars = 6, six_Moons = 7, ink_Inks = 8, ink_Colors = 9, } local center_unstb_slop = SMODS.Centers['m_unstb_slop'] or {} center_unstb_slop.suit_map = enhancement_suit_map center_unstb_slop.atlas = 'unstbex_enh_slop' center_unstb_slop.lc_atlas = 'unstbex_enh_slop' center_unstb_slop.hc_atlas = using_bunco_resprites and 'unstbex_enh_slop_hc_b' or 'unstbex_enh_slop_hc' local center_unstb_resource = SMODS.Centers['m_unstb_resource'] or {} center_unstb_resource.suit_map = enhancement_suit_map center_unstb_resource.atlas = 'unstbex_enh_res' --Generic Rank Replacement Utility --A map of orignal mod's rank, to new rank, and the mod's tag to check if the config is enabled or not local replace_rank_map = {} function register_rank_replacement(originalrank, newrank, keepsprite) replace_rank_map[originalrank] = {new_rank = newrank, keep_sprite = keepsprite} end local ref_card_set_base = Card.set_base function Card:set_base(card, initial) card = card or {} ref_card_set_base(self, card, initial) --Only run this piece of codes when inside the run (so, menu animations aren't interrupted) if G.GAME and G.GAME.blind then if self.base and self.base.value and replace_rank_map[self.base.value] then local replace_rank_data = replace_rank_map[self.base.value] if replace_rank_data.keep_sprite and vanilla_suits[self.base.suit] then --Re-assign the rank to UnStable's equivalent --Doing it this way should keep the sprites unchanged --Automatically replaced it completely if the suit isn't vanilla for stable reasons self.base.value = replace_rank_data.new_rank local rank = SMODS.Ranks[self.base.value] or {} self.base.nominal = rank.nominal or 0 self.base.face_nominal = rank.face_nominal or 0 self.base.id = rank.id else SMODS.change_base(self, nil, replace_rank_map[self.base.value].new_rank) end end end end --Blacklist "top" ranks for many jokers local top_rank_blacklist = { ['Ace'] = true, ['unstb_21'] = true, ['unstb_25'] = true, ['unstb_161'] = true, ['unstb_???'] = true, } if check_mod_active("Bunco") then print("Inject Bunco Jokers") local bunc_pawn = SMODS.Centers['j_bunc_pawn'] or {} --Blacklist ranks for Pawn bunc_pawn.loc_vars = function(self, info_queue, card) if G.playing_cards and #G.playing_cards > 0 then local rank = math.huge local target_rank = 'unstb_???'; for _, deck_card in ipairs(G.playing_cards) do local newrank = deck_card.base.nominal + (deck_card.base.face_nominal or 0) if newrank < rank and (not deck_card.config.center.no_rank or deck_card.config.center ~= G.P_CENTERS.m_stone) then rank = newrank target_rank = deck_card.base.value end end return {vars = {localize(target_rank, 'ranks')}} end return {vars = {localize('2', 'ranks')}} end bunc_pawn.calculate = function(self, card, context) if context.after and context.scoring_hand and not context.blueprint then for i = 1, #context.scoring_hand do local condition = false local other_card = context.scoring_hand[i] local rank = math.huge local target_rank = 'unstb_???'; for _, deck_card in ipairs(G.playing_cards) do local newrank = deck_card.base.nominal + (deck_card.base.face_nominal or 0) if newrank < rank and (not deck_card.config.center.no_rank or deck_card.config.center ~= G.P_CENTERS.m_stone) then rank = newrank target_rank = deck_card.base.value end end if other_card.base.value == target_rank and not top_rank_blacklist[other_card.base.value] then condition = true event({trigger = 'after', delay = 0.15, func = function() other_card:flip(); play_sound('card1', 1); other_card:juice_up(0.3, 0.3); return true end }) event({ trigger = 'after', delay = 0.1, func = function() local new_rank = get_next_x_rank(other_card.base.value, 1) assert(SMODS.change_base(other_card, nil, new_rank)) return true end }) event({trigger = 'after', delay = 0.15, func = function() other_card:flip(); play_sound('tarot2', 1, 0.6); big_juice(card); other_card:juice_up(0.3, 0.3); return true end }) end if condition then delay(0.7 * 1.25) end end end end local bunc_zero_shapiro = SMODS.Centers['j_bunc_zero_shapiro'] or {} local zeroshapiro_zerorank = { ['unstb_0'] = true, ['unstb_???'] = true, ['Jack'] = true, ['Queen'] = true, ['King'] = true, ['showdown_Butler'] = true, ['showdown_Princess'] = true, ['showdown_Lord'] = true, ['showdown_Zero'] = true, } bunc_zero_shapiro.calculate = function(self, card, context) if context.individual and context.cardarea == G.play then if context.other_card.config.center.no_rank or zeroshapiro_zerorank[context.other_card.base.value] then if pseudorandom('zero_shapiro'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds then return { extra = {message = '+'..localize{type = 'name_text', key = 'tag_d_six', set = 'Tag'}, colour = G.C.GREEN}, card = card, func = function() event({func = function() add_tag(Tag('tag_d_six')) return true end}) end } end end end end local bunc_crop_circles = SMODS.Centers['j_bunc_crop_circles'] or {} local crop_circles_rank_mult = { ['unstb_0'] = 1, ['unstb_0.5'] = 1, ['6'] = 1, ['8'] = 2, ['9'] = 1, ['10'] = 1, ['Queen'] = 1, ['showdown_8.5'] = 2, ['showdown_Butler'] = 2, ['showdown_Princess'] = 1, ['showdown_Zero'] = 1, } --Implemented differently than in Bunco, but should yield the same result bunc_crop_circles.calculate = function(self, card, context) if context.individual and context.cardarea == G.play then local other_card = context.other_card local total_mult = 0 --Check suit if not other_card.config.center.no_suit then if other_card.base.suit == 'bunc_Fleurons' then total_mult = total_mult + 4 elseif other_card.base.suit == 'Clubs' then total_mult = total_mult + 3 elseif other_card.base.suit == 'mtg_Clovers' then total_mult = total_mult + 4 end end --Check rank if not other_card.config.center.no_rank then if crop_circles_rank_mult[other_card.base.value] then total_mult = total_mult + crop_circles_rank_mult[other_card.base.value] end end --If the amount is greater than 0, grant the bonus w/ animation if total_mult > 0 then return { mult = total_mult, card = card } end end end end --Hook to Familiar's set_sprite_suits to account for new ranks local unstb_ranks_pos = {['unstb_0'] = 6, ['unstb_0.5'] = 2, ['unstb_1'] = 5, ['unstb_r2'] = 7, ['unstb_e'] = 3, ['unstb_Pi'] = 4, ['unstb_21'] = 0, ['unstb_???'] = 1, ['unstb_11'] = 0, ['unstb_12'] = 1, ['unstb_13'] = 2, ['unstb_25'] = 3, ['unstb_161'] = 4,} if check_mod_active("Familiar") then print('Inject Familiar set_sprite_suits') local ref_set_sprite_suits = set_sprite_suits function set_sprite_suits(card, juice) ref_set_sprite_suits(card, juice) --If the rank is one of the UnStable Rank, and has one of the ability if unstb_ranks_pos[card.base.value] and (card.ability.is_spade or card.ability.is_heart or card.ability.is_club or card.ability.is_diamond or card.ability.suitless) then --print('UnstbEX Set Sprite Suit Hook Active') local suit_check = {card.base.suit == 'Spades' or card.ability.is_spade or false, card.base.suit == 'Hearts' or card.ability.is_heart or false, card.base.suit == 'Clubs' or card.ability.is_club or false, card.base.suit == 'Diamonds' or card.ability.is_diamond or false} local suit_count = 0 for i=1, #suit_check do if suit_check[i] then suit_count = suit_count+1 end end --Suitless, or has more than 1 suits if card.ability.suitless or suit_count>1 then --Technically, if anyone wants to make it works properly, this would be where to check --Unfortunately, I don't think I can write them all because there's a lot of combination + lots of graphic to make --Hopefully there is a more elegant solution found in the future. card.children.front.atlas = G.ASSET_ATLAS[rank_atlas_map[card.base.value]..'_multi'] card.children.front:set_sprite_pos({x = unstb_ranks_pos[card.base.value], y = 0}) end end end print("Inject Familiar Vigor Fortune Card") local familiar_vigor = SMODS.Centers['c_fam_vigor'] or {} --Reimplemented Familiar Vigor Fortune Card to use get_next_x_rank instead familiar_vigor.use = function(self, card) for i = 1, #G.hand.highlighted do for j = 1, 3 do G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() local card = G.hand.highlighted[i] local new_rank = get_next_x_rank(card.base.value, 1) assert(SMODS.change_base(card, nil, new_rank)) card:juice_up(0.3, 0.5) return true end })) end end end end --Re-implementation of Ortalab's Index Card functions to support UNSTB Ranks --Notice: this rank changes the behavior from vanilla slightly, where rank 0 and 1 is immediately available local main_rankList = {'unstb_0', 'unstb_1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace'} --Special UNSTB Rank is pre-defined local rankMap = { ['unstb_0.5'] = {UP = 'unstb_1', MID = 'unstb_0.5' , DOWN = 'unstb_0'}, ['unstb_r2'] = {UP = '2', MID = 'unstb_r2' , DOWN = 'unstb_1'}, unstb_e = {UP = '3', MID = 'unstb_e' , DOWN = '2'}, unstb_Pi = {UP = '4', MID = 'unstb_Pi' , DOWN = '3'}, unstb_21 = {UP = 'unstb_21', MID = 'unstb_21' , DOWN = 'unstb_21'}, unstb_11 = {UP = 'unstb_12', MID = 'unstb_11' , DOWN = '10'}, unstb_12 = {UP = 'unstb_13', MID = 'unstb_12' , DOWN = 'unstb_11'}, unstb_13 = {UP = 'Ace', MID = 'unstb_13' , DOWN = 'unstb_12'}, unstb_25 = {UP = 'unstb_25', MID = 'unstb_25' , DOWN = 'unstb_25'}, unstb_161 = {UP = 'unstb_161', MID = 'unstb_161' , DOWN = 'unstb_161'}, ['unstb_???'] = {UP = 'unstb_???', MID = 'unstb_???' , DOWN = 'unstb_???'}, ['showdown_2.5'] = {UP = '3', MID = 'showdown_2.5' , DOWN = '2'}, ['showdown_5.5'] = {UP = '6', MID = 'showdown_5.5' , DOWN = '5'}, ['showdown_8.5'] = {UP = '9', MID = 'showdown_8.5' , DOWN = '8'}, ['showdown_Butler'] = {UP = 'Queen', MID = 'showdown_Butler' , DOWN = 'Jack'}, ['showdown_Princess'] = {UP = 'King', MID = 'showdown_Princess' , DOWN = 'Queen'}, ['showdown_Lord'] = {UP = 'Ace', MID = 'showdown_Lord' , DOWN = 'King'}, ['showdown_Zero'] = {UP = 'unstb_1', MID = 'showdown_Zero' , DOWN = 'Ace'}, } for i=1, #main_rankList do rankMap[main_rankList[i]] = {UP = main_rankList[i+1] or main_rankList[1], MID = main_rankList[i], DOWN = main_rankList[i-1] or main_rankList[#main_rankList]} end --print(inspectDepth(rankMap)) if check_mod_active("Ortalab") then print('Inject Ortalab Index Card') --Inject new property into Ortalab index card local ortalab_index = SMODS.Centers['m_ortalab_index'] or {} ortalab_index.set_ability = function(self, card, initial, delay_sprites) print('call set ability') if card.base and card.ability and card.ability.extra and type(card.ability.extra) == 'table' then if card.ability.extra.index_state == 'MID' then card.ability.extra.mainrank = card.base.value elseif card.ability.extra.index_state == 'UP' then card.ability.extra.mainrank = rankMap[card.base.value]['DOWN'] elseif card.ability.extra.index_state == 'DOWN' then card.ability.extra.mainrank = rankMap[card.base.value]['UP'] end end end ortalab_index.set_sprites = function(self, card, front) if card.ability and card.ability.extra and type(card.ability.extra) == 'table' then if card.ability.extra.index_state == 'MID' then card.children.center:set_sprite_pos({x = 2, y = 0}) elseif card.ability.extra.index_state == 'UP' then --card.ability.extra.mainrank = rankMap[card.base.value]['DOWN'] card.children.center:set_sprite_pos({x = 1, y = 2}) elseif card.ability.extra.index_state == 'DOWN' then card.children.center:set_sprite_pos({x = 0, y = 2}) end --print('main card value is '.. card.ability.extra.mainrank) end end ortalab_index.update = function(self, card) --Jank, handles special case where Tarot like Strength was used if (card.VT.w <= 0) then local isCollection = (card.area and card.area.config.collection) or false if not isCollection then if card.ability.extra.index_state == 'MID' then card.ability.extra.mainrank = card.base.value elseif card.ability.extra.index_state == 'UP' then card.ability.extra.mainrank = rankMap[card.base.value]['DOWN'] elseif card.ability.extra.index_state == 'DOWN' then card.ability.extra.mainrank = rankMap[card.base.value]['UP'] end end --print('main card value changed to '.. card.ability.extra.mainrank) end end G.FUNCS.increase_index = function(e, mute, nosave) --print('using unstbex implementation of func') e.config.button = nil local card = e.config.ref_table local area = card.area local change = 1 if card.ability.extra.index_state == 'DOWN' then change = 2 end card.ability.extra.index_state = 'UP' card.children.center:set_sprite_pos({x = 1, y = 2}) SMODS.change_base(card, nil, rankMap[card.ability.extra.mainrank] and rankMap[card.ability.extra.mainrank]['UP'] or 'unstb_???') end G.FUNCS.mid_index = function(e, mute, nosave) --print('using unstbex implementation of func') e.config.button = nil local card = e.config.ref_table local area = card.area local change = 1 if card.ability.extra.index_state == 'UP' then change = -1 end card.ability.extra.index_state = 'MID' card.children.center:set_sprite_pos({x = 2, y = 0}) --card.base.id = card.base.id + change SMODS.change_base(card, nil, rankMap[card.ability.extra.mainrank] and rankMap[card.ability.extra.mainrank]['MID'] or 'unstb_???') end G.FUNCS.decrease_index = function(e, mute, nosave) --print('using unstbex implementation of func') e.config.button = nil local card = e.config.ref_table local area = card.area local change = 1 if card.ability.extra.index_state == 'UP' then change = 2 end card.ability.extra.index_state = 'DOWN' card.children.center:set_sprite_pos({x = 0, y = 2}) --card.base.id = card.base.id - change SMODS.change_base(card, nil, rankMap[card.ability.extra.mainrank] and rankMap[card.ability.extra.mainrank]['DOWN'] or 'unstb_???') end --More safety check --[[ G.FUNCS.index_card_increase = function(e) if not e.config.ref_table.ability.extra or type(e.config.ref_table.ability.extra) ~= 'table' then e.config.colour = G.C.UI.BACKGROUND_INACTIVE e.config.button = nil return end if e.config.ref_table.ability.extra.index_state ~= 'UP' then e.config.colour = G.C.RED e.config.button = 'increase_index' else e.config.colour = G.C.UI.BACKGROUND_INACTIVE e.config.button = nil end end G.FUNCS.index_card_mid = function(e) if not e.config.ref_table.ability.extra or type(e.config.ref_table.ability.extra) ~= 'table' then e.config.colour = G.C.UI.BACKGROUND_INACTIVE e.config.button = nil return end if e.config.ref_table.ability.extra.index_state ~= 'MID' then e.config.colour = G.C.RED e.config.button = 'mid_index' else e.config.colour = G.C.UI.BACKGROUND_INACTIVE e.config.button = nil end end G.FUNCS.index_card_decrease = function(e) if not e.config.ref_table.ability.extra or type(e.config.ref_table.ability.extra) ~= 'table' then e.config.colour = G.C.UI.BACKGROUND_INACTIVE e.config.button = nil return end if e.config.ref_table.ability.extra.index_state ~= 'DOWN' then e.config.colour = G.C.RED e.config.button = 'decrease_index' else e.config.colour = G.C.UI.BACKGROUND_INACTIVE e.config.button = nil end end]] print("Inject Ortalab Flag Loteria") local ortalab_lot_flag = SMODS.Centers['c_ortalab_lot_flag'] or {} --Reimplementation to use UnStable version of get_next_x_rank ortalab_lot_flag.use = function(self, card, area, copier) --print("UnStbEX version") track_usage(card.config.center.set, card.config.center_key) local options = {} for i=1, card.ability.extra.rank_change do table.insert(options, i) end for i=1, #G.hand.highlighted do local percent = 1.15 - (i-0.999)/(#G.hand.highlighted-0.998)*0.3 G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.15,func = function() G.hand.highlighted[i]:flip();play_sound('card1', percent);G.hand.highlighted[i]:juice_up(0.3, 0.3);return true end })) end for _, card in pairs(G.hand.highlighted) do local sign = pseudorandom(pseudoseed('flag_sign')) > 0.5 and 1 or -1 local change = pseudorandom_element(options, pseudoseed('flag_change')) for i=1, change do G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.4,func = function() local new_rank = get_next_x_rank(card.base.value, sign) assert(SMODS.change_base(card, nil, new_rank)) return true end })) end -- card_eval_status_text(card, 'extra', nil, nil, nil, {message = tostring(sign*change), colour = G.ARGS.LOC_COLOURS.loteria, delay = 0.4}) end for i=1, #G.hand.highlighted do local percent = 0.85 + (i-0.999)/(#G.hand.highlighted-0.998)*0.3 G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.15,func = function() G.hand.highlighted[i]:flip();play_sound('tarot2', percent, 0.6);G.hand.highlighted[i]:juice_up(0.3, 0.3);return true end })) end G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end })) delay(0.5) end --Inject Ortalab Joker code --Mathmagician --Now used the same check from UnStable like Odd Todd and Even Steven local ortalab_mathmagician = SMODS.Centers['j_ortalab_mathmagician'] or {} ortalab_mathmagician.calculate = function(self, card, context) --Mathmagician logic if context.discard and context.other_card == context.full_hand[#context.full_hand] then local numbered_even = 0 local numbered_odd = 0 for _, v in ipairs(context.full_hand) do --Hardcoded check for ??? rank --For that, it counts as both. So it increments whatever needed left if v.base.value == 'unstb_???' then if numbered_odd < 2 then numbered_odd = numbered_odd + 1 else numbered_even = numbered_even + 1 end else --General case, use modulo check from UnStable if unstb_global.modulo_check(v, 2, 1) then numbered_odd = numbered_odd + 1 elseif unstb_global.modulo_check(v, 2, 0) then numbered_even = numbered_even + 1 end end end if numbered_even >= 2 and numbered_odd >= 2 and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then local choice = pseudorandom('mathmagician') > 0.5 and 'Loteria' or 'Zodiac' G.E_MANAGER:add_event(Event({ func = (function() G.E_MANAGER:add_event(Event({ func = function() local card = create_card(choice, G.consumeables) card:add_to_deck() G.consumeables:emplace(card) G.GAME.consumeable_buffer = G.GAME.consumeable_buffer - 1 return true end})) card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = localize('ortalab_'..string.lower(choice)..'_add'), colour = G.C.SET.Loteria}) return true end)})) end end end end --Cryptid Compat if check_mod_active("Cryptid") then --Special interaction w/ Plagiarism and rigged --[[ local j_plagiarism = SMODS.Centers['j_unstb_plagiarism'] if j_plagiarism then local ref_j_plagiarism_calculate = j_plagiarism.calculate j_plagiarism.calculate = function(self, card, context) --Alternate function entirely if it's rigged if card.ability.cry_rigged then --Code based on Familiar's Crimsonotype --This bit of code runs before hand played, cannot copyable by other blueprint if context.before and not context.blueprint and not context.repetition and not context.repetition_only then forced_message('Both', card, G.C.ORANGE, true) end local other_joker = nil for i = 1, #G.jokers.cards do if G.jokers.cards[i] == card then other_joker = {G.jokers.cards[i - 1], G.jokers.cards[i + 1]} end end if other_joker then for i = 1, #other_joker do if other_joker[i] and other_joker[i] ~= self then --local newcontext = context context.blueprint = (context.blueprint and (context.blueprint + 1)) or 1 context.blueprint_card = context.blueprint_card or card if context.blueprint > #G.jokers.cards + 1 then return end local other_joker_ret, trig = other_joker[i]:calculate_joker(context) --Context needs resetting afterwards, otherwise this value keeps persisting context.blueprint = nil local eff_card = context.blueprint_card or card context.blueprint_card = nil if other_joker_ret or trig then if not other_joker_ret then other_joker_ret = {} end other_joker_ret.card = eff_card other_joker_ret.colour = G.C.GREEN other_joker_ret.no_callback = true if other_joker_ret then --Jank, might result in message appear at wrong place idk but at least it should be executed properly SMODS.calculate_effect(other_joker_ret, context.individual and context.other_card or eff_card) --return other_joker_ret end end end end end else return ref_j_plagiarism_calculate(self, card, context) end end end]] --Add appropiate Jokers to the pool --Placeholder, there's no food jokers yet in UNSTB and/or EX --[[ if Cryptid.food then local food_jokers = { } for i = 1, #meme_jokers do Cryptid.food[#Cryptid.food+1] = food_jokers[i] end end]] if Cryptid.memepack then --Adds pretty much most shitpost-centric Joker onto it local meme_jokers = { "j_unstb_joker2", --Joker 2 "j_unstb_joker_stairs", --Joker Stairs "j_unstb_plagiarism", --Plagiarism "j_unstb_prssj", --prssj "j_unstb_the_jolly_joker", --The Jolly too just because "j_unstb_what", --69, 420. Unsure if this would break the in_pool tho } for i = 1, #meme_jokers do Cryptid.memepack[#Cryptid.memepack+1] = meme_jokers[i] end end end -- Hook for is_suit, in case other mods injected into it and it got caught early by the UnStable's hook -- Currently only used by CustomCards if check_mod_active("CustomCards") then local ref_card_is_suit = Card.is_suit function Card:is_suit(suit, bypass_debuff, flush_calc, bypass_seal) local result = ref_card_is_suit(self, suit, bypass_debuff, flush_calc, bypass_seal) --Return early if true (suit seal case) if result then return result end --If it is a trading card, check its own value if self.ability and self.ability.trading then local eval = self:calculate_exotic({bypass_debuff = bypass_debuff, flush_calc = flush_calc, is_suit = suit}) if eval then return eval end end --Should only return false here at this point? return result end end --Pokermon Compat if check_mod_active("Pokermon") then --Use get_next_x_rank instead local ref_poke_vary_rank = poke_vary_rank poke_vary_rank = function(card, decrease, seed) local new_rank if decrease then new_rank = get_next_x_rank(card.base.value, -1) elseif seed then new_rank = pseudorandom_element(SMODS.Ranks, pseudoseed(seed)).key else new_rank = get_next_x_rank(card.base.value, 1) end G.E_MANAGER:add_event(Event({ func = function() SMODS.change_base(card, nil, new_rank) return true end })) end --Inject Jokers Code --Oddish and Bellsprout Lines now used UnStable method of checking odd and even numbers (same as Odd Todd and Even Steven) --Oddish Line local poke_oddish = SMODS.Centers['j_poke_oddish'] or {} poke_oddish.calculate = function(self, card, context) if context.individual and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 1) then local value if pseudorandom('oddish') < .50 then value = card.ability.extra.mult else value = card.ability.extra.mult2 end return { message = localize{type = 'variable', key = 'a_mult', vars = {value}}, colour = G.C.MULT, mult = value, card = card } end end return level_evo(self, card, context, "j_poke_gloom") end local poke_gloom = SMODS.Centers['j_poke_gloom'] or {} poke_gloom.calculate = function(self, card, context) if context.individual and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 1) then local value if pseudorandom('gloom') < .50 then value = card.ability.extra.mult else value = card.ability.extra.mult2 end return { message = localize{type = 'variable', key = 'a_mult', vars = {value}}, colour = G.C.MULT, mult = value, card = card } end end return item_evo(self, card, context) end local poke_vileplume = SMODS.Centers['j_poke_vileplume'] or {} poke_vileplume.calculate = function(self, card, context) if context.individual and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 1) then if pseudorandom('vileplume') < .50 then return { x_mult = card.ability.extra.Xmult_multi, card = card } else return { mult = card.ability.extra.mult, card = card } end end end end local poke_bellossom = SMODS.Centers['j_poke_bellossom'] or {} poke_bellossom.calculate = function(self, card, context) if context.before and context.cardarea == G.jokers and not context.blueprint then local odds = {} for k, v in ipairs(context.scoring_hand) do local upgrade = pseudorandom(pseudoseed('bellossom')) if (unstb_global.modulo_check(v, 2, 1)) and upgrade > .50 then odds[#odds+1] = v if v.ability.name == 'Wild Card' and not v.edition then local edition = {polychrome = true} v:set_edition(edition, true, true) end v:set_ability(G.P_CENTERS.m_wild, nil, true) G.E_MANAGER:add_event(Event({ func = function() v:juice_up() return true end })) else v.bellossom_score = true end end if #odds > 0 then return { message = localize("poke_petal_dance_ex"), colour = G.C.MULT, card = card } end end if context.individual and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 1) then if context.other_card.bellossom_score then context.other_card.bellossom_score = nil return { message = localize{type = 'variable', key = 'a_mult', vars = {card.ability.extra.mult}}, colour = G.C.MULT, mult = card.ability.extra.mult, card = card } end end end end local poke_bellsprout = SMODS.Centers['j_poke_bellsprout'] or {} poke_bellsprout.calculate = function(self, card, context) if context.individual and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 0) then return { message = localize{type = 'variable', key = 'a_chips', vars = {card.ability.extra.chips}}, colour = G.C.CHIPS, chips = card.ability.extra.chips, card = card } end end return level_evo(self, card, context, "j_poke_weepinbell") end local poke_weepinbell = SMODS.Centers['j_poke_weepinbell'] or {} poke_weepinbell.calculate = function(self, card, context) if context.individual and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 0) then return { message = localize{type = 'variable', key = 'a_chips', vars = {card.ability.extra.chips}}, colour = G.C.CHIPS, chips = card.ability.extra.chips, card = card } end end return item_evo(self, card, context, "j_poke_victreebel") end local poke_victreebel = SMODS.Centers['j_poke_victreebel'] or {} poke_victreebel.calculate = function(self, card, context) if context.individual and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 0) then return { message = localize{type = 'variable', key = 'a_chips', vars = {card.ability.extra.chips}}, colour = G.C.CHIPS, chips = card.ability.extra.chips, card = card } end end if context.repetition and context.cardarea == G.play and not context.other_card.debuff then if unstb_global.modulo_check(context.other_card, 2, 0) then return { message = localize('k_again_ex'), repetitions = card.ability.extra.retriggers, card = card } end end end end --KCVanilla Compat if check_mod_active("KCVanilla") then print("Inject KCVanilla Joker") --Five Days Forecast, now used get_next_x_rank properly local function unstb_kcv_rank_up_discreetly(card) -- local newcard = kcv_get_rank_up_pcard(card) card.kcv_ignore_debuff_check = true card.kcv_ranked_up_discreetly = true -- card:set_base(newcard) local old_rank = SMODS.Ranks[card.base.value] local new_rank = get_next_x_rank(card.base.value, 1) card.kcv_display_rank = card.kcv_display_rank and card.kcv_display_rank or old_rank SMODS.change_base(card, card.base.suit, new_rank) -- Should respect "kcv_ranked_up_discreetly" as it uses card:set_base end local kc_5day = SMODS.Centers['j_kcvanilla_5day'] or {} kc_5day.calculate = function(self, card, context) if context.kcv_forecast_event and context.scoring_hand then if next(context.poker_hands["Straight"]) then for i, other_c in ipairs(context.scoring_hand) do if not top_rank_blacklist[other_c.base.value] then unstb_kcv_rank_up_discreetly(other_c) end end end end if context.before and context.scoring_hand then if next(context.poker_hands["Straight"]) then local targets = {} for i, other_c in ipairs(context.scoring_hand) do if other_c.kcv_ranked_up_discreetly then table.insert(targets, other_c) end end card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, { message = localize('k_active_ex'), colour = G.C.FILTER, card = context.blueprint_card or card }); for i_2, other_c_2 in ipairs(targets) do local percent = 1.15 - (i_2 - 0.999) / (#context.scoring_hand - 0.998) * 0.3 G.E_MANAGER:add_event(Event({ func = function() if not other_c_2.kcv_ranked_up_discreetly then -- was complete, but another 5-day joker is targeting this card return true end play_sound('card1', percent) other_c_2:flip() return true end })) delay(0.15) end delay(0.3) for i_3, other_c_3 in ipairs(targets) do local percent = 0.85 + (i_3 - 0.999) / (#context.scoring_hand - 0.998) * 0.3 G.E_MANAGER:add_event(Event({ func = function() if not other_c_3.kcv_ranked_up_discreetly then -- was complete, but another 5-day joker is targeting this card return true end -- kcv_log(other_c_3.base.id .. ' - ' .. other_c_3.kcv_display_rank) other_c_3.kcv_display_rank = SMODS.Ranks[get_next_x_rank(other_c_3.kcv_display_rank.key, 1)] -- Copying method SMODs uses local card_suit = SMODS.Suits[other_c_3.base.suit].card_key local card_rank = other_c_3.kcv_display_rank.card_key local newcard = G.P_CARDS[('%s_%s'):format(card_suit, card_rank)] -- set_base again to update sprites that were postponed by kcv_ranked_up_discreetly other_c_3:set_sprites(nil, newcard) play_sound('tarot2', percent, 0.6) other_c_3:flip() if other_c_3.kcv_display_rank.card_key == SMODS.Ranks[other_c_3.base.value].card_key then -- cleanup other_c_3.kcv_ranked_up_discreetly = nil other_c_3.kcv_ignore_debuff_check = nil other_c_3.kcv_display_rank = nil end return true end })) delay(0.15) end delay(0.5) end end end end --DnDJ Compat if check_mod_active("DnDJ") then --TO DO: Make it a toggle setting if the card from DnDJ contraband pack would keep its rank graphic or not local dndj_keep_sprite = unstbex.config.dndj.keep_sprite local dndj_rank_map = {['dndj_0'] = 'unstb_0', ['dndj_0.5'] = 'unstb_0.5', ['dndj_1'] = 'unstb_1', ['dndj_Pi'] = 'unstb_Pi', ['dndj_11'] = 'unstb_11', ['dndj_12'] = 'unstb_12', ['dndj_13'] = 'unstb_13', ['dndj_21'] = 'unstb_21'} for k, v in pairs(dndj_rank_map) do register_rank_replacement(k, v, dndj_keep_sprite) end end --Showdown compat if check_mod_active("Showdown") then local enable_unstable_decimal = unstbex.config.showdown.use_decimal local replace_zero = unstbex.config.showdown.replace_zero if replace_zero then register_rank_replacement('showdown_Zero', 'unstb_0') end --Adds "decimal" compat to all counterpart ranks local rank_sh_two_half = SMODS.Ranks['showdown_2.5'] rank_sh_two_half.decimal_compat = true local rank_sh_five_half = SMODS.Ranks['showdown_5.5'] rank_sh_five_half.decimal_compat = true local rank_sh_eight_half = SMODS.Ranks['showdown_8.5'] rank_sh_eight_half.decimal_compat = true local rank_sh_butler = SMODS.Ranks['showdown_Butler'] rank_sh_butler.decimal_compat = true local rank_sh_princess = SMODS.Ranks['showdown_Princess'] rank_sh_princess.decimal_compat = true local rank_sh_lord = SMODS.Ranks['showdown_Lord'] rank_sh_lord.decimal_compat = true --Additional information so UnStable's Engineer can work with them unstb_global.register_decimal_rank_map('showdown_2.5', '3') unstb_global.register_decimal_rank_map('showdown_5.5', '6') unstb_global.register_decimal_rank_map('showdown_8.5', '9') unstb_global.register_decimal_rank_map('showdown_Butler', 'Queen') unstb_global.register_decimal_rank_map('showdown_Princess', 'King') unstb_global.register_decimal_rank_map('showdown_Lord', 'Ace') --If the setting is enabled, add proper UnStable decimal rank mechanics onto the ranks if enable_unstable_decimal then --Hook into get_counterpart to erase the UI display for them local ref_getCounterPart = get_counterpart function get_counterpart(rank, onlyCounterpart) --onlyCounterpart is used for UI if onlyCounterpart then return nil end return ref_getCounterPart(rank, onlyCounterpart) end local max_rank_id_number = -1 for _, v in pairs(SMODS.Ranks) do if v.id > 0 and v.id > max_rank_id_number then max_rank_id_number = v.id end end rank_sh_two_half.is_decimal = true rank_sh_two_half.rank_act = {'2', '2.5', '3'} rank_sh_two_half.next = { 'unstb_e', '3', 'unstb_Pi', '4' } rank_sh_two_half.prev = { '2' } rank_sh_two_half.strength_effect = { fixed = 2, random = false, ignore = false } rank_sh_two_half.id = max_rank_id_number + 1 rank_sh_five_half.is_decimal = true rank_sh_five_half.rank_act = {'5', '5.5', '6'} rank_sh_five_half.next = { '6', '7'} rank_sh_five_half.prev = { '5' } rank_sh_five_half.id = max_rank_id_number + 2 rank_sh_eight_half.is_decimal = true rank_sh_eight_half.rank_act = {'8', '8.5', '9'} rank_sh_eight_half.next = { '9', '10'} rank_sh_eight_half.prev = { '8' } rank_sh_eight_half.id = max_rank_id_number + 3 rank_sh_butler.is_decimal = true rank_sh_butler.rank_act = {'Jack', 'Butler', 'Queen'} rank_sh_butler.next = {'Queen', 'showdown_Princess', 'King'} rank_sh_butler.prev = { 'Jack' } rank_sh_butler.id = max_rank_id_number + 4 rank_sh_princess.is_decimal = true rank_sh_princess.rank_act = {'Queen', 'Princess', 'King'} rank_sh_princess.next = {'King', 'showdown_Lord', 'Ace'} rank_sh_princess.prev = { 'Queen' } rank_sh_princess.id = max_rank_id_number + 5 rank_sh_lord.is_decimal = true rank_sh_lord.rank_act = {'King', 'Lord'} rank_sh_lord.next = {'Ace'} rank_sh_lord.prev = { 'King' } rank_sh_lord.id = max_rank_id_number + 6 --Jank, mostly bc Showdown's rank id is forced to be negative for the counterparts, thus ended up mess with the total rank ID orders SMODS.Rank.max_id.value = rank_sh_lord.id --Changes to existing ranks to allow Straight in numerical order SMODS.Ranks['2'].strength_effect = { fixed = 3, random = false, ignore = false } SMODS.Ranks['2'].next = {'showdown_2.5', 'unstb_e', '3', 'unstb_Pi'} SMODS.Ranks['5'].strength_effect = { fixed = 2, random = false, ignore = false } SMODS.Ranks['5'].next = {'showdown_5.5', '6'} SMODS.Ranks['8'].strength_effect = { fixed = 2, random = false, ignore = false } SMODS.Ranks['8'].next = {'showdown_8.5', '9'} SMODS.Ranks['10'].next = {'Jack', 'showdown_Butler', 'unstb_11'} SMODS.Ranks['Jack'].strength_effect = { fixed = 2, random = false, ignore = false } SMODS.Ranks['Jack'].next = {'showdown_Butler', 'Queen', 'showdown_Princess'} SMODS.Ranks['Queen'].strength_effect = { fixed = 2, random = false, ignore = false } SMODS.Ranks['Queen'].next = {'showdown_Princess', 'King', 'showdown_Lord'} SMODS.Ranks['King'].strength_effect = { fixed = 2, random = false, ignore = false } SMODS.Ranks['King'].next = {'showdown_Lord', 'Ace'} end end --Hook for the game's splash screen, to initialize any data that is sensitive to the mod's order (mainly rank stuff) local ref_gamesplashscreen = Game.splash_screen function Game:splash_screen() ref_gamesplashscreen(self) --Cryptid stuff has to be done on Splash Screen because of its high priority if check_mod_active("Cryptid") then print("Inject new nominal code override for Cryptid") --Make a dedicated table of rank id and the nominal order --This is because Cryptid randomize nominal chips in Misprint Deck and Glitched Edition local rank_nominal_order = {} for key, rank in pairs(SMODS.Ranks) do rank_nominal_order[key] = rank.nominal end --Basically the same code from the basegame, but swap nominal out with the new rank_nominal_order property function Card:get_nominal(mod) local mult = 1 local rank_mult = 1 if mod == 'suit' then mult = 30000 end if self.ability.effect == 'Stone Card' or (self.config.center.no_suit and self.config.center.no_rank) then mult = -10000 elseif self.config.center.no_suit then mult = 0 elseif self.config.center.no_rank then rank_mult = 0 end --Temporary fix so the card with the lowest nominal can still be sorted properly local nominal = rank_nominal_order[self.base.value] or 0 if self.base.value == 'unstb_???' then nominal = 0.3 elseif nominal < 0.4 then nominal = 0.31 + nominal*0.1 end --Hardcode this so it's sorted properly if self.base.value == 'unstb_161' then nominal = 30 end return 10*(nominal)*rank_mult + self.base.suit_nominal*mult + (self.base.suit_nominal_original or 0)*0.0001*mult + 10*self.base.face_nominal*rank_mult + 0.000001*self.unique_val end --Secret interaction: The "Jolly Joker" (UnStable Joker) counts as Jolly as well local ref_card_is_jolly = Card.is_jolly function Card:is_jolly() if self.config.center.key == 'j_unstb_the_jolly_joker' then return true end return ref_card_is_jolly(self) end --Inject Blinds effect print("Inject Blind effects for Cryptid") local blind_hammer = SMODS.Blinds['bl_cry_hammer'] or {} blind_hammer.recalc_debuff = function(self, card, from_blind) if card.area ~= G.jokers and not G.GAME.blind.disabled then if card.ability.effect ~= "Stone Card" and ( card.base.value == "3" or card.base.value == "5" or card.base.value == "7" or card.base.value == "9" or card.base.value == "Ace" or card.base.value == "unstb_1" or card.base.value == "unstb_21" or card.base.value == "unstb_11" or card.base.value == "unstb_13" or card.base.value == "unstb_25" or card.base.value == "unstb_161" or card.base.value == "unstb_???" ) then return true end return false end end local blind_magic = SMODS.Blinds['bl_cry_magic'] or {} blind_magic.recalc_debuff = function(self, card, from_blind) if card.area ~= G.jokers and not G.GAME.blind.disabled then if card.ability.effect ~= "Stone Card" and ( card.base.value == "2" or card.base.value == "4" or card.base.value == "6" or card.base.value == "8" or card.base.value == "10" or card.base.value == "unstb_0" or card.base.value == "unstb_12" or card.base.value == "unstb_???" or card.base.value == "showdown_Zero" ) then return true end return false end end --Override ://VARIABLE Code card's code --Because the original code has problem with the card with modded rank --Also, switch over to SMODS.change_base instead of manually building card key, --which was the cause of the problem unstbex_global.cryptid_variable_rank = {'', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace', '', '', '', 'unstb_0', 'unstb_21', 'unstb_0.5', 'unstb_r2', 'unstb_e', 'unstb_Pi', 'unstb_1', 'unstb_11', 'unstb_12', 'unstb_13', 'unstb_25', 'unstb_161', 'unstb_???'} G.FUNCS.variable_apply = function() local rank_table = { {}, { "2", "Two", "II" }, { "3", "Three", "III" }, { "4", "Four", "IV" }, { "5", "Five", "V" }, { "6", "Six", "VI" }, { "7", "Seven", "VII" }, { "8", "Eight", "VIII" }, { "9", "Nine", "IX" }, { "10", "1O", "Ten", "X", "T" }, { "J", "Jack" }, { "Q", "Queen" }, { "K", "King" }, { "A", "Ace"}, --Notably, 1 is now 1 and not Ace :P { "M" }, { "nil" }, {}, --Not sure if I should left it blank but its used for a cheat check below?? --UNSTB Rank {"0", "O", "Zero"}, {"21", "Twenty-One", "TwentyOne", "XXI", "BJ"}, {"0.5", "O.5", "Half"}, {"1.4", "1.41", "Root2", "Sqrt2", "Root", "Sqrt", "r", "sq"}, {"2.7", "2.71","e", "Euler"}, {"3.1", "3.14", "22/7", "Pi", "P"}, {"1", "One", "I"}, {"11", "Eleven", "XI"}, {"12", "Twelve", "XII"}, {"13", "Thirteen", "XIII"}, {"25", "Twenty-Five", "TwentyFive", "XXV", "quad"}, {"161", "OneHundredSixtyOne", "OneSixOne", "CLXI", "abomination"}, {"?", "???", "Question", "idk"}, } local rank_suffix = nil for i, v in pairs(rank_table) do for j, k in pairs(v) do if string.lower(G.ENTERED_RANK) == string.lower(k) then rank_suffix = i end end end if rank_suffix then G.PREVIOUS_ENTERED_RANK = G.ENTERED_RANK G.GAME.USING_CODE = false if rank_suffix == 15 then check_for_unlock({ type = "cheat_used" }) local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") card:add_to_deck() G.jokers:emplace(card) elseif rank_suffix == 16 then check_for_unlock({ type = "cheat_used" }) local card = create_card("Code", G.consumeables, nil, nil, nil, nil, "c_cry_crash") card:add_to_deck() G.consumeables:emplace(card) elseif rank_suffix == 17 then check_for_unlock({ type = "cheat_used" }) G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.4, func = function() play_sound("tarot1") return true end, })) for i = 1, #G.hand.highlighted do local percent = 1.15 - (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.15, func = function() G.hand.highlighted[i]:flip() play_sound("card1", percent) G.hand.highlighted[i]:juice_up(0.3, 0.3) return true end, })) end delay(0.2) for i = 1, #G.hand.highlighted do local CARD = G.hand.highlighted[i] local percent = 0.85 + (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.15, func = function() CARD:flip() CARD:set_ability( G.P_CENTERS[pseudorandom_element(G.P_CENTER_POOLS.Consumeables, pseudoseed("cry_variable")).key], true, nil ) play_sound("tarot2", percent) CARD:juice_up(0.3, 0.3) return true end, })) end else G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.4, func = function() play_sound("tarot1") return true end, })) for i = 1, #G.hand.highlighted do local percent = 1.15 - (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.15, func = function() G.hand.highlighted[i]:flip() play_sound("card1", percent) G.hand.highlighted[i]:juice_up(0.3, 0.3) return true end, })) end delay(0.2) for i = 1, #G.hand.highlighted do G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.1, func = function() local card = G.hand.highlighted[i] local new_rank = unstbex_global.cryptid_variable_rank[rank_suffix] --Fallback if not new_rank or new_rank == '' then new_rank = 'unstb_???' end SMODS.change_base(card, nil, new_rank) return true end, })) end for i = 1, #G.hand.highlighted do local percent = 0.85 + (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.15, func = function() G.hand.highlighted[i]:flip() play_sound("tarot2", percent, 0.6) G.hand.highlighted[i]:juice_up(0.3, 0.3) return true end, })) end G.E_MANAGER:add_event(Event({ trigger = "after", delay = 0.2, func = function() G.hand:unhighlight_all() return true end, })) delay(0.5) end G.CHOOSE_RANK:remove() end end end end