diff --git a/Talisman/config.lua b/Talisman/config.lua index 0f706ea..5cdb68f 100644 --- a/Talisman/config.lua +++ b/Talisman/config.lua @@ -1 +1 @@ -return {["disable_anims"]=false,["score_opt_id"]=3,["break_infinity"]="omeganum",} \ No newline at end of file +return {["disable_anims"]=true,["score_opt_id"]=3,["break_infinity"]="omeganum",} \ No newline at end of file diff --git a/UnStable-main/LICENSE b/UnStable-main/LICENSE new file mode 100644 index 0000000..a75caa7 --- /dev/null +++ b/UnStable-main/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Kirbio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/UnStable-main/README.md b/UnStable-main/README.md new file mode 100644 index 0000000..5c54e6b --- /dev/null +++ b/UnStable-main/README.md @@ -0,0 +1,23 @@ +![img](https://kirbio.github.io/UnStableModSite/img/logo.png) + +This mod adds a few new mechanics, along with the supported Jokers and Consumables and a few more extra stuff, while maintaining the Vanilla-esque aesthetic and gameplay. + +The total additions are as follows: +- 75 New Jokers +- 8 New Ranks to discover during the gameplay +- New Consumable Type: Auxiliary Card +- Suit and Face Seal +- 5 new Enhancement +- 3 new Unhancement: Enhancement that has negative effects +- 5 Tarot and 9 Spectral Cards for supporting all new features + +You can also toggle each feature separately via the setting. + +For more details, please visit the mod website at +https://kirbio.github.io/UnStableModSite/ + +Please note that this mod is designed and balanced to be played as a standalone mod. +It is possible to play it with other mods, however. +If you are playing it with other mods, please also get the [UnStableEX](https://github.com/kirbio/UnStableEX) expansion as well for cross-mod compatibility. + +Enjoy! diff --git a/UnStable-main/Unstable.lua b/UnStable-main/Unstable.lua new file mode 100644 index 0000000..71ce667 --- /dev/null +++ b/UnStable-main/Unstable.lua @@ -0,0 +1,9550 @@ +local unstb = SMODS.current_mod +local filesystem = NFS or love.filesystem +local path = unstb.path +local unstb_config = unstb.config + +--Global Table +unstb_global = {} +unstb_global.config = unstb.config + +-- Debug message +local function print(message) + sendDebugMessage('[UnStable] - '..(tostring(message) or '???')) +end + +print("Starting Unstable") + +--Initialize All Colors +G.C.UNSTB_AUX = HEX('00a669') + +--Localization Colors +--Properly hook into the function, so it does not error +local ref_loc_colour = loc_colour + +function loc_colour(_c, default) + if not G.ARGS.LOC_COLOURS then + ref_loc_colour(_c, default) + elseif not G.ARGS.LOC_COLOURS.unstb_colour then --Init UNSTB's exclusive color + G.ARGS.LOC_COLOURS.unstb_colour = true + + G.ARGS.LOC_COLOURS['auxiliary'] = G.C.UNSTB_AUX + end + + return ref_loc_colour(_c, default) +end + +--Description page formatting +unstb.description_loc_vars = function() + return { background_colour = G.C.CLEAR, text_colour = G.C.WHITE, scale = 1.2 } +end + +--Config Stuff + +function unstb.save_config(self) + SMODS.save_mod_config(self) +end + +local unstb_config_tab = function() + return{ + { + label = localize("unstb_config_header_mech_setting"), + 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={ + + {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}}}}, + }}, + + {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={ + {n=G.UIT.R, config={align = "cl"}, nodes={ + + {n=G.UIT.R, config={align = "cm"}, nodes={{n = G.UIT.T, config = {text = localize("unstb_config_header_rank"), colour = G.C.ORANGE, scale = 0.5}}}}, + create_toggle({label = localize("unstb_config_rank21"), ref_table = unstb.config.rank, ref_value = 'rank_21', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_rank_bi"), ref_table = unstb.config.rank, ref_value = 'rank_binary', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_rank_decimal"), ref_table = unstb.config.rank, ref_value = 'rank_decimal', callback = function() unstb:save_config() end}), + + }}, + + {n=G.UIT.R, config={align = "cl"}, nodes={ + + {n=G.UIT.R, config={align = "cm"}, nodes={{n = G.UIT.T, config = {text = localize("unstb_config_header_enh"), colour = G.C.ORANGE, scale = 0.5}}}}, + create_toggle({label = localize("unstb_config_enh_custom"), ref_table = unstb.config.enh, ref_value = 'enh_custom', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_enh_disenh"), ref_table = unstb.config.enh, ref_value = 'enh_disenh', callback = function() unstb:save_config() end}), + + }} + }}, + + -- Right Side Column + {n=G.UIT.C, config={align = "cl"}, nodes={ + + {n=G.UIT.R, config={align = "cl"}, nodes={ + + {n=G.UIT.R, config={align = "cm"}, nodes={{n = G.UIT.T, config = {text = localize("unstb_config_header_mechanics"), colour = G.C.ORANGE, scale = 0.5}}}}, + create_toggle({label = localize("unstb_config_mech_upgrade"), ref_table = unstb.config.gameplay, ref_value = 'edition_upgrade', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_mech_suitseal"), ref_table = unstb.config.gameplay, ref_value = 'seal_suit', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_mech_aux"), ref_table = unstb.config.gameplay, ref_value = 'c_aux', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_mech_music"), ref_table = unstb.config.gameplay, ref_value = 'music', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_mech_fallback"), info = localize("unstb_config_mech_fallback_desc"), ref_table = unstb.config.gameplay, ref_value = 'c_rebundant', callback = function() unstb:save_config() end}), + create_toggle({label = localize("unstb_config_mech_new_spectral"), ref_table = unstb.config.gameplay, ref_value = 'new_spectrals', callback = function() unstb:save_config() end}), + + }}, + }}, + + }} + }, + } + 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 = { + + {n=G.UIT.R, config={align = "cm"}, nodes={ + + {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}}}}, + }}, + + {n=G.UIT.R, config={align = "cm"}, nodes={ --Base Box containing everything + + -- Right Side Column + {n=G.UIT.C, config={align = "cl"}, nodes={ + + {n=G.UIT.R, config={align = "cl"}, nodes={ + + {n=G.UIT.R, config={align = "cm"}, nodes={{n = G.UIT.T, config = {text = "Joker", colour = G.C.ORANGE, scale = 0.5}}}}, + create_toggle({label = 'Vanilla Joker Overrides', info = {'Re-coded Vanilla Jokers to work with new features'}, ref_table = unstb.config.joker, ref_value = 'vanilla', callback = function() unstb:save_config() end}), + create_toggle({label = 'Shitpost Jokers', ref_table = unstb.config.joker, ref_value = 'shitpost', callback = function() unstb:save_config() end}), + create_toggle({label = 'Major Gameplay-Altering Jokers', info = {'Jokers that may alters the playstyle drastically'}, ref_table = unstb.config.joker, ref_value = 'alter', callback = function() unstb:save_config() end}), + create_toggle({label = 'Powerful Jokers', info = {'Jokers that is significantly powerful than base game'}, ref_table = unstb.config.joker, ref_value = 'powerful', callback = function() unstb:save_config() end}), + }} + }}, + + }} + + }, + + + } + end + } + } +end + +unstb.extra_tabs = unstb_config_tab + +--Just so the gear icon shows up +unstb.config_tab = true + +--Map config value with a single string keyword +local config_value = { + ["rank_21"] = unstb_config.rank.rank_21, + ["rank_binary"] = unstb_config.rank.rank_binary, + ["rank_decimal"] = unstb_config.rank.rank_decimal, + + ["enh_custom"] = unstb_config.enh.enh_custom, + ["enh_disenh"] = unstb_config.enh.enh_disenh, + + ["edition_upgrade"] = unstb_config.gameplay.edition_upgrade, + ["seal_suit"] = unstb_config.gameplay.seal_suit, + ["c_aux"] = unstb_config.gameplay.c_aux, + ["music"] = unstb_config.gameplay.music, + ["c_rebundant"] = unstb_config.gameplay.c_rebundant, + ["new_spectrals"] = unstb_config.gameplay.new_spectrals, + + ["j_vanilla"] = unstb_config.joker.vanilla, + ["j_shitpost"] = unstb_config.joker.shitpost, + ["j_alter"] = unstb_config.joker.alter, + ["j_powerful"] = unstb_config.joker.powerful, +} + +--Inclusion Check by a list of keyword +local function check_enable_taglist(taglist) + local isAdded = true + for _, v in ipairs(taglist) do + isAdded = isAdded and config_value[v] + end + return isAdded +end + +--Talisman Compatibility +to_big = to_big or function(num) + return num +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 Jokers to use +SMODS.Atlas { + -- Key for code to find it with + key = "unstb_jokers", + -- The name of the file, for the code to pull the atlas from + path = "jokers.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for special Jokers (multi-face, dynamic graphic, etc) +SMODS.Atlas { + -- Key for code to find it with + key = "unstb_jokers_ex", + -- The name of the file, for the code to pull the atlas from + path = "jokers_ex.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "unstb_jokers_wip", + -- The name of the file, for the code to pull the atlas from + path = "jokers_wip.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for new enhancements + +SMODS.Atlas { + -- Key for code to find it with + key = "unstb_back", + -- The name of the file, for the code to pull the atlas from + path = "back.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "enh_slop", + -- The name of the file, for the code to pull the atlas from + path = "enh_slop.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "enh_slop_hc", + -- The name of the file, for the code to pull the atlas from + path = "enh_slop_hc.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "enh_resource", + -- The name of the file, for the code to pull the atlas from + path = "enh_res.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for Suit Seals +SMODS.Atlas { + -- Key for code to find it with + key = "suit_seal", + -- The name of the file, for the code to pull the atlas from + path = "suit_seal.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for Booster Pack +SMODS.Atlas { + -- Key for code to find it with + key = "booster", + -- The name of the file, for the code to pull the atlas from + path = "booster.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for Auxiliary Cards + +SMODS.Atlas { + -- Key for code to find it with + key = "auxiliary_undiscovered", + -- The name of the file, for the code to pull the atlas from + path = "auxiliary_undiscovered.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "auxiliary", + -- The name of the file, for the code to pull the atlas from + path = "auxiliary.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for Other Consumable Cards + +--Tarot +SMODS.Atlas { + -- Key for code to find it with + key = "tarot", + -- The name of the file, for the code to pull the atlas from + path = "tarot.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + + +--Spectral +SMODS.Atlas { + -- Key for code to find it with + key = "spectral", + -- The name of the file, for the code to pull the atlas from + path = "spectral.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for Vouchers +SMODS.Atlas { + -- Key for code to find it with + key = "voucher", + -- The name of the file, for the code to pull the atlas from + path = "voucher.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for Decks +SMODS.Atlas { + -- Key for code to find it with + key = "deck", + -- The name of the file, for the code to pull the atlas from + path = "deck.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +--Atlas for extra ranks +SMODS.Atlas { + -- Key for code to find it with + key = "rank_ex", + -- The name of the file, for the code to pull the atlas from + path = "rank_ex.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "rank_ex2", + -- The name of the file, for the code to pull the atlas from + path = "rank_ex2.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "rank_ex_hc", + -- The name of the file, for the code to pull the atlas from + path = "rank_ex_hc.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + +SMODS.Atlas { + -- Key for code to find it with + key = "rank_ex2_hc", + -- The name of the file, for the code to pull the atlas from + path = "rank_ex2_hc.png", + -- Width of each sprite in 1x size + px = 71, + -- Height of each sprite in 1x size + py = 95 +} + + +--Music + +local aux_music_enable = unstb_config.gameplay.music + +SMODS.Sound({ + key = "music_aux", + path = "music_aux.ogg", + select_music_track = function() + return aux_music_enable + and ( G.pack_cards and G.pack_cards.cards and G.pack_cards.cards[1] and G.pack_cards.cards[1].ability.set == "Auxiliary") + end, +}) + +--SFXs + +SMODS.Sound({key = 'heal', path = 'heal.ogg'}) +SMODS.Sound({key = 'poison', path = 'poison.ogg'}) + + +--Jokers +--filesystem.load(unstb.path..'joker\\joker.lua')() + +--Utility + +--Check if table value already exists in the List +local function table_has_value(list, value) + for i, v in ipairs(list) do + if v==value then + return true + end + end + + return false +end + +--Expose this function so I can use this in global scope +unstb_global.table_has_value = table_has_value + +--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 + +--Joker creation wrapper, based on Bunco +local function create_joker(joker) + + --Check Joker's keyword tag, if the setting is turned off then don't add anything + local isAdded = true + if joker.gameplay_tag and type(joker.gameplay_tag) == 'table' then + for _, v in ipairs(joker.gameplay_tag) do + isAdded = check_enable_taglist(joker.gameplay_tag) + end + end + if not isAdded then return end + + -- Sprite position + + local width = 10 -- Width of the spritesheet (in Jokers) + + -- Soul sprite + + if joker.rarity == 'Legendary' then + joker.soul = get_coordinates(joker.id) -- Calculates coordinates based on the position variable + end + + joker.position = get_coordinates(joker.id) + + -- Sprite atlas + + if joker.type == nil then + joker.atlas = 'unstb_jokers' + elseif joker.type == 'Banned' then + joker.atlas = 'unstb_jokers_banned' + end + + if joker.rarity == 'Legendary' then + joker.atlas = 'unstb_jokers_legend' + end + + --If the joker has ex property, used extra sheet + if joker.ex_art then + joker.atlas = 'unstb_jokers_ex' + end + + --If the joker has no art, fallback to WIP sheet + if joker.no_art then + joker.atlas = 'unstb_jokers_wip' + end + + -- Key generation from name + + local key = string.gsub(string.lower(joker.name), '%s', '_') -- Removes spaces and uppercase letters + + -- Rarity conversion + + if joker.rarity == 'Common' then + joker.rarity = 1 + elseif joker.rarity == 'Uncommon' then + joker.rarity = 2 + elseif joker.rarity == 'Rare' then + joker.rarity = 3 + elseif joker.rarity == 'Legendary' then + joker.rarity = 4 + end + + -- Config values + + if joker.vars == nil then joker.vars = {} end + + joker.config = {extra = {}} + + for _, kv_pair in ipairs(joker.vars) do + -- kv_pair is {a = 1} + local k, v = next(kv_pair) + joker.config.extra[k] = v + end + + -- Joker creation + SMODS.Joker{ + name = joker.name, + key = key, + + atlas = joker.atlas, + pos = joker.position, + soul_pos = joker.soul, + + rarity = joker.rarity, + cost = joker.cost, + + unlocked = true, + --check_for_unlock = joker.check_for_unlock, + --unlock_condition = joker.unlock_condition, + --discovered = true, --false, + + blueprint_compat = joker.blueprint or false, + + eternal_compat = (joker.eternal == nil) or joker.eternal, + + perishable_compat = (joker.perishable == nil) or joker.perishable, + + + process_loc_text = joker.process_loc_text, + + config = joker.custom_config or joker.config, + loc_vars = joker.custom_vars or function(self, info_queue, card) + + -- Localization values + + local vars = {} + + for _, kv_pair in ipairs(joker.vars) do + -- kv_pair is {a = 1} + local k, v = next(kv_pair) + -- k is `a`, v is `1` + table.insert(vars, card.ability.extra[k]) + end + + return {vars = vars} + end, + + calculate = joker.calculate, + update = joker.update, + remove_from_deck = joker.remove_from_deck, + add_to_deck = joker.add_to_deck, + + set_ability = joker.set_ability, + set_sprites = joker.set_sprites, + load = joker.load, + + calc_dollar_bonus = joker.calc_dollar_bonus, + + in_pool = joker.custom_in_pool or pool, + + effect = joker.effect + } +end + +--New global function to get a random eligible suit and rank from the deck without rank-overrides enhancements getting in the way +--Code based on Castle from base game +function get_valid_card_from_deck(seed) + + local res_suit = 'Spades' + local res_rank = '2' + + local valid_cards = {} + for k, v in ipairs(G.playing_cards) do + if not v.config.center.replace_base_card then --Excludes all cards with replace_base_card enhancements + valid_cards[#valid_cards+1] = v + end + end + if valid_cards[1] then + local target_card = pseudorandom_element(valid_cards, pseudoseed(seed or 'validcard'..G.GAME.round_resets.ante)) + + res_suit = target_card.base.suit + res_rank = target_card.base.value + end + + return {suit = res_suit, rank = res_rank} +end + +--Black Jack Rank Calculation +local function blackJack_evalrank(hand, bustAmount) + --Black Jack-style total rank evaluation + + bustAmount = bustAmount or 21 + + local aceCount = 0 + local rank = 0 + + for i = 1, #hand do + local currentCard = hand[i] + if not (currentCard.config.center == G.P_CENTERS.m_stone or currentCard.config.center.no_rank) and not currentCard.debuff then + + if currentCard.base.value ~= 'Ace' then + rank = rank + (SMODS.Ranks[currentCard.base.value].nominal or 0) --Supports modded ranks as well, just in case + else + aceCount = aceCount + 1 + + rank = rank + 11 + end + + end + end + + --Handle Ace rank + while( aceCount > 0 ) + do + + if rank > bustAmount then + rank = rank - 10 + end + + aceCount = aceCount - 1 + end + + return rank + +end + +--"Upgrade" function, used on card +local function edition_upgrade(card) + local edition = (card.edition or {}).key + + if not edition then + card:set_edition("e_foil", true, false) + elseif edition=="e_foil" then + card:set_edition("e_holo", true, false) + elseif edition=="e_holo" then + card:set_edition("e_polychrome", true, false) + end +end + +--General Helper function for rank increment / decrement + +--Insert "prev" property into SMODS.Ranks +function init_prev_rank_data() + print("Initialize Remaining Previous Rank Data") + for _, rank in pairs(SMODS.Ranks) do + + --Initialize + --In case the rank table does not have prev existed + --Base rank and UNSTB one has them defined manually by default + if not rank.prev then + rank.prev = {} + end + + next_rank_list = rank.next + + for i=1, #next_rank_list do + local next_rank = SMODS.Ranks[next_rank_list[i]] + local prev = next_rank.prev or {} + + if not table_has_value(prev, rank.key) then + table.insert(prev, rank.key) + next_rank.prev = prev + end + end + end +end + +--Utility function to get the next rank by specified step, walked using the same algorithm as SMODS Implementation of Strength Tarot +--Negative Step is also ok +function get_next_x_rank(rank, step) + local currentRank = SMODS.Ranks[rank] + + if not currentRank then + return 'unstb_???' --Fallback, if the rank is invalid then returning ??? rank card + end + + local behavior + local new_rank + + local mul = (step > 0 and 1) or -1 + + --Based on SMODS Current implementation of Strength + for i=mul, step, mul do + + if mul > 0 then + behavior = currentRank.strength_effect or { fixed = 1, ignore = false, random = false } + else + behavior = currentRank.decrease_effect or { fixed = 1, ignore = false, random = false } + end + + if behavior.ignore or not next(currentRank.next) then + return rank + elseif behavior.random then + -- TODO doesn't respect in_pool + if mul>0 then + new_rank = pseudorandom_element(currentRank.next, pseudoseed('nextrank')) + else + new_rank = pseudorandom_element(currentRank.prev, pseudoseed('prevrank')) + end + + else + if mul>0 then + local ii = (behavior.fixed and currentRank.next[behavior.fixed]) and behavior.fixed or 1 + new_rank = currentRank.next[ii] + else + local ii = (behavior.fixed and currentRank.prev[behavior.fixed]) and behavior.fixed or 1 + new_rank = currentRank.prev and currentRank.prev[ii] or currentRank.key + end + + end + + currentRank = SMODS.Ranks[new_rank] + --print(SMODS.Ranks[new_rank].key) + end + + return new_rank or rank + +end + +--Face Seal + +if unstb_config.gameplay.seal_suit then + +SMODS.Seal({ + key = "face", + atlas = "suit_seal", + + pos = { x = 4, y = 0 }, + badge_colour = HEX "f59c00", + shiny = true, + + weight = 0, + config = {extra = {}}, + + loc_vars = function(self, info_queue, card) + return {vars = {}} + end, + --[[calculate = function(self, card, context) + + end]] + + --This cannot spawn naturally at all + in_pool = function(self, args) + return false + end + }) + +end + +--Hook into is_face to account for Face Seal +local card_isfaceref = Card.is_face + +function Card:is_face(from_boss) + if self.debuff and not from_boss then return end + + if self.seal == 'unstb_face' then + return true + end + + return card_isfaceref(self, from_boss) +end + +--Suit Seals + +--Global table for Suit Seals-related stuff +SuitSeal = {} + +SuitSeal.suit_seal_colors = { + Spades = HEX "603de8", + Hearts = HEX "e83d61", + Clubs = HEX "19b8a1", + Diamonds = HEX "e8793d", +} + +--External function to supports register more suits (especially modded) if needed +function SuitSeal.registerSealColor(suit, color) + SuitSeal.suit_seal_colors[suit] = HEX(color) +end + +--Creating an actual SMODS Seal object +function SuitSeal.initSeal(suit, atlas, posX) + SMODS.Seal({ + key = string.lower(suit), + atlas = atlas or "suit_seal", + + --Extra field for suit seal only, + suit_seal = suit, + + pos = { x = posX or 0, y = 0 }, + badge_colour = SuitSeal.suit_seal_colors[suit], + weight = 0, + config = {extra = {}}, + + loc_vars = function(self, info_queue, card) + return {vars = {localize(self.suit_seal, 'suits_plural'), colours = {G.C.SUITS[self.suit_seal]}}} + end, + + --This cannot spawn naturally at all + in_pool = function(self, args) + return false + end + }) +end + +local suit_seal_list = {"Spades", "Hearts", "Clubs", "Diamonds"} + +--Global Suit Seal map from suit +unstb_global.SUIT_SEAL = {} + +--TODO: A function wrapper that registers extra suit seal, corresponding auxillary card at the same time +--Might be a part of UnStableEX + +if unstb_config.gameplay.seal_suit then + +for i = 1, #suit_seal_list do + SuitSeal.initSeal(suit_seal_list[i], "suit_seal", i-1 ) + + unstb_global.SUIT_SEAL[suit_seal_list[i]] = {} + + unstb_global.SUIT_SEAL[suit_seal_list[i]].seal_key = 'unstb_'..string.lower(suit_seal_list[i]) + unstb_global.SUIT_SEAL[suit_seal_list[i]].aux_key = 'c_unstb_aux_'..string.lower(suit_seal_list[i]) +end + +end + +--Heal Seal + +function Card:heal(initial, delay_sprites) + local prev_center = self.config.prev_center or G.P_CENTERS.c_base + + --Nullify hand xmult just in case + if self.ability.h_x_mult then + self.ability.h_x_mult = 0 + end + + self:set_ability(prev_center, initial, delay_sprites) +end + +if unstb_config.enh.enh_disenh then + +SMODS.Seal({ + key = "heal", + atlas = "suit_seal", + + pos = { x = 5, y = 0 }, + badge_colour = HEX "7ce83d", + + weight = 0, + config = {extra = {}}, + + loc_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'disenhancement'} + return {vars = {}} + end, + calculate = function(self, card, context) + if context.cardarea == G.play and context.main_scoring then + + local valid_cards = {} + for i = 1, #G.hand.cards do + if G.hand.cards[i].config.center.disenhancement and not G.hand.cards[i].healed then + valid_cards[#valid_cards+1] = G.hand.cards[i] + end + end + + if valid_cards[1] then + local target_card = pseudorandom_element(valid_cards, pseudoseed('heal'..G.GAME.round_resets.ante)) + --Set variable in advance because events are delayed + target_card.healed = true + + --Nullify hand xmult in advance + if target_card.config.center == G.P_CENTERS.m_unstb_radioactive then + target_card.ability.h_x_mult = 0 + end + + event({func = function() + target_card:heal() + play_sound('unstb_heal', 1, 0.3) + target_card:juice_up() + target_card.healed = nil + return true + end + }) + + forced_message("Healed!", target_card, G.C.GREEN, true) + end + end + end, + + --This cannot spawn naturally at all + in_pool = function(self, args) + return false + end + }) + +end + + +--New Enhancements + + function get_valid_card_from_deck(seed) + + local res_suit = 'Spades' + local res_rank = '2' + + local valid_cards = {} + for k, v in ipairs(G.playing_cards) do + if not v.config.center.replace_base_card then --Excludes all cards with replace_base_card enhancements + valid_cards[#valid_cards+1] = v + end + end + if valid_cards[1] then + local target_card = pseudorandom_element(valid_cards, pseudoseed(seed or 'validcard'..G.GAME.round_resets.ante)) + + res_suit = target_card.base.suit + res_rank = target_card.base.value + end + + return {suit = res_suit, rank = res_rank} +end + +--Patch get_chip_bonus to allow total chip override +local cardGetChipBonusPointer = Card.get_chip_bonus + +function Card:get_chip_bonus() + + if self.config.center.override_chip then + return self.config.center.override_chip + end + + return cardGetChipBonusPointer(self) +end + +if unstb_config.enh.enh_custom then + +--Acorn +SMODS.Enhancement { + key = "acorn", + atlas = "unstb_back", + pos = {x=0, y = 0}, + + replace_base_card = false, + no_suit = false, + no_rank = false, + always_scores = false, + override_chip = 0, + + + config = {extra = { totalchips = 0, originalchips = 0}}, + + loc_vars = function(self, info_queue, card) + return { + vars = { (card and (card.base.nominal + (card.ability.perma_bonus or 0)) *2) or 0 } + } + end, + + + + --Override genere_ui so it does not display any chips + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + SMODS.Enhancement.super.generate_ui(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + end, + + + calculate = function(self, card, context) + if context.cardarea == G.play and context.main_scoring then + local rulesJoker = SMODS.find_card('j_unstb_rules_errata') + + if next(rulesJoker) then + + --Makes the Joker bounce when this card score + event({trigger = 'after', func = function() + big_juice(rulesJoker[1]) + return true end }) + + return { + mult = card.base.nominal * 0.5, + } + + else + event({trigger = 'after', delay = 0.05, func = function() + play_sound('tarot2', 1, 0.4); + return true end }) + + forced_message("Not Allowed!", card, G.C.RED, true) + end + end + + if context.cardarea == G.hand and context.main_scoring then + card.ability.extra.totalchips = (card.base.nominal + (card.ability.perma_bonus or 0)) * 2 + + if not card.debuff then + --ret.unstb_h_chips = card.ability.extra.totalchips + return { + h_chips = card.ability.extra.totalchips + } + end + + end + end + } + +--Vintage +SMODS.Enhancement { + key = "vintage", + atlas = "unstb_back", + pos = {x=1, y = 0}, + + + config = {extra = { bonus_chip = 0, chip_gain_rate = 10, current_odd = 0, odd_destroy = 15, destroy_rate = 1}}, + + loc_vars = function(self, info_queue, card) + + local odds_current = 0 + local destroy_rate = 1 + + if card then + odds_current = card.ability.extra.current_odd or 0 + destroy_rate = card.ability.extra.destroy_rate + end + + if G.GAME and G.GAME.probabilities.normal then + odds_current = odds_current * G.GAME.probabilities.normal + destroy_rate = destroy_rate * G.GAME.probabilities.normal + end + + return { + vars = { self.config.extra.chip_gain_rate, odds_current, self.config.extra.odd_destroy, destroy_rate} + } + end, + + + + + calculate = function(self, card, context) + if context.cardarea == G.play and context.main_scoring then + card.ability.perma_bonus = (card.ability.perma_bonus or 0) + card.ability.extra.chip_gain_rate + card.ability.extra.current_odd = (card.ability.extra.current_odd or 0) + card.ability.extra.destroy_rate + + forced_message("Upgrade!", card, G.C.CHIPS, true) + end + end, + + after_play = function(self, card, context) + local isDestroy = pseudorandom('vintage'..G.SEED) < card.ability.extra.current_odd * G.GAME.probabilities.normal / card.ability.extra.odd_destroy + + if isDestroy then + event({trigger = 'after', delay = 0.05, func = function() + play_sound('tarot2', 1, 0.4); + return true end }) + + forced_message("Torn...", card, G.C.BLACK, true) + return { + to_destroy = true + } + end + end, + } + + --Promo +SMODS.Enhancement { + key = "promo", + atlas = "unstb_back", + pos = {x=2, y = 0}, + + replace_base_card = false, + no_suit = false, + no_rank = false, + always_scores = false, + + config = {extra = { gold = 0, gold_rate = 1, odds_destroy = 8}}, + + loc_vars = function(self, info_queue, card) + return { + vars = { (card and card.ability and card.ability.extra.gold) or 0, self.config.extra.gold_rate, (G.GAME and G.GAME.probabilities.normal or 1), self.config.extra.odds_destroy } + } + end, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.main_scoring then + card.ability.extra.gold = (card.ability.extra.gold or 0) + card.ability.extra.gold_rate + forced_message("Upgrade!", card, G.C.GOLD, true) + end + + if context.cardarea == G.hand and context.main_scoring then + if not card.debuff and card.ability.extra.gold>0 then + --ret.dollars = card.ability.extra.gold + return { + dollars = card.ability.extra.gold + } + end + end + + end, + + after_play = function(self, card, context) + --print("Trigger afterplay") + + local isDestroy = pseudorandom('promo'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_destroy + + if isDestroy then + event({trigger = 'after', delay = 0.05, func = function() + play_sound('tarot2', 1, 0.4); + return true end }) + + forced_message("Sold", card, G.C.ORANGE, true) + return { + to_destroy = true + } + end + end, + } + + --Slop +SMODS.Enhancement { + key = "slop", + + lc_atlas = "enh_slop", + hc_atlas = "enh_slop_hc", + + atlas = "enh_slop", + is_hc = false, + + pos = {x=0, y = 0}, + + replace_base_card = true, + no_suit = false, + no_rank = false, + always_scores = true, + + config = {extra = { suit = 'Spades', rank = '2', chips = 0}}, + + loc_vars = function(self, info_queue, card) + + local suit_text = '(Suit)' + local suit_text_color = G.C.ORANGE + + if card and card.ability then + suit_text = localize(card.ability.extra.suit, 'suits_plural'); + suit_text_color = G.C.SUITS[card.ability.extra.suit] + end + + return { + vars = { card and card.ability.extra.chips or 2, suit_text, localize(card and card.ability.extra.rank or '2', 'ranks') , + colours = {suit_text_color} } + } + end, + + + + suit_map = { + Hearts = 0, + Clubs = 1, + Diamonds = 2, + Spades = 3, + }, + + set_sprites = function(self, card, front) + local isCollection = (card.area and card.area.config.collection) or false + + if not isCollection and card.ability and card.ability.extra then + local suit = (card.base and card.base.suit) or 'Spades' + + local pos = get_coordinates( (self.suit_map[suit] or -1) +1 ) + + card.children.center:set_sprite_pos(pos) + end + + end, + + set_ability = function(self, card, initial, delay_sprites) + if card.base then + card.ability.extra.suit = card.base.suit or 'Hearts' + card.ability.extra.rank = card.base.value or '2' + card.ability.extra.chips = SMODS.Ranks[card.ability.extra.rank].nominal + end + end, + + update = function(self, card) + + --Jank, supporting for high contrast texture change settings + if G.SETTINGS.colourblind_option ~= card.atlasmode then + + if G.SETTINGS.colourblind_option then + self.atlas = self.hc_atlas + else + self.atlas = self.lc_atlas + end + card.children.center.atlas = G.ASSET_ATLAS[self.atlas] + card.atlasmode = G.SETTINGS.colourblind_option + + end + + --Update the value for the 'wise' player who tries to change card value using tarots + if (card.VT.w <= 0) and card.base.suit and card.base.value then + local isCollection = (card.area and card.area.config.collection) or false + + if not isCollection then + card.ability.extra.suit = card.base.suit or 'Hearts' + card.ability.extra.rank = card.base.value or '2' + card.ability.extra.chips = SMODS.Ranks[card.ability.extra.rank].nominal + + local suit = (card.base and card.base.suit) or 'Spades' + local pos = get_coordinates( (self.suit_map[suit] or -1) +1 ) + card.children.center:set_sprite_pos(pos) + end + end + end, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.main_scoring then + local scoredAmount = card.ability.extra.chips + card.ability.perma_bonus + --Store the current scoring hand, for after_play + --Had to check to not include itself and other Slop Cards so it won't have cyclic references + if not card.ability.extra.prevhand and context.scoring_hand then + local hand = {} + + for i=1, #context.scoring_hand do + if context.scoring_hand[i] ~= card and not context.scoring_hand[i].config.center.replace_base_card then + hand[#hand+1] = {suit = context.scoring_hand[i].base.suit, rank = context.scoring_hand[i].base.value} + end + end + + card.ability.extra.prevhand = hand + + --print(inspectDepth(card.ability.extra.prevhand)) + end + + return { + chips = scoredAmount, + } + + end + end, + + after_play = function(self, card, context) + + forced_message("Randomize!", card, G.C.RED, true) + + event({trigger = 'after', delay = 0.05, func = function() + card.ability.extra.suit = pseudorandom_element(SMODS.Suits, pseudoseed('slop_card')..G.SEED).key + card.ability.extra.rank = pseudorandom_element(SMODS.Ranks, pseudoseed('slop_card')..G.SEED).key + + local prompt_joker = SMODS.find_card('j_unstb_prompt') + + if next(prompt_joker) then + + if #card.ability.extra.prevhand>0 then + card.ability.extra.suit = pseudorandom_element(card.ability.extra.prevhand, pseudoseed('slop_card')..G.SEED).suit + card.ability.extra.rank = pseudorandom_element(card.ability.extra.prevhand, pseudoseed('slop_card')..G.SEED).rank + + --Bounce the Joker a lil bit for the feedback effect + big_juice(prompt_joker[1]) + end + + end + + --Clear the variable, unsure why this caused the game to crash when hovering on the deck if left uncleared + card.ability.extra.prevhand = nil + + card.ability.extra.chips = SMODS.Ranks[card.base.value].nominal + + local suit_data = SMODS.Suits[card.ability.extra.suit] + local suit_prefix = suit_data.card_key + + local targetCard = suit_prefix .. '_' ..SMODS.Ranks[card.ability.extra.rank].card_key + + --print(targetCard) + + card:set_base(G.P_CARDS[targetCard]) + + local suit = (card.base and card.base.suit) or 'Spades' + local pos = get_coordinates( (self.suit_map[suit] or -1) +1 ) + card.children.center:set_sprite_pos(pos) + + return true end }) + end, + + --This cannot spawn naturally at all + in_pool = function(self, args) + return false + end + } + + --Resource + SMODS.Enhancement { + key = "resource", + atlas = "enh_resource", + pos = {x=0, y = 0}, + + replace_base_card = true, + --no_suit = false, + no_rank = true, + always_scores = true, + + config = {extra = { xmult = 5, suit = "undefined"} }, + + loc_vars = function(self, info_queue, card) + + local suit_text = 'undefined' + local suit_text_color = {} + + if card.ability then + suit_text = card.ability.extra.suit; + + if suit_text ~= '(Corresponding Suit)' then + suit_text = localize(card.ability.extra.suit, 'suits_singular'); + suit_text_color = G.C.SUITS[card.ability.extra.suit] + else + suit_text_color = G.C.ORANGE + end + end + + return { + vars = { (card.ability and card.ability.extra.xmult) or 5, suit_text, + colours = {suit_text_color} } + } + end, + + suit_map = { + Hearts = 0, + Clubs = 1, + Diamonds = 2, + Spades = 3, + }, + + + + set_sprites = function(self, card, front) + + local isCollection = (card.area and card.area.config.collection) or false + + if not isCollection and card.ability and card.ability.extra then + local suit = (card.base and card.base.suit) or 'Spades' + + card.ability.extra.suit = suit + + local pos = get_coordinates( (self.suit_map[suit] or -1) +2 ) + + card.children.center:set_sprite_pos(pos) + end + + end, + + update = function(self, card) + if (card.VT.w <= 0 or card.ability.extra.suit == 'undefined') then + local isCollection = (card.area and card.area.config.collection) or false + + if not isCollection then + card.ability.extra.suit = card.base.suit + card.children.center:set_sprite_pos( get_coordinates( (self.suit_map[card.base.suit] or -1) +2 ) ) + else + card.ability.extra.suit = "(Corresponding Suit)" + end + end + end, + + calculate = function(self, card, context) + + if context.cardarea == G.play and context.main_scoring then + local has_suit = false + + if context.scoring_hand then + for i = 1, #context.scoring_hand do + local currentCard = context.scoring_hand[i] + if currentCard ~= card and currentCard.config.center ~= G.P_CENTERS.m_unstb_resource and currentCard:is_suit(card.ability.extra.suit) then + has_suit = true + break + end + end + end + + if has_suit then + --[[event({trigger = 'after', func = function() + play_sound('multhit1') + return true end })]] + + card.ability.extra.to_destroy = true + + return { + xmult = card.ability.extra.xmult, + } + + else + event({trigger = 'after', delay = 0.05, func = function() + play_sound('tarot2', 1, 0.4); + return true end }) + + forced_message("Invalid", card, G.C.ORANGE, true) + end + + return { + repetitions = -1, + card = context.other_card + } + end + end, + + --Injected the trigger using lovely, can be used for any post-play enchantment stuff + after_play = function(self, card, context) + if card.ability.extra.to_destroy then + forced_message("Redeemed!", card, G.C.RED, true) + + return { + to_destroy = true + } + end + end, + + --Can spawn when Catan mode enabled + in_pool = function(self, args) + return G.GAME.pool_flags.catan_enabled + end + } + + end + +--"Negative" Enhancements + +--Global function wrapper to set DisEnhancements +function Card:set_disenhancement(center, initial, delay_sprites) + + --If the card has heal seal + if self.seal and self.seal == 'unstb_heal' then + + if self.debuff then + --If debuff, remove the seal and proceed through the rest of the process + self.seal = nil + else + --If the card is on hand, show resisting animation + if self.area == G.hand then + event({func = function() play_sound('unstb_heal', 1, 0.3) return true end}) + forced_message("Resist!", self, G.C.GREEN, true) + elseif self.area == G.play then + --Play healing sound effect if it's in play + event({func = function() play_sound('unstb_heal', 1, 0.3) return true end}) + end + + --Don't turn the card into DisEnhancements + return + end + + end + + --If the card is debuffed, removed the heal seal entirely + if self.seal and self.seal == 'unstb_heal' and self.debuff then + self.seal = nil + end + + local old_center = self.config.center + + --Set the previous enhancement to keep track + if not old_center.disenhancement then + self.config.prev_center = old_center + end + + --Calling appropriate set_ability + self:set_ability(center, initial, delay_sprites) +end + +if unstb_config.enh.enh_disenh then + +--Global blacklist for card enhancements that cannot be converted by radioactive card +unstb_global.radioactive_blacklist = { +m_unstb_radioactive = true, +m_unstb_slop = true, +} + +--Radioactive +SMODS.Enhancement { + key = "radioactive", + atlas = "unstb_back", + pos = {x=3, y = 0}, + + + + replace_base_card = true, + no_suit = true, + no_rank = true, + always_scores = true, + + disenhancement = true, --exclusive property to check if it's disenhancement or not + + config = {extra = { chips = 13, odds_conv = 2, odds_mult = 3, mult_good = 1.75, mult_bad = 0.5 }, h_x_mult = 1}, + + loc_vars = function(self) + return { + vars = { self.config.extra.chips, (G.GAME and G.GAME.probabilities.normal or 1), self.config.extra.odds_conv, self.config.extra.odds_mult, self.config.extra.mult_good, self.config.extra.mult_bad } + } + end, + + + + set_ability = function(self, card, initial, delay_sprites) + --Set the hand multiplier the first time + if pseudorandom('radioactive'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_mult then + card.ability.h_x_mult = card.ability.extra.mult_good + else + card.ability.h_x_mult = card.ability.extra.mult_bad + end + end, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.main_scoring then + if #context.scoring_hand > 1 then + + --polling the eligible hand first + + local eligible_hand = {} + + for i=1, #context.scoring_hand do + --Exclude slop card because it interacts horribly with this + if not unstb_global.radioactive_blacklist[context.scoring_hand[i].config.center.key] and not context.scoring_hand[i].to_convert then + eligible_hand[#eligible_hand+1] = context.scoring_hand[i] + end + end + + --Update: no longer show "Safe!" if the entire hand is not convertable + if #eligible_hand > 0 then + local target = pseudorandom_element(eligible_hand, pseudoseed('radioactive_conv'..G.SEED)) + if pseudorandom('radioactive'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_conv then + target.to_convert = true + --Flipping Animation + event({trigger = 'after', delay = 0.1, func = function() target:flip(); play_sound('card1', 1); target:juice_up(0.3, 0.3); return true end }) + + --Changing Card Property + + event({trigger = 'after', delay = 0.05, func = function() + + target:set_disenhancement(G.P_CENTERS.m_unstb_radioactive) + + return true end }) + + --Unflipping Animation + event({trigger = 'after', delay = 0.1, func = function() target:flip(); play_sound('tarot2', 1, 0.6); big_juice(card); target:juice_up(0.3, 0.3); target.to_convert = nil; return true end }) + + forced_message("Decayed!", target, G.C.RED, true) + else + forced_message("Safe!", card, G.C.GREEN, true) + end + end + + end + + return { + chips = card.ability.extra.chips + } + + + end + + if context.cardarea == G.hand and not card.healed and context.main_scoring then + --Xmult handling ability is built-in, so this one just checks for odds and alter it respectively. + if pseudorandom('radioactive'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_mult then + card.ability.h_x_mult = card.ability.extra.mult_good + else + card.ability.h_x_mult = card.ability.extra.mult_bad + end + + end + end, + + --This cannot spawn naturally at all + in_pool = function(self, args) + return false + end + } + +SMODS.Enhancement { + key = "biohazard", + atlas = "unstb_back", + pos = {x=4, y = 0}, + + replace_base_card = true, + no_suit = true, + no_rank = true, + always_scores = true, + + disenhancement = true, + + config = {extra = { xmult = 0.9, h_money = 1, odds_conv = 3}}, + + loc_vars = function(self) + return { + vars = { self.config.extra.xmult, self.config.extra.h_money, (G.GAME and G.GAME.probabilities.normal or 1), self.config.extra.odds_conv } + } + end, + + calculate = function(self, card, context) + + if context.cardarea == G.play and context.main_scoring then + return { + xmult = card.ability.extra.xmult, + } + end + + if context.discard and context.other_card == card then + local is_neutralized = next(SMODS.find_card('j_unstb_vaccination_card')) + + --check if it is activated + if pseudorandom('biohazard'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_conv and not is_neutralized then + + --check hand card + local hand_card = {} + for i = 1, #G.hand.cards do + hand_card[G.hand.cards[i]] = true + end + + --populate valid cards + local valid_cards = {} + for k, v in ipairs(G.playing_cards) do + if v.config.center ~= G.P_CENTERS.m_unstb_biohazard and not hand_card[v] then --Exclude biohazard cards + valid_cards[#valid_cards+1] = v + end + end + + if valid_cards[1] then + local target_card = pseudorandom_element(valid_cards, pseudoseed(seed or 'validcard'..G.GAME.round_resets.ante)) + target_card:set_disenhancement(G.P_CENTERS.m_unstb_biohazard , nil, true) + end + + end + + end + + if context.cardarea == G.hand and not card.healed and context.main_scoring then + + local is_neutralized = next(SMODS.find_card('j_unstb_vaccination_card')) + + if not card.debuff and not is_neutralized then + return { + dollars = -card.ability.extra.h_money + } + end + end + end, + + --This cannot spawn naturally at all + in_pool = function(self, args) + return false + end + } + +SMODS.Enhancement { + key = "poison", + atlas = "unstb_back", + pos = {x=5, y = 0}, + + replace_base_card = false, + no_suit = false, + no_rank = false, + always_scores = true, + + disenhancement = true, + + override_chip = 0, + + config = {extra = { chip = 0.9, h_money = 1}}, + + loc_vars = function(self, info_queue, card) + + local chip = 0 + if card and card.base then + chip = card.base.nominal + (card.ability.perma_bonus or 0) + end + + return { + vars = { chip , self.config.extra.h_money } + } + end, + + --Override generate_ui so it does not display any chips + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + SMODS.Enhancement.super.generate_ui(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + end, + + + calculate = function(self, card, context) + + if context.cardarea == G.play and context.main_scoring then + local totalChip = card.base.nominal + (card.ability.perma_bonus or 0) --Counted bonus chips too, go crying + return { + chips = -totalChip, + colour = G.C.RED + } + end + end, + + discard_override = function(self, card, args) --Discard overridden function, injected by Lovely + card.ability.discarded = nil + + draw_card(G.hand, G.deck, args.delay, 'down', false, card) + + --Wrap shuffle in the event instead, so it should work now hopefully??? + event({trigger = 'after', func = function() + G.deck:shuffle('poison'..G.GAME.round_resets.ante) + return true end}) + end, + + --This cannot spawn naturally at all + in_pool = function(self, args) + return false + end + } + +end + +--Add proper tag to stone cards, nothing should change gameplay-wise +SMODS.Enhancement:take_ownership('m_stone', { + replace_base_card = true, + no_suit = true, + no_rank = true, + always_scores = true, + }, true) + +--New Ranks + +--Hook for Card:get_id() + +local decimal_rank_map = { ['unstb_0.5'] = 'unstb_1', unstb_r2 = '2', unstb_e = '3', unstb_Pi = '4'} + +--Expose this part so other mods can register decimal rank used for Engineer +function unstb_global.register_decimal_rank_map(decimal_rank, integer_rank) + decimal_rank_map[decimal_rank] = integer_rank +end + +local ref_getid = Card.get_id +function Card:get_id() + + local engineer_joker = next(SMODS.find_card('j_unstb_engineer')) + + --If 'Engineer' Joker exists, returns rounded up rank instead + if engineer_joker and SMODS.Ranks[self.base.value].is_decimal then + return SMODS.Ranks[decimal_rank_map[self.base.value]].id + end + + return ref_getid(self) +end + +--Function wrapper to check if a card has decimal rank + +local function is_decimal(card) + return (SMODS.Ranks[card.base.value].is_decimal or SMODS.Ranks[card.base.value].decimal_compat) and not card.config.center.no_rank +end + +--Hook for Poker Hand name + +local ref_get_poker_hand_info = G.FUNCS.get_poker_hand_info + +G.FUNCS.get_poker_hand_info = function(_cards) + local text, loc_disp_text, poker_hands, scoring_hand, disp_text = ref_get_poker_hand_info(_cards) + --print(disp_text) + + if string.find(disp_text, 'Straight') then + for i=1, #scoring_hand do + if is_decimal(scoring_hand[i]) then + loc_disp_text = string.gsub(loc_disp_text, 'Straight', 'Gay') + break + end + end + end + + return text, loc_disp_text, poker_hands, scoring_hand, disp_text +end + + +--Pool flag wrapper function to help assist managing ranks enable / disable +function setPoolRankFlagEnable(rank, isEnable) + if not G.GAME or G.GAME.pool_flags[rank] == isEnable then return end + + G.GAME.pool_flags[rank] = isEnable +end + +function getPoolRankFlagEnable(rank) + return (G.GAME and G.GAME.pool_flags[rank] or false) +end + +--Shared pool rank checking function +local function unstb_rankCheck(self, args) + if args and args.initial_deck then + return false + end + + return getPoolRankFlagEnable(self.key) +end + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = '0', + card_key = '0', + pos = { x = 6 }, + nominal = 0, + + strength_effect = { + fixed = 2, + random = false, + ignore = false + }, + next = { 'unstb_0.5', 'unstb_1', 'unstb_r2' }, + + prev = { 'unstb_???' }, + + shorthand = '0', + + straight_edge = true, + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = '0.5', + card_key = '0.5', + pos = { x = 2 }, + nominal = 0.5, + + next = {'unstb_1', 'unstb_r2', '2', 'unstb_e' }, + + prev = { 'unstb_0' }, + + shorthand = '0.5', + + is_decimal = true, + rank_act = {'0', '0.5', '1'}, + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = '1', + card_key = '1', + pos = { x = 5 }, + nominal = 1, + strength_effect = { + fixed = 2, + random = false, + ignore = false + }, + next = {'unstb_r2', '2', 'unstb_e' }, + + prev = { 'unstb_0' }, + + shorthand = '1', + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = 'r2', + card_key = 'R', + pos = { x = 7 }, + nominal = 1.41, + + next = {'2', 'unstb_e', '3', 'unstb_Pi' }, + prev = { 'unstb_1' }, + + shorthand = '/2', + + is_decimal = true, + rank_act = {'1', '1.41', '2'}, + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = 'e', + card_key = 'E', + pos = { x = 3 }, + nominal = 2.72, + + next = { '3', 'unstb_Pi', '4' }, + prev = { '2' }, + + shorthand = 'e', + + is_decimal = true, + rank_act = {'2', '2.72', '3'}, + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = 'Pi', + card_key = 'P', + pos = { x = 4 }, + nominal = 3.14, + + next = { '4', '5' }, + prev = { '3' }, + + shorthand = 'Pi', + + is_decimal = true, + rank_act = {'3', '3.14', '4'}, + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = '???', + card_key = '?', + pos = { x = 1 }, + nominal = 0, + next = { 'unstb_???' }, + prev = { 'unstb_???' }, + shorthand = '?', + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex_hc', + lc_atlas = 'rank_ex', + + + hidden = true, + + key = '21', -- the number or name (ex. "Jack") of your rank if it has one + card_key = '21', -- the short key put after the suit when coding a card object (ex. for the card "H_5" the card_key is 5). this seems to usually match the shorthand + pos = { x = 0 }, -- x position on the card atlas + nominal = 21, -- the number of chips this card scores + next = { 'unstb_???' }, -- the next rank directly above it, used for Strength Tarot + prev = { 'unstb_???' }, -- UNSTB addition: the previous rank directly below it + shorthand = '21', -- used for deck preview + + in_pool = unstb_rankCheck, +} + +-- "Premium Pack" Ranks +SMODS.Rank { + hc_atlas = 'rank_ex2_hc', + lc_atlas = 'rank_ex2', + + hidden = true, + + key = '11', + card_key = '11', + pos = { x = 0 }, + nominal = 11, + next = { 'unstb_12' }, + prev = {'10'}, + shorthand = '11', + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex2_hc', + lc_atlas = 'rank_ex2', + + hidden = true, + + key = '12', + card_key = '12', + pos = { x = 1 }, + nominal = 12, + next = { 'unstb_13' }, + prev = {'unstb_11'}, + shorthand = '12', + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex2_hc', + lc_atlas = 'rank_ex2', + + hidden = true, + + key = '13', + card_key = '13', + pos = { x = 2 }, + nominal = 13, + next = { 'Ace' }, + prev = {'unstb_12'}, + shorthand = '13', + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex2_hc', + lc_atlas = 'rank_ex2', + + hidden = true, + + key = '25', + card_key = '25', + pos = { x = 3 }, + nominal = 25, + next = { 'unstb_???' }, + prev = { 'unstb_???' }, + shorthand = '25', + + in_pool = unstb_rankCheck, +} + +SMODS.Rank { + hc_atlas = 'rank_ex2_hc', + lc_atlas = 'rank_ex2', + + hidden = true, + + key = '161', + card_key = '161', + pos = { x = 4 }, + nominal = 161, + next = { 'unstb_???' }, + prev = { 'unstb_???' }, + shorthand = '161', + + in_pool = unstb_rankCheck, +} +-- + +SMODS.Ranks['2'].strength_effect = { + fixed = 2, + random = false, + ignore = false + } +SMODS.Ranks['2'].next = {'unstb_e', '3', 'unstb_Pi'} + +SMODS.Ranks['3'].strength_effect = { + fixed = 2, + random = false, + ignore = false + } +SMODS.Ranks['3'].next = {'unstb_Pi', '4'} + +--Change straight edge off from Ace, so it start to look at rank 0 instead +--SMODS.Ranks['Ace'].straight_edge = false +SMODS.Ranks['Ace'].strength_effect = { + fixed = 2, + random = false, + ignore = false + } +SMODS.Ranks['Ace'].next = {'unstb_r2', '2', 'unstb_e'} + +--Vanilla Rank Alteration for Set 2 +SMODS.Ranks['10'].next = {'Jack', 'unstb_11'} + +--Add preliminary prev property into vanilla rank list, so the default behavior will always point to this one +local vanilla_rank_list = {'2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace'} + +for i=#vanilla_rank_list, 2, -1 do + SMODS.Ranks[vanilla_rank_list[i]].prev = {vanilla_rank_list[i-1]} +end +SMODS.Ranks['2'].prev = {'Ace'} + +--Add a custom in_pool for high vanilla ranks, so they can be hidden from appearing in Lowkey Combo Decks HUD + +for i=#vanilla_rank_list, 5, -1 do + SMODS.Ranks[vanilla_rank_list[i]].in_pool = function() + return not G.GAME.prevent_high_rank + end +end + +--Booster Pack for Premium Card + +local premium_booster_rate = {0.75, 0.75, 0.5, 0.1} +local premium_booster_cost = {4, 4, 6, 8} + +--Needs all three ranks setting to be on to start appearing +if check_enable_taglist({'rank_binary', 'rank_decimal', 'rank_21'}) then + +for i = 1, 4 do + SMODS.Booster{ + key = 'prem_'..(i <= 2 and i or i == 3 and 'jumbo' or 'mega'), + cost = premium_booster_cost[i], + + config = {extra = i <= 2 and 3 or 5, choose = i <= 3 and 1 or 2}, + draw_hand = false, + + create_card = function(self, card) + local card = create_card("Base", G.pack_cards, nil, nil, nil, true, nil, 'prem') + local edition_rate = 3 + local edition = poll_edition('premium_edition'..G.GAME.round_resets.ante, edition_rate, true) + + local rank_set = {"unstb_0", "unstb_0.5", "unstb_1", "unstb_r2", "unstb_e", "unstb_Pi", "unstb_11", "unstb_12", "unstb_13", "unstb_21", "unstb_25", "unstb_161"} + + --Lowkey Deck / Sleeve combo mode, prevent high rank from being spawned + if G.GAME.prevent_high_rank then + rank_set = {"unstb_0", "unstb_0.5", "unstb_1", "unstb_r2", "unstb_e", "unstb_Pi"} + end + + SMODS.change_base(card, nil, pseudorandom_element(rank_set, pseudoseed('premium_rank'..G.GAME.round_resets.ante))) + + --Pooling Enhancements + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if not v.replace_base_card and not v.disenhancement then + cen_pool[#cen_pool+1] = v + end + end + + local enh = pseudorandom_element(cen_pool, pseudoseed('premium_enhance')) + + card:set_ability(enh) + + card:set_edition(edition) + card:set_seal(SMODS.poll_seal({mod = 10})) + + return card + -- return {set = 'Polymino', area = G.pack_cards, skip_materialize = nil, soulable = nil, key_append = 'vir'} + end, + + ease_background_colour = function(self) ease_background_colour{new_colour = HEX('62a1b4'), special_colour = HEX('fce1b6'), contrast = 2} end, + particles = function(self) + G.booster_pack_sparkles = Particles(1, 1, 0,0, { + timer = 0.015, + scale = 0.3, + initialize = true, + lifespan = 3, + speed = 0.2, + padding = -1, + attach = G.ROOM_ATTACH, + colours = {G.C.BLACK, G.C.GOLD}, + fill = true + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + end, + + pos = get_coordinates(i+3), + atlas = 'booster', + + weight = premium_booster_rate[i], + } +end + +end + + +-- Poker Hand Rewrite to support new ranks probably? +-- Currently unused: Straights use SMODS Implementation perfectly fine + +local function get_keys(t) + local keys={} + for key,_ in pairs(t) do + table.insert(keys, key) + end + return keys +end + +local isAllowDecimal = true + +local decimalHands = {['unstb_0.5'] = {0, 1}, ['unstb_e'] = {2, 3}, ['unstb_Pi'] = {3, 4}} + +--Unused, redirect the entire straight calculation to ustb_get_straight instead due to straight_edge shenanigans +function ustb_get_straight_wrapper(hand) + + local ret = {} + if #hand < (5 - (four_fingers and 1 or 0)) then return ret end + + local hasDecimal = false + for i = 1, #hand do + if decimalHands[hand[i].base.value] then + hasDecimal = true + break + end + end + + return ustb_straight_calculation(hand) + + --[[if hasDecimal then + --print('Has decimal') + return ustb_straight_decimal(hand) + else + --print('do not has decimal') + return get_straight(hand) + end]] +end + +function ustb_get_straight(hand) + --Basically SMOD's implementation of straight_edge + --But with a slightly different fix to make extra ranks work + + local ret = {} + local four_fingers = next(SMODS.find_card('j_four_fingers')) + local can_skip = next(SMODS.find_card('j_shortcut')) + + local can_loop = next(SMODS.find_card('j_unstb_portal')) + + if #hand < (5 - (four_fingers and 1 or 0)) then return ret end + + local t = {} + local RANKS = {} + for i = 1, #hand do + if hand[i]:get_id() > 0 then + local rank = hand[i].base.value + RANKS[rank] = RANKS[rank] or {} + RANKS[rank][#RANKS[rank] + 1] = hand[i] + end + end + + local straight_length = 0 + local straight = false + local skipped_rank = false + local vals = {} + for k, v in pairs(SMODS.Ranks) do + if v.straight_edge then + table.insert(vals, k) + end + end + + local init_vals = {} + for _, v in ipairs(vals) do + init_vals[v] = true + end + if not next(vals) then table.insert(vals, 'Ace') end + + local initial = true + local br = false + local end_iter = false + local i = 0 + + --print(inspect(vals)) + + --Manually swapping vals because it would not work properly if it starts at Ace + --However, Ace has to be kept otherwise it allows the loop back + + --TODO: Honestly could figure out the proper logic so it can be converted to lovely patch instead + + vals[1] = 'unstb_0' + --vals[2] = 'Ace' + + if not can_loop then + vals[2] = 'Ace' + end + + --If there is no possible RANKS, breaks the function early + if not next(RANKS) then return ret end + + while 1 do + end_iter = false + if straight_length >= (5 - (four_fingers and 1 or 0)) then + straight = true + end + i = i + 1 + if br or (i > #SMODS.Rank.obj_buffer + 1) then break end + if not next(vals) then break end + for _, val in ipairs(vals) do + --print(val) + --print('===') + if init_vals[val] and not initial and not can_loop then br = true end + if RANKS[val] then + straight_length = straight_length + 1 + skipped_rank = false + for _, vv in ipairs(RANKS[val]) do + t[#t + 1] = vv + end + + vals = SMODS.Ranks[val].next + + --print(inspect(vals)) + --print('---') + + initial = false + end_iter = true + break + end + end + if not end_iter then + local new_vals = {} + for _, val in ipairs(vals) do + for _, r in ipairs(SMODS.Ranks[val].next) do + table.insert(new_vals, r) + end + end + vals = new_vals + if can_skip and not skipped_rank then + skipped_rank = true + else + straight_length = 0 + skipped_rank = false + if not straight then t = {} end + if straight then break end + end + end + end + if not straight then return ret end + table.insert(ret, t) + + return ret +end + +SMODS.PokerHandPart:take_ownership('_straight', { + func = function(hand) return ustb_get_straight(hand) end +}) + +--Generic consumable selection check utility function +--Written this way for Cryptid compatibility +local function get_consumable_use_hand_count(card, hand) + local selected_cards = {} + for i = 1, #hand do + if hand[i] ~= card then selected_cards[#selected_cards+1] = hand[i] end + end + return #selected_cards +end + +--Returns selected hands excluding itself (in case of Cryptid merged consumable cards) +local function get_selected_cards(card, hand) + local selected_cards = {} + for i = 1, #hand do + if hand[i] ~= card then selected_cards[#selected_cards+1] = hand[i] end + end + return selected_cards +end + +--Auxiliary Card + +if unstb_config.gameplay.c_aux then + +SMODS.ConsumableType{ + key = 'Auxiliary', + primary_colour = HEX('424e54'), + secondary_colour = G.C.UNSTB_AUX, + loc_txt = {}, + default = 'c_unstb_aux_seal_move', + collection_rows = {5, 5} +} + +SMODS.UndiscoveredSprite{ + key = 'Auxiliary', + atlas = 'auxiliary_undiscovered', + pos = get_coordinates(0) +} + +--Booster Pack for Auxiliary Cards + +local aux_booster_rate = {1, 1, 1, 0.25} +local aux_booster_cost = {4, 4, 6, 8} + +for i = 1, 4 do + SMODS.Booster{ + key = 'aux_'..(i <= 2 and i or i == 3 and 'jumbo' or 'mega'), + cost = aux_booster_cost[i], + + config = {extra = i <= 2 and 3 or 5, choose = i <= 3 and 1 or 2}, + draw_hand = false, + + create_card = function(self, card) + local card = create_card('Auxiliary', G.pack_cards, nil, nil, true, true, nil, 'aux') + local neg_chance = pseudorandom('aux_rng') + + if neg_chance < 0.4 and G.GAME.used_vouchers.v_unstb_aux2 and card.config.center.key ~= 'c_unstb_aux_dark_matter' then + card:set_edition({negative = true}, true) + end + + return card + end, + + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.UNSTB_AUX) + ease_background_colour{new_colour = HEX('477978'), special_colour = HEX('00a669'), contrast = 4} + end, + + pos = get_coordinates(i-1), + atlas = 'booster', + + weight = aux_booster_rate[i], + + --in_pool = function() return (pseudorandom('aux'..G.SEED) < aux_booster_rate[i]) end + } +end + +--Original Reserve Card Button Code from MoreFluff (which credits to Cryptid and Betmma) +G.FUNCS.can_take_card = function(e) + local card = e.config.ref_table + + --If the edition is negative, adds one more buffer to check if you can take it or not + local neg_buffer = (card.edition or {}).key == 'e_negative' and 1 or 0 + + if #G.consumeables.cards < G.consumeables.config.card_limit + neg_buffer then + e.config.colour = G.C.GREEN + e.config.button = "take_card" + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + end +end + +G.FUNCS.take_card = function(e) +local c1 = e.config.ref_table +G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + c1.area:remove_card(c1) + c1:add_to_deck() + if c1.children.price then + c1.children.price:remove() + end + c1.children.price = nil + if c1.children.buy_button then + c1.children.buy_button:remove() + end + c1.children.buy_button = nil + remove_nils(c1.children) + + play_sound('generic1') + + G.consumeables:emplace(c1) + G.GAME.pack_choices = G.GAME.pack_choices - 1 + if G.GAME.pack_choices <= 0 then + G.FUNCS.end_consumeable(nil, delay_fac) + end + return true + end, +})) +end + +local G_UIDEF_card_focus_ui_ref = G.UIDEF.card_focus_ui + +function G.UIDEF.card_focus_ui(card) + base_background = G_UIDEF_card_focus_ui_ref(card) + local card_width = card.T.w + (card.ability.consumeable and -0.1 or card.ability.set == 'Voucher' and -0.16 or 0) + local base_attach = base_background:get_UIE_by_ID('ATTACH_TO_ME') + + if ((card.area == G.pack_cards and G.pack_cards)) and card.ability.consumeable and card.ability.set == "Auxiliary" then + base_attach.children.use = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'select', + func = 'can_take_card', button = 'use_card', card_width = card_width + } + end + + return base_background +end + +local G_UIDEF_use_and_sell_buttons_ref = G.UIDEF.use_and_sell_buttons +function G.UIDEF.use_and_sell_buttons(card) + if (card.area == G.pack_cards and G.pack_cards) and card.ability.consumeable then --Add a take button for Auxiliary pack + if card.ability.set == "Auxiliary" then + return { + n = G.UIT.ROOT, + config = { padding = -0.1, colour = G.C.CLEAR }, + nodes = { + { + n = G.UIT.R, + config = { + ref_table = card, + r = 0.08, + padding = 0.1, + align = "bm", + minw = 0.5 * card.T.w - 0.15, + minh = 0.7 * card.T.h, + maxw = 0.7 * card.T.w - 0.15, + hover = true, + shadow = true, + colour = G.C.UI.BACKGROUND_INACTIVE, + one_press = true, + button = "use_card", + func = "can_take_card", + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'SELECT', + colour = G.C.UI.TEXT_LIGHT, + scale = 0.55, + shadow = true, + }, + }, + }, + }, + }, + } + end + end + return G_UIDEF_use_and_sell_buttons_ref(card) +end + +--Auxiliary Cards Code Starts Here-- + +if unstb_config.gameplay.seal_suit then + +--Suit Seals Addition Cards +for i = 1, #suit_seal_list do + SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_'..string.lower(suit_seal_list[i]), + + config = {extra = {count = 2, seal = 'unstb_'..string.lower(suit_seal_list[i])}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + local suit = localize(suit_seal_list[i], 'suits_singular') ..' Seal' + local suit_color = G.C.SUITS[suit_seal_list[i]] + + info_queue[#info_queue+1] = {set = 'Other', key = 'suit_seal'} + + return {vars = {card.ability.extra.count, suit, colours = {suit_color}}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) >= 1 and get_consumable_use_hand_count(card, G.hand.highlighted) <= card.ability.extra.count) then + return true + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + for i = 1, #G.hand.highlighted do + event({trigger = 'after', delay = 0.2, + func = function() + G.hand.highlighted[i]:set_seal(card.ability.extra.seal, nil, true) + return true end }) + end + + delay(0.5) + event({trigger = 'after', delay = 0.2, + func = function() + G.hand:unhighlight_all(); + return true end }) + + + end, + + pos = get_coordinates(i+1), + } +end + +--Face Seal Card +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_face', + + config = {extra = {count = 2, seal = 'unstb_face'}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'unstb_face_seal'} + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) >= 1 and get_consumable_use_hand_count(card, G.hand.highlighted) <= card.ability.extra.count) then + return true + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + for i = 1, #G.hand.highlighted do + event({trigger = 'after', delay = 0.2, + func = function() + G.hand.highlighted[i]:set_seal(card.ability.extra.seal, nil, true) + return true end }) + end + + delay(0.5) + event({trigger = 'after', delay = 0.2, + func = function() + G.hand:unhighlight_all(); + return true end }) + + + end, + + pos = get_coordinates(6), +} + +end + +-- +2 +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_plus_two', + + config = {extra = {count = 2}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + local selected_hands = get_selected_cards(card, G.hand.highlighted) + if G.hand and (#selected_hands == 1) and not selected_hands[1].config.center.no_suit then + return true + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local targetCard = G.hand.highlighted[1] + + event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i=1, card.ability.extra.count do + cards[i] = true + local _rank = pseudorandom_element(SMODS.Ranks, pseudoseed('aux_plus_two')) or SMODS.Ranks['2'] + local _suit = SMODS.Suits[targetCard.base.suit] + + create_playing_card({front = G.P_CARDS[(_suit.card_key)..'_'..(_rank.card_key)], center = G.P_CENTERS.c_base}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral}) + end + playing_card_joker_effects(cards) + return true end }) + end, + + pos = get_coordinates(7), +} + +-- Wild +4 +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_plus_four_wild', + + config = {extra = {count = 4}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + local selected_hands = get_selected_cards(card, G.hand.highlighted) + if G.hand and (#selected_hands == 1) and not selected_hands[1].config.center.no_rank then + return true + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local targetCard = G.hand.highlighted[1] + + event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i=1, card.ability.extra.count do + cards[i] = true + local _rank = SMODS.Ranks[targetCard.base.value] + local _suit = pseudorandom_element(SMODS.Suits, pseudoseed('aux_wild_plus_four')) or SMODS.Suits['Spades'] + + create_playing_card({front = G.P_CARDS[(_suit.card_key)..'_'..(_rank.card_key)], center = G.P_CENTERS.c_base}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral}) + end + playing_card_joker_effects(cards) + return true end }) + end, + + pos = get_coordinates(8), +} + +-- In-round Instants +local function get_instant_effect(id, amount) + if id == 3 then + ease_hands_played(amount) + elseif id == 1 then + ease_discard(amount) + elseif id == 2 then + --Base game has this for tags, so I utilize them here + G.hand:change_size(amount) + G.FUNCS.draw_from_deck_to_hand(amount) + + G.GAME.round_resets.temp_handsize = (G.GAME.round_resets.temp_handsize or 0) + amount + else + forced_message("Nope!", currentCard, G.C.PURPLE, true) + end +end + +local aux_instants = {'aux_inst_disc', 'aux_inst_hsize', 'aux_inst_hand'} + +for i = 1, #aux_instants do + SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = aux_instants[i], + + config = {extra = {amount = 3}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.amount}} + end, + + can_use = function(self, card) + --Only usable in-round. I can't believe it had to be checked *this much* + return G.hand and not G.blind_select and G.STATE ~= G.STATES.ROUND_EVAL and not G.shop and not G.booster_pack + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + event({ + trigger = 'after', + delay = 0.2, + func = function() + play_sound('generic1') + + get_instant_effect(i, card.ability.extra.amount) + return true end }) + end, + + pos = get_coordinates(8+i), + } +end + +--Seal Swap +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_seal_move', + + config = {extra = {}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) == 2) then + return true + end + return false + end, + + use = function(self, card) + local card_l_seal = G.hand.highlighted[1].seal + + --Animation code mostly ported from basegame tarot + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + for i=1, #G.hand.highlighted do + local percent = 1.15 - (i-0.999)/(#G.hand.highlighted-0.998)*0.3 + 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) + + --Handle the conversion + event({trigger = 'after',delay = 0.1,func = function() + G.hand.highlighted[1]:set_seal(G.hand.highlighted[2].seal, true, true) + G.hand.highlighted[2]:set_seal(card_l_seal, true, true) + + --If a heal seal is on disenhanced card, heal it + for i=1, #G.hand.highlighted do + if G.hand.highlighted[i].config.center.disenhancement and G.hand.highlighted[i].seal == 'unstb_heal' then + G.hand.highlighted[i]:heal() + end + end + + + return true end }) + + for i=1, #G.hand.highlighted do + local percent = 0.85 + (i-0.999)/(#G.hand.highlighted-0.998)*0.3 + 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 + event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end }) + delay(0.5) + end, + + pos = get_coordinates(12), +} + +if unstb_config.rank.rank_binary then + +--All for One +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_conv_1', + + config = {extra = {count = 3}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) >= 1) and (get_consumable_use_hand_count(card, G.hand.highlighted) <= card.ability.extra.count) then + return true + end + return false + end, + + use = function(self, card) + --Enable Rank Flag + setPoolRankFlagEnable('unstb_1', true); + + --Animation code mostly ported from basegame tarot + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + for i=1, #G.hand.highlighted do + local percent = 1.15 - (i-0.999)/(#G.hand.highlighted-0.998)*0.3 + 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) + + --Handle the conversion + for i=1, #G.hand.highlighted do + event({trigger = 'after',delay = 0.1,func = function() + SMODS.change_base(G.hand.highlighted[i], G.hand.highlighted[i].base.suit, 'unstb_1') + 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 + 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 + event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end }) + delay(0.5) + end, + + pos = get_coordinates(13), +} + +end + +if unstb_config.rank.rank_21 then + +--The Twenty-One +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_21', + + config = {extra = {count = 5}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + local selected_hands = get_selected_cards(card, G.hand.highlighted) + if G.hand and (#selected_hands >= 1) and (#selected_hands <= card.ability.extra.count) and blackJack_evalrank(selected_hands, 21)>=21 then + return true + end + return false + end, + + use = function(self, card) + --Enable Rank Flag + setPoolRankFlagEnable('unstb_21', true); + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Populate removed cards + local destroyed_cards = {} + for i = 1, #G.hand.highlighted do destroyed_cards[#destroyed_cards+1] = G.hand.highlighted[i] end + + --Destroy Selected Card + event({ + trigger = 'after', + delay = 0.1, + func = function() + for i=#destroyed_cards, 1, -1 do + local c = destroyed_cards[i] + if c.ability.name == 'Glass Card' then + c:shatter() + else + c:start_dissolve(nil, i == #destroyed_cards) + end + end + return true end }) + --Adds Card + event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {true} + local _rank = SMODS.Ranks['unstb_21'] + local _suit = pseudorandom_element(SMODS.Suits, pseudoseed('aux_21')) or SMODS.Suits['Spades'] + + create_playing_card({front = G.P_CARDS[(_suit.card_key)..'_'..(_rank.card_key)], center = G.P_CENTERS.c_base}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral}) + playing_card_joker_effects(cards) + return true end }) + + --Call joker calculations for post-destroyed stuff + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({remove_playing_cards = true, removed = destroyed_cards}) + end + end, + + pos = get_coordinates(14), +} + +end + +if check_enable_taglist({'edition_upgrade', 'enh_disenh'}) then + +--Monkey Paw +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_upgrade', + + config = {extra = {}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = {set = 'Other', key = 'upgrade_edition'} + info_queue[#info_queue+1] = {set = 'Other', key = 'poison_tooltip'} + + return {vars = {}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) == 1) then + return true + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local selected_card = G.hand.highlighted[1] + local selected_index + + local poisoned_card = {} + + --look for two adjacent cards in hand + for i=1, #G.hand.cards do + if G.hand.cards[i] == selected_card then + selected_index = i + end + end + + --print(selected_index) + + --Cover edge cases + + local handCount = #G.hand.cards + + if handCount >=3 then --If the hand has 3 cards or more, pick two adjacent cards to poison + if selected_index>1 then + poisoned_card[1] = G.hand.cards[selected_index-1] + else + poisoned_card[1] = G.hand.cards[handCount] + end + + if selected_index 10 + end, +} + +--Heal Hand +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_heal_hand', + + config = {extra = {}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'disenhancement'} + + return {vars = {card.ability.extra.count}} + end, + + weight = 10, --Large chance to appear when it is needed + + can_use = function(self, card) + --Usable only in-game + if G.hand and not G.blind_select and G.STATE ~= G.STATES.ROUND_EVAL and not G.shop and not G.booster_pack then + + --Check if the hand contains at least one DisEnhancements + for i = 1, #G.hand.cards do + if G.hand.cards[i].config.center.disenhancement then + return true + end + end + + return false + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Populate List of cards to heal + local heal_cards = {} + for i = 1, #G.hand.cards do + if G.hand.cards[i].config.center.disenhancement then + heal_cards[#heal_cards+1] = G.hand.cards[i] + end + end + + if #heal_cards>0 then + play_sound('unstb_heal', 1, 0.3) + end + + for i = 1, #heal_cards do + event({trigger = 'after', delay = 0.1, func = function() + heal_cards[i]:heal() + play_sound('tarot2', 1, 0.4) + heal_cards[i]:juice_up() + return true + end + }) + end + + end, + + pos = get_coordinates(17), + + --Can spawn only when more than 1/4 of deck is Disenhanced + in_pool = function(self, args) + + --For some reason, not checking this crash Cryptid on start up + if not G.playing_cards then return false end + + local count = 0 + + for k, v in ipairs(G.playing_cards) do + if v.config.center.disenhancement then + count = count+1 + end + end + + return count > #G.playing_cards * 0.25 + end, +} + +end + +--Lottery +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_lottery', + + config = {extra = {odds_win = 4, prize = 20}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_win, card.ability.extra.prize }} + end, + + can_use = function(self, card) + return true + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + event({trigger = 'after', func = function() + if pseudorandom('aux_lottery'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_win then + ease_dollars(20, true) + else + --Port from Wheel of Fortune + event({trigger = 'after', delay = 0.4, func = function() + attention_text({ + text = localize('k_nope_ex'), + scale = 1.3, + hold = 1.4, + major = card, + backdrop_colour = G.C.UNSTB_AUX, + align = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and 'tm' or 'cm', + offset = {x = 0, y = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and -0.2 or 0}, + silent = true + }) + 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) + card:juice_up(0.3, 0.5) + return true end }) + end + return true + end + }) + end, + + pos = get_coordinates(18), +} + +--Blank +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_blank', + + config = {extra = {}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {}} + end, + + can_use = function(self, card) + return true + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + event({trigger = 'after', func = function() + G.GAME.aux_blank_count = (G.GAME.aux_blank_count or 0) + 1 + return true + end + }) + end, + + pos = get_coordinates(0), + + in_pool = function(self, args) + if not G.GAME then return false end + + return G.booster_pack and (G.GAME.aux_blank_count or 0) < (G.GAME.aux_blank_max_count or 2) + end, +} + +--Dark Matter +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_dark_matter', + + config = {extra = {slot = 1}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + local key = self.key + if (card.edition or {}).key == 'e_negative' then + key = self.key..'_n' + end + + return {key = key, vars = {card.ability.extra.slot}} + end, + + can_use = function(self, card) + return G.jokers.config.card_limit > 0 + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + G.GAME.aux_blank_count = 0 + + --Increase the count for the next Dark Matter + G.GAME.aux_blank_max_count = (G.GAME.aux_blank_max_count or 2)+1 + + event({trigger = 'after', func = function() + if (card.edition or {}).key == 'e_negative' then + G.jokers.config.card_limit = G.jokers.config.card_limit - card.ability.extra.slot + else + G.jokers.config.card_limit = G.jokers.config.card_limit + card.ability.extra.slot + end + return true + end + }) + end, + + pos = get_coordinates(20), + + --Can spawn only when redeemed Blank Auxiliary Card enough time + in_pool = function(self, args) + if not G.GAME then return false end + + return G.booster_pack and (G.GAME.aux_blank_count or 0) >= (G.GAME.aux_blank_max_count or 2) + end, +} + +--Random +SMODS.Consumable{ + set = 'Auxiliary', atlas = 'auxiliary', + key = 'aux_random', + + config = {extra = {count = 2}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + return true + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + for i = 1, math.min(card.ability.extra.count, G.consumeables.config.card_limit - #G.consumeables.cards) do + event({trigger = 'after', delay = 0.4, func = function() + if G.consumeables.config.card_limit > #G.consumeables.cards then + play_sound('timpani') + local c = create_card('Auxiliary', G.consumeables, nil, nil, nil, nil, nil, 'aux_random') + c:add_to_deck() + G.consumeables:emplace(c) + card:juice_up(0.3, 0.5) + end + return true end }) + end + delay(0.6) + end, + + pos = get_coordinates(19), +} + +end + +--Other Basegame Consumable Supports for new features + +--Tarots + +--main function for all three tarots +local function conversionTarot(hand, newcenter) + --Animation ported from basegame Tarot + + for i=1, #hand do + local percent = 1.15 - (i-0.999)/(#hand-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() hand[i]:flip();play_sound('card1', percent);hand[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.2) + + --Handle the conversion + for i=1, #hand do + event({trigger = 'after',delay = 0.1,func = function() + hand[i]:set_ability(G.P_CENTERS[newcenter]) + return true end }) + end + + for i=1, #hand do + local percent = 0.85 + (i-0.999)/(#hand-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() hand[i]:flip();play_sound('tarot2', percent, 0.6);hand[i]:juice_up(0.3, 0.3);return true end }) + end + event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end }) + delay(0.5) +end + +if unstb_config.enh.enh_custom then + +--The Time +SMODS.Consumable{ + set = 'Tarot', atlas = 'tarot', + key = 'trt_time', + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_tarot_exclaim"), get_type_colour(self or card.config, card), nil, 1.2) + end, + + config = {extra = {count = 2}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_vintage'] + + return {vars = {card and card.ability.extra.count or self.config.extra.count}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) >= 1) and (get_consumable_use_hand_count(card, G.hand.highlighted) <= card.ability.extra.count) then + return true + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Special Interaction: If it is a Vintage Card already, reset the breakdown chance + if G.hand.highlighted[1].config.center.key == 'm_unstb_vintage' then + G.hand.highlighted[1].ability.extra.current_odd = 0 + end + + conversionTarot(G.hand.highlighted, 'm_unstb_vintage') + end, + + pos = get_coordinates(0), +} + +--The Acorn +SMODS.Consumable{ + set = 'Tarot', atlas = 'tarot', + key = 'trt_acorn', + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_tarot_exclaim"), get_type_colour(self or card.config, card), nil, 1.2) + end, + + config = {extra = {count = 1}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = {set = 'Other', key = 'acorn_tooltip'} + + return {vars = {card and card.ability.extra.count or self.config.extra.count}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) >= 1) and (get_consumable_use_hand_count(card, G.hand.highlighted) <= card.ability.extra.count) then + return true + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + conversionTarot(G.hand.highlighted, 'm_unstb_acorn') + end, + + pos = get_coordinates(1), +} + +--The Greed +SMODS.Consumable{ + set = 'Tarot', atlas = 'tarot', + key = 'trt_greed', + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_tarot_exclaim"), get_type_colour(self or card.config, card), nil, 1.2) + end, + + config = {extra = {count = 2}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_promo'] + + return {vars = {card and card.ability.extra.count or self.config.extra.count}} + end, + + can_use = function(self, card) + if G.hand and (get_consumable_use_hand_count(card, G.hand.highlighted) >= 1) and (get_consumable_use_hand_count(card, G.hand.highlighted) <= card.ability.extra.count) then + return true + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + conversionTarot(G.hand.highlighted, 'm_unstb_promo') + end, + + pos = get_coordinates(2), +} + +end + +--New Rank-based Tarot + +local tarot_half_rankList = {['unstb_0'] = 'unstb_0', + ['unstb_1'] = 'unstb_0', + ['2'] = 'unstb_1', + ['3'] = 'unstb_1', + ['4'] = '2', + ['5'] = '2', + ['6'] = '3', + ['7'] = '3', + ['8'] = '4', + ['9'] = '4', + ['10'] = '5', + ['Ace'] = '5', + ['unstb_11'] = '5', + ['unstb_12'] = '6', + ['unstb_13'] = '6', + ['unstb_21'] = '10', + ['unstb_25'] = 'unstb_12',} + +if check_enable_taglist({'rank_binary', 'rank_decimal'}) then + +SMODS.Consumable{ + set = 'Tarot', atlas = 'tarot', + key = 'trt_half', + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_tarot_exclaim"), get_type_colour(self or card.config, card), nil, 1.2) + end, + + config = {extra = {count = 2}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + return {vars = {}} + end, + + can_use = function(self, card) + local selected_hands = get_selected_cards(card, G.hand.highlighted) + if G.hand and (#selected_hands == 1) and not selected_hands[1].config.center.replace_base_card and tarot_half_rankList[selected_hands[1].base.value] then + return true + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local target_card = G.hand.highlighted[1] + + --Create two copies of the card, with half rank + --Create one additional rank 0.5 card if applicable, copy the enhancement only, no other bonus property + + local new_rank = tarot_half_rankList[target_card.base.value] + + local extra_card = 0 + + if target_card.base.nominal%2 ==1 then + extra_card = 1 + + --Enable Rank Flag + setPoolRankFlagEnable('unstb_0.5', true); + end + + event({ + trigger = 'after', + delay = 0.7, + func = function() + local _first_dissolve = nil + local new_cards = {} + for i = 1, card.ability.extra.count + extra_card do + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local _card = copy_card(target_card, nil, nil, G.playing_card) + + if extra_card>0 and i==card.ability.extra.count + extra_card then + --Remove special ability + --_card.set_ability(G.P_CENTERS.c_base) + _card:set_edition(nil, true, true) + _card:set_seal(nil, true, true) + _card.ability.perma_bonus = 0 + + SMODS.change_base(_card, nil, 'unstb_0.5') + else + SMODS.change_base(_card, nil, new_rank) + end + + _card:add_to_deck() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + table.insert(G.playing_cards, _card) + G.hand:emplace(_card) + _card:start_materialize(nil, _first_dissolve) + _first_dissolve = true + new_cards[#new_cards+1] = _card + end + playing_card_joker_effects(new_cards) + return true end }) + + + --Destroy the original card + local destroyed_cards = {target_card} + event({ + trigger = 'after', + delay = 0.1, + func = function() + for i=#destroyed_cards, 1, -1 do + local c = destroyed_cards[i] + if c.ability.name == 'Glass Card' then + c:shatter() + else + c:start_dissolve(nil, i == #destroyed_cards) + end + end + return true end }) + + --Calling Jokers to process the card destroying + --Edit: Disable this part for now, technically it should not be counted as destroying card because the new card is considered a split + + --[[delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({remove_playing_cards = true, removed = destroyed_cards}) + end]] + + end, + + pos = get_coordinates(3), +} + +end + +if unstb_config.rank.rank_decimal then + +SMODS.Consumable{ + set = 'Tarot', atlas = 'tarot', + key = 'trt_knowledge', + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_tarot_exclaim"), get_type_colour(self or card.config, card), nil, 1.2) + end, + + config = {extra = {count = 1}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + return {vars = {}} + end, + + can_use = function(self, card) + local selected_hands = get_selected_cards(card, G.hand.highlighted) + if G.hand and (#selected_hands == 1) and not selected_hands[1].config.center.replace_base_card then + return true + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local targetCard = G.hand.highlighted[1] + + event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i=1, card.ability.extra.count do + cards[i] = true + local created_rank = pseudorandom_element({'unstb_0.5', 'unstb_r2', 'unstb_e', 'unstb_Pi'}, pseudoseed('trt_knowledge')) + setPoolRankFlagEnable(created_rank, true) + + local _rank = SMODS.Ranks[created_rank] + local _suit = SMODS.Suits[targetCard.base.suit] + + create_playing_card({front = G.P_CARDS[(_suit.card_key)..'_'..(_rank.card_key)], center = G.P_CENTERS.c_base}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral}) + end + playing_card_joker_effects(cards) + return true end }) + event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end }) + + end, + + pos = get_coordinates(4), +} +end + +--"Rebundant" Spectral (Same ability set as some Auxiliary exclusives, but worse) + +if unstb_config.gameplay.c_rebundant then + +if unstb_config.enh.enh_disenh then + +--Elixir of Life +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_elixir', + + config = {extra = {}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'disenhancement'} + + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + --Check the entire deck + for k, v in ipairs(G.playing_cards) do + if v.config.center.disenhancement then + return true + end + end + + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Populate List of cards to heal + local heal_cards = {} + + for k, v in ipairs(G.playing_cards) do + if v.config.center.disenhancement then + heal_cards[#heal_cards+1] = v + end + end + + if #heal_cards>0 then + play_sound('unstb_heal', 1, 0.3) + end + + for i = 1, #heal_cards do + if heal_cards[i].area == G.hand then --If card is in hand, play animation + event({trigger = 'after', delay = 0.1, func = function() + heal_cards[i]:heal() + play_sound('tarot2', 1, 0.4) + heal_cards[i]:juice_up() + return true + end + }) + else --Otherwise, just heal + heal_cards[i]:heal() + end + + end + + --Reduce Money if it's greater than 0, otherwise do nothing + if G.GAME.dollars > 0 then + ease_dollars(-math.ceil(G.GAME.dollars*0.5), true) + end + end, + + weight = 10, --Large chance to appear when it is needed + + pos = get_coordinates(0), + + --Can spawn only when more than 1/3 of deck is Disenhanced + in_pool = function(self, args) + + --For some reason, not checking this crash Cryptid on start up + if not G.playing_cards then return false end + + local count = 0 + + for k, v in ipairs(G.playing_cards) do + if v.config.center.disenhancement then + count = count+1 + end + end + + return count > #G.playing_cards * 0.3 + end, +} + +end + +if unstb_config.gameplay.seal_suit then + +--Vessel +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_vessel', + + config = {extra = {count = 4}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'suit_seal'} + return {vars = {card.ability.extra.count}} + end, + + can_use = function(self, card) + local selected_hands = get_selected_cards(card, G.hand.highlighted) + + if G.hand and (#selected_hands > 1 and #selected_hands <= card.ability.extra.count) then + + local targetCard = selected_hands[1] + --Sort to get the actual target card + for i=1, #selected_hands do if selected_hands[i].T.x < targetCard.T.x then targetCard = selected_hands[i] end end + + return not targetCard.config.center.no_suit and (unstb_global.SUIT_SEAL[targetCard.base.suit] or {}).seal_key + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Figure out what is left card, what is the left card + local targetCard = G.hand.highlighted[1] + --Sort to get the actual target card + for i=1, #G.hand.highlighted do if G.hand.highlighted[i].T.x < targetCard.T.x then targetCard = G.hand.highlighted[i] end end + + --Adds Suit Seal to the right card (or none, if there's no matching suit) + local suit_seal = (unstb_global.SUIT_SEAL[targetCard.base.suit] or {}).seal_key + + for i=1, #G.hand.highlighted do + if G.hand.highlighted[i] ~= targetCard then + G.hand.highlighted[i]:set_seal(suit_seal, nil, true) + end + end + + --Destroy the leftmost card + local destroyed_cards = {targetCard} + + event({ + trigger = 'after', + delay = 0.1, + func = function() + for i=#destroyed_cards, 1, -1 do + local c = destroyed_cards[i] + if c.ability.name == 'Glass Card' then + c:shatter() + else + c:start_dissolve(nil, i == #destroyed_cards) + end + end + return true end }) + --Calling Jokers to process the card destroying + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({remove_playing_cards = true, removed = destroyed_cards}) + end + end, + + pos = get_coordinates(1), +} + +--Conferment +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_conferment', + + config = {extra = {count = 4, cost = 8}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'unstb_face_seal'} + return {vars = {card and card.ability.extra.count or self.config.extra.count, card and card.ability.extra.cost or self.config.extra.cost}} + end, + + can_use = function(self, card) + if G.hand and #G.hand.cards > 1 then + + --Check if the hand contains at least one non-face card + for i = 1, #G.hand.cards do + if not G.hand.cards[i]:is_face() then + return true + end + end + + return false + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + + for i = 1, card.ability.extra.count do + + event({ + trigger = 'after', + delay = 0.2, + func = function() + local hand_card = {} + + --Pool All eligible cards in hand, this has to be done each repetition + --Maybe there could be a prettier way for this? + for i = 1, #G.hand.cards do + if not G.hand.cards[i]:is_face() then + hand_card[#hand_card+1] = G.hand.cards[i] + end + end + + local target_card = pseudorandom_element(hand_card, pseudoseed('conferment')) + + if target_card then + target_card:set_seal('unstb_face', nil, true) + end + return true end + }) + + end + + --Always decrease regardless of the current money + ease_dollars(-card.ability.extra.cost, true) + + end, + + pos = get_coordinates(2), +} + +end + +if unstb_config.rank.rank_binary then + +--Amnesia +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_amnesia', + + config = {extra = {count = 3}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + return {vars = {card and card.ability.extra.count or self.config.extra.count}} + end, + + can_use = function(self, card) + if G.hand and #G.hand.cards > 1 then + + --Check if the hand contains at least one non-face card + for i = 1, #G.hand.cards do + if not G.hand.cards[i]:is_face() then + return true + end + end + + return false + end + return false + end, + + use = function(self, card) + --Enable Rank Flag + setPoolRankFlagEnable('unstb_0', true); + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Based on Basegame's Immolate + local converted_card = {} + + local temp_hand = {} + for k, v in ipairs(G.hand.cards) do temp_hand[#temp_hand+1] = v end + table.sort(temp_hand, function (a, b) return not a.playing_card or not b.playing_card or a.playing_card < b.playing_card end) + pseudoshuffle(temp_hand, pseudoseed('amnesia')) + + for i = 1, card.ability.extra.count do converted_card[#converted_card+1] = temp_hand[i] end + + if #converted_card > 0 then + --Animation from Basegame Tarot + for i=1, #converted_card do + local percent = 1.15 - (i-0.999)/(#converted_card-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() converted_card[i]:flip();play_sound('card1', percent);converted_card[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.2) + + --Handle the conversion + for i=1, #converted_card do + event({trigger = 'after',delay = 0.1,func = function() + SMODS.change_base(converted_card[i], nil, 'unstb_0') + return true end }) + end + + for i=1, #converted_card do + local percent = 0.85 + (i-0.999)/(#converted_card-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() converted_card[i]:flip();play_sound('tarot2', percent, 0.6);converted_card[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.5) + end + end, + + pos = get_coordinates(3), +} + +end + +if unstb_config.rank.rank_21 then + +--Altar +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_altar', + + config = {extra = {destroy_count = 4, create_count = 3}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + return {vars = {card and card.ability.extra.destroy_count or self.config.extra.destroy_count, card and card.ability.extra.create_count or self.config.extra.create_count}} + end, + + can_use = function(self, card) + if G.hand and #G.hand.cards > 1 then + return true + end + return false + end, + + use = function(self, card) + --Enable Rank Flag + setPoolRankFlagEnable('unstb_21', true); + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Based on Basegame's Immolate + local destroyed_cards = {} + + local temp_hand = {} + for k, v in ipairs(G.hand.cards) do temp_hand[#temp_hand+1] = v end + table.sort(temp_hand, function (a, b) return not a.playing_card or not b.playing_card or a.playing_card < b.playing_card end) + pseudoshuffle(temp_hand, pseudoseed('altar')) + + for i = 1, card.ability.extra.destroy_count do destroyed_cards[#destroyed_cards+1] = temp_hand[i] end + + --Destroy Cards + event({ + trigger = 'after', + delay = 0.1, + func = function() + for i=#destroyed_cards, 1, -1 do + local c = destroyed_cards[i] + --print(c) + + if c.ability.name == 'Glass Card' then + c:shatter() + else + c:start_dissolve(nil, i == #destroyed_cards) + end + end + return true end }) + --Calling Jokers to process the card destroying + delay(0.3) + --print('joker') + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({remove_playing_cards = true, removed = destroyed_cards}) + end + + --Adding New Cards + event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i=1, card.ability.extra.create_count do + cards[i] = true + local _rank = SMODS.Ranks['unstb_21'] + local _suit = pseudorandom_element(SMODS.Suits, pseudoseed('altar')) or SMODS.Suits['Spades'] + + --Pooling Enhancements + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if not v.replace_base_card and not v.disenhancement then + cen_pool[#cen_pool+1] = v + end + end + + create_playing_card({front = G.P_CARDS[(_suit.card_key)..'_'..(_rank.card_key)], center = pseudorandom_element(cen_pool, pseudoseed('altar'))}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral}) + end + playing_card_joker_effects(cards) + return true end }) + + end, + + pos = get_coordinates(4), +} + +end + +if check_enable_taglist({'edition_upgrade', 'enh_disenh'}) then + +--Devil's Contract +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_contract', + + config = {extra = {upgrade_count = 2, disenc_count = 2}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = {set = 'Other', key = 'upgrade_edition'} + info_queue[#info_queue+1] = {set = 'Other', key = 'disenhancement'} + + return {vars = {card and card.ability.extra.upgrade_count or self.config.extra.upgrade_count, card and card.ability.extra.disenc_count or self.config.extra.disenc_count}} + end, + + can_use = function(self, card) + if G.hand and #G.hand.cards > 1 then + return true + end + return false + end, + + use = function(self, card) + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + --Based on Basegame's Immolate + local upgrade_card = {} + local disenc_card = {} + + local temp_hand = {} + for k, v in ipairs(G.hand.cards) do temp_hand[#temp_hand+1] = v end + table.sort(temp_hand, function (a, b) return not a.playing_card or not b.playing_card or a.playing_card < b.playing_card end) + pseudoshuffle(temp_hand, pseudoseed('dvcontract')) + + for i = 1, card.ability.extra.upgrade_count do upgrade_card[#upgrade_card+1] = temp_hand[i] end + for i = card.ability.extra.upgrade_count+1, card.ability.extra.upgrade_count+card.ability.extra.disenc_count do disenc_card[#disenc_card+1] = temp_hand[i] end + + --Upgrade the card + for i=1, #upgrade_card do + event({trigger = 'after',delay = 0.1,func = function() + edition_upgrade(upgrade_card[i]) + return true end }) + end + + --Disenhanced the Remaining Cards + if #disenc_card > 0 then + --Animation from Basegame Tarot + for i=1, #disenc_card do + local percent = 1.15 - (i-0.999)/(#disenc_card-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() disenc_card[i]:flip();play_sound('card1', percent);disenc_card[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.2) + + --Handle the conversion + for i=1, #disenc_card do + event({trigger = 'after',delay = 0.1,func = function() + disenc_card[i]:set_ability(G.P_CENTERS[pseudorandom_element({'m_unstb_radioactive', 'm_unstb_biohazard', 'm_unstb_poison'}, pseudoseed('dvcontract'))]) + return true end }) + end + + for i=1, #disenc_card do + local percent = 0.85 + (i-0.999)/(#disenc_card-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() disenc_card[i]:flip();play_sound('tarot2', percent, 0.6);disenc_card[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.5) + end + end, + + pos = get_coordinates(5), +} + +end + +end + +--Other Spectrals + +if unstb_config.gameplay.new_spectrals then + +--Poltergeist +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_poltergeist', + + config = {extra = {}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + return {vars = {}} + end, + + can_use = function(self, card) + if G.hand and #G.jokers.cards > 1 then + + --Check if there is at least one joker with edition + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].edition then + return true + end + end + + return false + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local joker_list = G.jokers.cards + + local temp_edition = {} + for k, v in ipairs(joker_list) do temp_edition[#temp_edition+1] = v.edition or 'none' end + pseudoshuffle(temp_edition, pseudoseed('poltergeist')) + + --Give all joker new editions + --Animation from Basegame Tarot + for i=1, #joker_list do + local percent = 1.15 - (i-0.999)/(#joker_list-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() joker_list[i]:flip();play_sound('card1', percent);joker_list[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.2) + + --Set the new edition + for i=1, #joker_list do + event({trigger = 'after',delay = 0.1,func = function() + + local edition = temp_edition[i] + + if edition == 'none' then + edition = nil + end + + joker_list[i]:set_edition(edition, true, true) + return true end }) + end + + for i=1, #joker_list do + local percent = 0.85 + (i-0.999)/(#joker_list-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() joker_list[i]:flip();play_sound('tarot2', percent, 0.6);joker_list[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.5) + end, + + pos = get_coordinates(6), + + --Can spawn only if there is more than one joker, and at least one with edition + in_pool = function(self, args) + + --For some reason, not checking this crash Cryptid on start up + if not G.jokers then return false end + + if #G.jokers.cards > 1 then + for i=1, #G.jokers.cards do + if G.jokers.cards[i].edition then + return true + end + end + end + + return false + end, +} + +--Projection +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_projection', + + config = {extra = {odds_break = 4}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card and card.ability.extra.odds_break or self.config.extra.odds_break}} + end, + + can_use = function(self, card) + if G.hand and #G.jokers.cards > 1 and #G.jokers.highlighted ==1 and G.jokers.highlighted[1] then + + --Check if there is at least one of the selected jokers have edition and is not the same + local joker = G.jokers.highlighted[1] + local position = nil + + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == joker then position = i; break end + end + + --Selected Joker is at the rightmost + if position==#G.jokers.cards then + return false + end + + local nextjoker = G.jokers.cards[position+1] + + --If both has edition, see if it's the same + if joker.edition and nextjoker.edition then + return joker.edition.key ~= nextjoker.edition.key + end + + --Return true if at least one of them has edition + return joker.edition or nextjoker.edition + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local joker = G.jokers.highlighted[1] + local position = nil + + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == joker then position = i; break end + end + + local nextjoker = G.jokers.cards[position+1] + + local firstEdition = joker.edition + local nextEdition = nextjoker.edition + + local joker_list = {joker, nextjoker} + + --Animation from Basegame Tarot + for i=1, #joker_list do + local percent = 1.15 - (i-0.999)/(#joker_list-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() joker_list[i]:flip();play_sound('card1', percent);joker_list[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.2) + + --Set the new edition + for i=1, #joker_list do + event({trigger = 'after',delay = 0.1,func = function() + joker:set_edition(nextEdition, true, true) + nextjoker:set_edition(firstEdition, true, true) + return true end }) + end + + for i=1, #joker_list do + local percent = 0.85 + (i-0.999)/(#joker_list-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() joker_list[i]:flip();play_sound('tarot2', percent, 0.6);joker_list[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.5) + + --Destroy one of the joker + if pseudorandom('projection'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_break then + local target = pseudorandom_element(joker_list, pseudoseed('projection')) + event({func = function() + target:start_dissolve({HEX("57ecab")}, nil, 1.6) + return true end }) + forced_message("Failed...", target, G.C.BLACK, true) + end + + + end, + + pos = get_coordinates(7), + + --Can spawn only if there is at least more than 1 Joker with distinct edition + in_pool = function(self, args) + + --For some reason, not checking this crash Cryptid on start up + if not G.jokers then return false end + + local prev_edition = nil + + if #G.jokers.cards > 1 then + --Set the first one as a pivot check + prev_edition = G.jokers.cards[1].edition + + --Iterate through the rest + for i=2, #G.jokers.cards do + --If there is one Joker with different edition, breaks and return true + if prev_edition ~= G.jokers.cards[i].edition then + return true + end + end + end + + return false + end, +} + +unstb_global.siphon_blacklist = {} +unstb_global.siphon_blacklist['e_negative'] = true + +--Global register function, just in case more modded edition can blackList them +function unstb_global.register_siphon_blacklist(edition_list) + for i=1, #edition_list do + unstb_global.siphon_blacklist[edition_list[i]] = true + end +end + +--Siphon +SMODS.Consumable{ + set = 'Spectral', atlas = 'spectral', + key = 'spc_siphon', + + config = {extra = {count = 4}}, + --discovered = true, + + loc_vars = function(self, info_queue, card) + + return {vars = {card and card.ability.extra.count or self.config.extra.count}} + end, + + can_use = function(self, card) + if G.hand and #G.jokers.cards >= 1 and #G.jokers.highlighted ==1 and G.jokers.highlighted[1] then + return G.jokers.highlighted[1].edition and not unstb_global.siphon_blacklist[G.jokers.highlighted[1].edition.key] + end + return false + end, + + use = function(self, card) + + event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + card:juice_up(0.3, 0.5) + return true end }) + + local edition_card = {} + local temp_hand = {} + + for k, v in ipairs(G.hand.cards) do temp_hand[#temp_hand+1] = v end + table.sort(temp_hand, function (a, b) return not a.playing_card or not b.playing_card or a.playing_card < b.playing_card end) + pseudoshuffle(temp_hand, pseudoseed('siphon')) + + for i = 1, card.ability.extra.count do edition_card[#edition_card+1] = temp_hand[i] end + + local joker = G.jokers.highlighted[1] + local edition = joker.edition + + --Destroy the joker + + event({func = function() + joker:start_dissolve({HEX("57ecab")}, nil, 1.6) + return true end }) + delay(0.5) + + --Upgrade the card + + event({trigger = 'after',delay = 0.1,func = function() + for i=1, #edition_card do + edition_card[i]:set_edition(edition, true, i==1) + end + return true end }) + + + end, + + pos = get_coordinates(8), + + --Can spawn only if there is one joker or more with edition + in_pool = function(self, args) + + --For some reason, not checking this crash Cryptid on start up + if not G.jokers then return false end + + if #G.jokers.cards >= 1 then + for i=1, #G.jokers.cards do + if G.jokers.cards[i].edition then + return true + end + end + end + + return false + end, +} + +end + +--Vouchers + +if unstb_config.gameplay.c_aux then + +SMODS.Voucher({ + object_type = "Voucher", + key = "aux1", + + atlas = "voucher", + pos = { x = 0, y = 0 }, + unlocked = true, + --discovered = true, + redeem = function(self) + event({ + func = function() + G.GAME.auxiliary_rate = (G.GAME.auxiliary_rate or 0) + 2 + return true + end, + }) + end, + unredeem = function(self) + event({ + func = function() + G.GAME.auxiliary_rate = (G.GAME.auxiliary_rate or 2) - 2 + return true + end, + }) + end +}) + +SMODS.Voucher({ + object_type = "Voucher", + key = "aux2", + + atlas = "voucher", + pos = { x = 1, y = 0 }, + unlocked = true, + --discovered = true, + requires = { "v_unstb_aux1" }, +}) + +end + +-------- Joker Code Starts Here ------ + +--Basic Jokers + +--Lunar Calendar +create_joker({ + name = 'Lunar Calendar', id = 40, + rarity = 'Common', cost = 5, + + blueprint = true, eternal = true, perishable = true, + + vars = { {odds_spawn = 4} }, + + custom_vars = function(self, info_queue, card) + local suit = card and card.ability.extra and card.ability.extra.suit or 'Spades' + + local suit_name = localize(suit, 'suits_singular') + local suit_color = G.C.SUITS[suit] + + return {vars = {suit_name, G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_spawn, colours = {suit_color}}} + end, + + set_ability = function(self, card, initial, delay_sprites) + --Initialize variables + card.ability.extra.suit = pseudorandom_element(SMODS.Suits, pseudoseed('lunarcalendar')).key --All suits, not just strictly in the deck + end, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + --Main Context + if context.other_card:is_suit(card.ability.extra.suit) then + local isActivated = pseudorandom('lunarcalendar'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_spawn + + if isActivated then + if #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + event({func = function() + local created_card = create_card('Planet', G.consumeables, nil, nil, nil, nil, nil, nil) + created_card:add_to_deck() + G.consumeables:emplace(created_card) + G.GAME.consumeable_buffer = 0 + return true end + }) + + forced_message("Planet", context.blueprint_card or card, G.C.SECONDARY_SET.Planet, true) + end + end + + end + end + + --End-of-round Randomize + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + card.ability.extra.suit = pseudorandom_element(SMODS.Suits, pseudoseed('lunarcalendar')).key + return{ + message = localize(card.ability.extra.suit, 'suits_singular'), + } + end + end, +}) + +--Dragon Hoard +create_joker({ + name = 'Dragon Hoard', id = 42, + rarity = 'Common', cost = 4, + + blueprint = true, eternal = true, perishable = true, + + vars = {{mult_rate = 15}, {held_amount = 2}, {mult_current = 0}}, + + custom_vars = function(self, info_queue, card) + + local held_amount = G.consumeables and #G.consumeables.cards or 0 + + local mult_current = card.ability.extra.mult_rate * math.floor(held_amount/card.ability.extra.held_amount) + + return { vars = {card.ability.extra.mult_rate, card.ability.extra.held_amount, mult_current}} + end, + + + calculate = function(self, card, context) + --Main context + if context.joker_main then + + card.ability.extra.mult_current = card.ability.extra.mult_rate * math.floor(#G.consumeables.cards/card.ability.extra.held_amount) + + if card.ability.extra.mult_current > 0 then + return { + mult_mod = card.ability.extra.mult_current, + message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult_current } } + } + end + end + + end, +}) + +--Card Dealer +create_joker({ + name = 'Card Dealer', id = 41, + rarity = 'Common', cost = 5, + + blueprint = true, eternal = true, perishable = true, + + vars = { {chip_rate = 15}, {chips = 0} }, + + custom_vars = function(self, info_queue, card) + + return { vars = {card.ability.extra.chip_rate, card.ability.extra.chips}} + end, + + calculate = function(self, card, context) + + if context.joker_main then + return { + chip_mod = card.ability.extra.chips, + message = localize { type = 'variable', key = 'a_chips', vars = { card.ability.extra.chips } } + } + end + + if context.before and context.full_hand and not context.blueprint then + card.ability.extra.chips = card.ability.extra.chips + #context.full_hand * card.ability.extra.chip_rate + + return { + message = 'Upgraded!', + colour = G.C.CHIPS, + card = card + } + end + + --End-of-round Resets + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + card.ability.extra.chips = 0 + return{ + message = localize('k_reset'), + colour = G.C.RED + } + end + + end, +}) + +--Match Three +create_joker({ + name = 'Match Three', id = 67, + rarity = 'Common', cost = 6, + + blueprint = true, eternal = true, perishable = true, + + vars = { {mult = 15}, {count = 3} }, + + custom_vars = function(self, info_queue, card) + + return { vars = {card.ability.extra.mult, card.ability.extra.count}} + end, + + calculate = function(self, card, context) + + if context.joker_main then + + --Not enough card + if #context.scoring_hand < card.ability.extra.count then + return + end + + local card_count = 1 + local max_card_count = 0 + local current_suit = context.scoring_hand[1].base.suit + + for i=2, #context.scoring_hand do + if context.scoring_hand[i]:is_suit(current_suit) then + card_count = card_count + 1 + else + --reset count + + if card_count > max_card_count then + max_card_count = card_count + end + + card_count = 1 + current_suit = context.scoring_hand[i].base.suit + end + end + + if card_count > max_card_count then + max_card_count = card_count + end + + --print(max_card_count) + --print(card.ability.extra.count) + + if max_card_count >= card.ability.extra.count then + return { + mult_mod = card.ability.extra.mult, + message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult } } + } + end + end + + end, +}) + +create_joker({ + name = 'Furry Joker', id = 44, + rarity = 'Uncommon', cost = 7, + + blueprint = true, eternal = true, perishable = true, + + vars = { {odds_poly = 8} }, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = G.P_CENTERS['m_wild'] + info_queue[#info_queue+1] = G.P_CENTERS['e_polychrome'] + + return { vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_poly}} + end, + + calculate = function(self, card, context) + if context.after and context.scoring_hand then + local polyAmount = 0 + for i=1, #context.scoring_hand do + local current_card = context.scoring_hand[i] + if current_card.config.center == G.P_CENTERS.m_wild and (current_card.edition or {}).key ~= 'e_polychrome' and not current_card.becoming_poly then + local isActivated = pseudorandom('furry'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_poly + if isActivated then + current_card.becoming_poly = true + event({trigger = 'after', delay = 0.4, func = function() + big_juice(context.blueprint_card or card) + current_card:set_edition("e_polychrome", true, false) + current_card.becoming_poly = nil + return true end + }) + polyAmount = polyAmount+1 + end + end + end + if polyAmount>0 then + delay(2.5) + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one wild card in the deck + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_wild then return true end + end + return false + + end +}) + +--Hook set_cost to allow messing with prices +local ref_set_cost = Card.set_cost +function Card.set_cost(self) + ref_set_cost(self) + for k,v in ipairs(SMODS.find_card("j_unstb_luxurious_handbag")) do + self.cost = self.cost + v.ability.extra.inflation + end + +end + +create_joker({ + name = 'Luxurious Handbag', id = 43, + rarity = 'Uncommon', cost = 6, + + blueprint = false, eternal = true, perishable = true, + + vars = { {add_slot = 4}, {inflation = 2} }, + + custom_vars = function(self, info_queue, card) + return { vars = {card.ability.extra.add_slot, card.ability.extra.inflation}} + end, + + add_to_deck = function(self, card, from_debuff) + + G.consumeables:change_size(card.ability.extra.add_slot) + + --Recalculate all prices in the shop + event({func = function() + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end + }) + + end, + + remove_from_deck = function(self, card, from_debuff) + G.consumeables:change_size(-card.ability.extra.add_slot) + + --Recalculate all prices in the shop + event({func = function() + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end + }) + end, + + --Actual functionality is in Card.set_cost hook mostly + --[[ + calculate = function(self, card, context) + + end,]] +}) + +--Portal +create_joker({ + name = 'Portal', id = 39, + rarity = 'Uncommon', cost = 7, + + blueprint = true, eternal = true, perishable = true, + + --vars = { {times = 1}}, + + --Actual ability is in ustb_get_straight + --[[calculate = function(self, card, context) + + end,]] +}) + +--Suit Seal Support Jokers + +--Vainglorious Joker +create_joker({ + name = 'Vainglorious Joker', id = 32, + rarity = 'Common', cost = 5, + + gameplay_tag = {'seal_suit'}, + + blueprint = true, eternal = true, perishable = true, + + vars = { {mult = 5} }, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'suit_seal'} + return {vars = {card.ability.extra.mult}} + end, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.seal and SMODS.Seals[context.other_card.seal] and SMODS.Seals[context.other_card.seal].suit_seal then + --big_juice(context.blueprint_card or card) + return { + mult = card.ability.extra.mult, + card = card + } + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one card with suit seal + for _, v in pairs(G.playing_cards) do + if v.seal and SMODS.Seals[v.seal] and SMODS.Seals[v.seal].suit_seal then return true end + end + return false + + end +}) + +--Acedia Joker +create_joker({ + name = 'Acedia Joker', id = 31, + rarity = 'Common', cost = 5, + + gameplay_tag = {'seal_suit'}, + + blueprint = true, eternal = true, perishable = true, + + vars = { {mult = 10} }, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'suit_seal'} + return {vars = {card.ability.extra.mult}} + end, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.seal and SMODS.Seals[context.other_card.seal] and SMODS.Seals[context.other_card.seal].suit_seal then + local is_activate = false + + --If same suit, returns true immediately. This should also handle Wild Card cases and other Joker-related calculation probably? + --I hope so, if there's a case that slips by I'll be crying + if context.other_card:is_suit(SMODS.Seals[context.other_card.seal].suit_seal, nil, nil, true) then + is_activate = true + + --Otherwise, checks suit group + elseif get_suit_group(context.other_card.base.suit) == get_suit_group(SMODS.Seals[context.other_card.seal].suit_seal) then + is_activate = true + end + + if is_activate then + return { + mult = card.ability.extra.mult, + card = card + } + end + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one card with suit seal + for _, v in pairs(G.playing_cards) do + if v.seal and SMODS.Seals[v.seal] and SMODS.Seals[v.seal].suit_seal then return true end + end + return false + + end +}) + +--Cinnabar +create_joker({ + name = 'Cinnabar', id = 33, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'seal_suit', 'c_aux'}, + + blueprint = true, eternal = true, perishable = true, + + vars = { {odds = 6} }, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'suit_seal'} + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds}} + end, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.seal and SMODS.Seals[context.other_card.seal] and SMODS.Seals[context.other_card.seal].suit_seal then + local isActivated = pseudorandom('cinnabar'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds + + if isActivated then + local created_card = unstb_global.SUIT_SEAL[SMODS.Seals[context.other_card.seal].suit_seal].aux_key + if #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + event({func = function() + local created_card = create_card('Auxiliary', G.consumeables, nil, nil, nil, nil, created_card, nil) + created_card:add_to_deck() + G.consumeables:emplace(created_card) + G.GAME.consumeable_buffer = 0 + return true end + }) + + forced_message("Auxiliary", context.blueprint_card or card, G.C.UNSTB_AUX, true) + end + end + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one card with suit seal + for _, v in pairs(G.playing_cards) do + if v.seal and SMODS.Seals[v.seal] and SMODS.Seals[v.seal].suit_seal then return true end + end + return false + + end +}) + +--Auxiliary Support + +--Free Trial +create_joker({ + name = 'Free Trial', id = 54, + rarity = 'Uncommon', cost = 4, + + gameplay_tag = {'c_aux'}, + + vars = {{odds = 4}}, + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {key = 'e_negative_consumable', set = 'Edition', config = {extra = 1}} + + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds}} + end, + + blueprint = true, eternal = true, perishable = true, + + calculate = function(self, card, context) + + if context.using_consumeable then + if context.consumeable.ability.set == "Auxiliary" then + if pseudorandom('freetrial'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds then + event({trigger = 'after', delay = 0.4, func = function() + if G.consumeables.config.card_limit >= #G.consumeables.cards then + play_sound('timpani') + local c = create_card('Auxiliary', G.consumeables, nil, nil, nil, nil, nil, 'freetrial') + c:set_edition({negative = true}, true) + c:add_to_deck() + G.consumeables:emplace(c) + + big_juice(context.blueprint_card or card) + card_eval_status_text(context.blueprint_card or card,'extra',nil, nil, nil,{message = "Free Trial", colour = G.C.UNSTB_AUX, instant = true}) + + --forced_message("Free Trial!", context.blueprint_card or card, G.C.UNSTB_AUX, ) + end + return true end }) + + end + end + end + end +}) + +--Extended Warranty +create_joker({ + name = 'Extended Warranty', id = 55, + rarity = 'Rare', cost = 8, + + gameplay_tag = {'c_aux'}, + + vars = {{xmult = 1.5}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.xmult}} + end, + + blueprint = true, eternal = true, perishable = true, + + calculate = function(self, card, context) + + if context.joker_main then + for i=1, #G.consumeables.cards do + if G.consumeables.cards[i].ability.set == "Auxiliary" then + + event({trigger = 'after', func = function() + (context.blueprint_card or card):juice_up(0.5, 0.5) + return true end + }) + + SMODS.calculate_effect({xmult = card.ability.extra.xmult}, G.consumeables.cards[i]) + end + end + + end + + end +}) + +--Technician +create_joker({ + name = 'Technician', id = 56, + rarity = 'Common', cost = 6, + + gameplay_tag = {'c_aux'}, + + vars = {{chip_rate = 15}, {chips = 0}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.chip_rate, card.ability.extra.chips}} + end, + + blueprint = true, eternal = true, perishable = true, + + set_ability = function(self, card, initial, delay_sprites) + if G.GAME and G.GAME.consumeable_usage_total then + card.ability.extra.chips = (G.GAME.consumeable_usage_total.auxiliary or 0) * card.ability.extra.chip_rate + else + card.ability.extra.chips = 0 + end + end, + + calculate = function(self, card, context) + + --Upgrades + if context.using_consumeable and not context.blueprint then + if context.consumeable.ability.set == "Auxiliary" then + card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_rate + event({ + func = function() + big_juice(card) + forced_message("Upgraded!", card, G.C.CHIPS, true) + --card_eval_status_text(card,'extra',nil, nil, nil,{message = "Upgraded", colour = G.C.MULT, instant = true}) + return true end}) + end + end + + --Main context + if context.joker_main then + if card.ability.extra.chips > 0 then + return { + chip_mod = card.ability.extra.chips, + message = localize { type = 'variable', key = 'a_chips', vars = { card.ability.extra.chips } } + } + end + end + + end +}) + +--Season Pass +create_joker({ + name = 'Season Pass', id = 57, + rarity = 'Uncommon', cost = 5, + + gameplay_tag = {'c_aux'}, + + vars = {{odds = 4}}, + + custom_vars = function(self, info_queue, card) + + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds}} + end, + + blueprint = true, eternal = true, + + calculate = function(self, card, context) + if context.discard then + if not context.other_card.debuff and context.other_card:is_face() and pseudorandom('season_pass'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds then + --Create consumable + if #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + event({func = function() + local created_card = create_card('Auxiliary', G.consumeables, nil, nil, nil, nil, nil, 'season_pass') + created_card:add_to_deck() + G.consumeables:emplace(created_card) + G.GAME.consumeable_buffer = 0 + return true end + }) + + forced_message("Auxiliary", context.blueprint_card or card, G.C.UNSTB_AUX, 0.5) + end + end + end + end +}) + +--BlackJack + Question Mark Line Jokers + +--Black Jack +create_joker({ + name = 'Black Jack', id = 15, + rarity = 'Common', cost = 5, + + gameplay_tag = {'rank_21', 'j_alter'}, + + blueprint = true, eternal = true, perishable = false, + + vars = {{maxRank = 21}, {chips = 0}}, + + calculate = function(self, card, context) + --This part handles the chip reward + if context.joker_main then + return { + chip_mod = card.ability.extra.chips, + message = localize { type = 'variable', key = 'a_chips', vars = { card.ability.extra.chips } } + } + end + + --This part handles the scaling + if context.before and context.scoring_hand and not context.blueprint then + + local totalRank = blackJack_evalrank(context.scoring_hand, card.ability.extra.maxRank) + + if totalRank < card.ability.extra.maxRank then + card.ability.extra.chips = card.ability.extra.chips + totalRank + + return { + message = 'Upgraded!', + colour = G.C.CHIPS, + card = card + } + elseif totalRank == card.ability.extra.maxRank then + card.ability.extra.chips = (card.ability.extra.chips + totalRank) * 2 + + local popup_msg = 'Black Jack!' + if card.ability.extra.maxRank ~= 21 then + popup_msg = 'Black Jack...?' + else + + end + + event({ trigger = 'after', delay = 0.2, func = function() + play_sound('multhit1') + return true end }) + + return { + message = popup_msg, + colour = G.C.RED, + card = card + } + else + card.ability.extra.chips = 0 + + event({ trigger = 'after', delay = 0.2, func = function() + play_sound('tarot1') + return true end }) + + return { + message = 'Busted...', + colour = G.C.BLACK, + card = card + } + end + + + end + end +}) + +--What +create_joker({ + name = 'What', id = 14, + rarity = 'Rare', cost = 6, + + gameplay_tag = {'rank_21'}, + + blueprint = true, eternal = true, perishable = true, + + vars = { {chips = 420}, {mult = 69} }, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.base.value == 'unstb_???' then + return { + chips = card.ability.extra.chips, + mult = card.ability.extra.mult, + card = card + } + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one rank ??? card + for _, v in pairs(G.playing_cards) do + if v.base.value == 'unstb_???' then return true end + end + return false + + end +}) + +--Decimal-line Jokers + +create_joker({ + name = 'Floating Point Error', id = 34, + rarity = 'Uncommon', cost = 4, + + gameplay_tag = {'rank_decimal'}, + + blueprint = true, eternal = true, perishable = true, + + --vars = { {bonus = 10}}, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + local currentCard = context.other_card + if is_decimal(currentCard) then + --big_juice(card) + + currentCard.ability.perma_bonus = (currentCard.ability.perma_bonus or 0) + SMODS.Ranks[currentCard.base.value].nominal + + event({ trigger = 'after', func = function() + big_juice(card) + return true end }) + + return { + extra = {message = "Upgrade!", colour = G.C.CHIPS}, + colour = G.C.CHIPS, + card = currentCard + } + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one decimal card + for _, v in pairs(G.playing_cards) do + if is_decimal(v) then return true end + end + return false + + end +}) + +--Academic Journal +create_joker({ + name = 'Academic Journal', id = 35, + rarity = 'Uncommon', cost = 6, + + gameplay_tag = {'rank_decimal'}, + + blueprint = true, eternal = true, perishable = true, + + vars = { {times_max = 5}}, + + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.times_max, card.ability.extra.times_max - card.ability.extra.times_current}} + end, + + add_to_deck = function(self, card, from_debuff) + --Enable all decimal ranks card in pools + if not from_debuff then + setPoolRankFlagEnable('unstb_0.5', true); + setPoolRankFlagEnable('unstb_r2', true); + setPoolRankFlagEnable('unstb_e', true); + setPoolRankFlagEnable('unstb_Pi', true); + end + end, + + set_ability = function(self, card, initial, delay_sprites) + --Initialize variables + card.ability.extra.times_current = 0 + end, + + calculate = function(self, card, context) + if context.before and not context.blueprint then + local is_active = true + + if card.ability.extra.times_current < card.ability.extra.times_max then + for i=1, #context.scoring_hand do + if context.scoring_hand[i]:is_face() then + is_active = false + break + end + end + + if is_active then + card.ability.extra.times_current = (card.ability.extra.times_current or 0) + 1 + end + else + is_active = false + end + + card.ability.extra.is_active = is_active + end + + if context.after then + --Create Card if it activates + if card.ability.extra.is_active then + --Adds a card + event({trigger = 'after', func = function() + local rank = SMODS.Ranks[pseudorandom_element({'unstb_0.5', 'unstb_r2', 'unstb_e', 'unstb_Pi'}, pseudoseed('researchpaper')..G.SEED)].card_key + local suit = pseudorandom_element(SMODS.Suits, pseudoseed('researchpaper')..G.SEED).card_key + + --Pooling Enhancements + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if not v.replace_base_card and not v.disenhancement then + cen_pool[#cen_pool+1] = v + end + end + + local _card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS[suit..'_'..rank], pseudorandom_element(cen_pool, pseudoseed('researchpaper')..G.SEED), {playing_card = G.playing_card}) + + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.play:emplace(_card) + table.insert(G.playing_cards, _card) + + big_juice(context.blueprint_card or card) + + return true end + }) + delay(1.5) + + event({trigger = 'after', func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + draw_card(G.play,G.deck, 90,'up', nil) + return true end + }) + + playing_card_joker_effects({true}) + end + end + + --End of round check, make sure it's checked absolutely once per round + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + if card.ability.extra.times_current > 0 then + card.ability.extra.times_current = 0 + return { + message = 'Reset!' + } + end + end + end, +}) + +--Engineer +create_joker({ + name = 'Engineer', id = 37, + rarity = 'Common', cost = 5, + + gameplay_tag = {'rank_decimal'}, + + blueprint = false, eternal = true, perishable = true, + + --vars = { {times = 1}}, + + --Engineer's actual ability is in Card:get_id hook above + --[[calculate = function(self, card, context) + + end,]] + + custom_in_pool = function(self, args) + + --Spawns if there is at least one decimal card + for _, v in pairs(G.playing_cards) do + if is_decimal(v) then return true end + end + return false + + end +}) + +--Thesis Proposal +create_joker({ + name = 'Thesis Proposal', id = 36, + rarity = 'Common', cost = 6, + + gameplay_tag = {'rank_decimal'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{ repetitions = 2 }}, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.repetition and not context.repetition_only then + if is_decimal(context.other_card) then + return { + message = 'Again!', + repetitions = card.ability.extra.repetitions, + card = context.blueprint_card or card + } + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one decimal card + for _, v in pairs(G.playing_cards) do + if is_decimal(v) then return true end + end + return false + + end +}) + +--Rainbow Flag +create_joker({ + name = 'Rainbow Flag', id = 38, + rarity = 'Uncommon', cost = 5, + + gameplay_tag = {'rank_decimal'}, + + blueprint = true, eternal = true, perishable = true, + + vars = { {odds_poly = 6} }, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = G.P_CENTERS['e_polychrome'] + + return { vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_poly}} + end, + + calculate = function(self, card, context) + if context.after and next(context.poker_hands['Straight']) and context.scoring_hand then + + --Check if the Joker should be activated or not + local is_activate = false + for i=1, #context.scoring_hand do + if is_decimal(context.scoring_hand[i]) then + is_activate = true + break + end + end + + if is_activate then + local eligible_card = {} + + --Populate the list of eligible card + for i=1, #context.scoring_hand do + if (context.scoring_hand[i].edition or {}).key ~= 'e_polychrome' and not context.scoring_hand[i].becoming_poly then + eligible_card[#eligible_card+1] = context.scoring_hand[i] + end + end + + --If there is eligible card, and the random chance hits + if #eligible_card > 0 and pseudorandom('rainbowflag'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_poly then + local targetCard = pseudorandom_element(eligible_card, pseudoseed('rainbowflag')..G.SEED) + + targetCard.becoming_poly = true + event({trigger = 'after', delay = 0.4, func = function() + big_juice(context.blueprint_card or card) + targetCard:set_edition("e_polychrome", true, false) + targetCard.becoming_poly = nil + return true end + }) + delay(2.5) + end + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one decimal card + for _, v in pairs(G.playing_cards) do + if is_decimal(v) then return true end + end + return false + + end +}) + +--Binary-line Jokers + +local chipsAbilityMatch = { + m_stone = 50, + m_unstb_resource = 0, + m_unstb_radioactive = 13, + m_unstb_biohazard = 0 +} + +create_joker({ + name = 'Dummy Data', id = 6, + rarity = 'Uncommon', cost = 6, + + gameplay_tag = {'rank_binary'}, + + vars = {{odds = 2}, {unscored_card = {}}}, + + custom_vars = function(self, info_queue, card) + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds}} + end, + + blueprint = false, eternal = true, perishable = true, + + add_to_deck = function(self, card, from_debuff) + --Enable rank 0 card in pools + if not from_debuff then + setPoolRankFlagEnable('unstb_0', true); + setPoolRankFlagEnable('unstb_1', true); + end + end, + + calculate = function(self, card, context) + if context.before and context.scoring_hand and not context.blueprint then + card.ability.extra.unscored_card = {} + for k, v in pairs(context.full_hand) do + local unscoring = true + for _k,_v in pairs(context.scoring_hand) do + if v == _v then + unscoring = false + break + end + end + + if unscoring and not v.debuff then + card.ability.extra.unscored_card[#card.ability.extra.unscored_card+1] = v + end + end + end + + if context.after and not context.blueprint then + for i = 1, #card.ability.extra.unscored_card do + + local isTurning = pseudorandom('dummy'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds + if isTurning then + local currentCard = card.ability.extra.unscored_card[i] + + currentCard.ability.perma_bonus = (currentCard.ability.perma_bonus or 0) + SMODS.Ranks[currentCard.base.value].nominal + + --Flipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('card1', 1); currentCard:juice_up(0.3, 0.3); return true end }) + + --Changing Card Property + + event({trigger = 'after', delay = 0.05, func = function() + + local suit_data = SMODS.Suits[currentCard.base.suit] + local suit_prefix = suit_data.card_key + + currentCard:set_base(G.P_CARDS[suit_prefix .. '_unstb_0' ]) + + --Un-stoned the stone card + if currentCard.config.center.key == 'm_unstb_slop' or chipsAbilityMatch[currentCard.config.center.key] then + currentCard:set_ability(G.P_CENTERS.c_base) + end + + return true end }) + + --Unflipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('tarot2', 1, 0.6); big_juice(card); currentCard:juice_up(0.3, 0.3); return true end }) + forced_message("Zero!", currentCard, G.C.GREY, true) + end + end + end + end +}) + +create_joker({ + name = 'Micro SD Card', id = 4, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'rank_binary', 'j_alter', 'j_powerful'}, + + vars = {{odds_current = 0}, {odds_destroy = 512}, {stored_chips = 0}}, + + custom_vars = function(self, info_queue, card) + + local activate_text = 'Inactive' + local activate_color = G.C.RED + if G.jokers and G.jokers.cards[1] == card then + activate_text = 'Active' + activate_color = G.C.GREEN + end + + return {vars = {G.GAME and G.GAME.probabilities.normal * card.ability.extra.odds_current or 0, card.ability.extra.odds_destroy, G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.stored_chips, activate_text, + colours = {activate_color} }} + end, + + blueprint = true, eternal = false, perishable = false, + + --Set sprites and hitbox + + set_sprites = function(self, card, front) + local w_scale, h_scale = 41/71, 59/95 + card.children.center.scale.y = card.children.center.scale.y * h_scale + card.children.center.scale.x = card.children.center.scale.x * w_scale + end, + + set_ability = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 41/71, 59/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + load = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 41/71, 59/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + add_to_deck = function(self, card, from_debuff) + --Enable rank 0 card in pools + setPoolRankFlagEnable('unstb_0', true); + setPoolRankFlagEnable('unstb_1', true); + end, + + calculate = function(self, card, context) + --This part handles the chip reward + if context.joker_main then + return { + chip_mod = card.ability.extra.stored_chips, + message = localize { type = 'variable', key = 'a_chips', vars = { card.ability.extra.stored_chips } } + } + end + + --The scaling part is not copyable by Blueprint + if context.discard and not context.blueprint then + + --Check if the joker is on the leftmost slot + if G.jokers.cards[1] == card then + local currentCard = context.other_card + + --Not debuffed, and isn't face card, and is base card + if not currentCard:is_face() and not currentCard.debuff and currentCard.config.center == G.P_CENTERS.c_base then + + local bonusChip = currentCard.ability.perma_bonus or 0 + local baseChip = SMODS.Ranks[currentCard.base.value].nominal + + local totalChip = baseChip + bonusChip + + if totalChip>0 then + card.ability.extra.stored_chips = (card.ability.extra.stored_chips or 0) + totalChip + + card.ability.extra.odds_current = (card.ability.extra.odds_current or 0) + totalChip + + --Change card + event({trigger = 'after', delay = 0.02, func = function() + + local suit_data = SMODS.Suits[currentCard.base.suit] + local suit_prefix = suit_data.card_key + + currentCard:juice_up(0.3, 0.3); + currentCard:set_base(G.P_CARDS[suit_prefix .. '_unstb_0' ]) + + return true end }) + + return { + message = localize { type = 'variable', key = 'a_chips', vars = { totalChip } }, + colour = G.C.CHIPS, + card = card + } + end + end + end + + end + + --End of round check, make sure it's checked absolutely once per round + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + if pseudorandom('sdcard'..G.SEED) < G.GAME.probabilities.normal * card.ability.extra.odds_current / card.ability.extra.odds_destroy then + event({func = function() + + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + + --Destroy Card + event({trigger = 'after', delay = 0.3, func = function() + + G.jokers:remove_card(card) + card:remove() + card = nil + return true end }) + + return true end }) + return { + message = 'Corrupted...', + colour = G.C.BLACK + } + else + return { + message = 'Safe!' + } + end + end + end +}) + +create_joker({ + name = 'Social Experiment', id = 65, + rarity = 'Rare', cost = 9, + + gameplay_tag = {'rank_binary', 'j_powerful'}, + + blueprint = false, eternal = true, perishable = true, + + add_to_deck = function(self, card, from_debuff) + --Enable rank 0 card in pools + setPoolRankFlagEnable('unstb_0', true); + setPoolRankFlagEnable('unstb_1', true); + end, + + calculate = function(self, card, context) + + if context.after and context.scoring_hand and #context.scoring_hand > 1 and not context.blueprint then + local totalChipCount = 0 + + for i = 1, #context.scoring_hand do + if i<#context.scoring_hand and not (context.scoring_hand[i]:is_face() and not (context.scoring_hand[i].config.center.key == 'm_unstb_slop' or context.scoring_hand[i].config.center.no_rank)) then --context.scoring_hand[i].config.center ~= G.P_CENTERS.m_stone then --Check if it is not a Stone card + local currentCard = context.scoring_hand[i] + + local bonusChip = currentCard.ability.perma_bonus or 0 + + + local baseChip = SMODS.Ranks[currentCard.base.value].nominal + + if currentCard.config.center.key == 'm_unstb_slop' then + baseChip = 0 + bonusChip = bonusChip + currentCard.ability.extra.chips + elseif chipsAbilityMatch[currentCard.config.center.key] then + baseChip = 0 + bonusChip = bonusChip + chipsAbilityMatch[currentCard.config.center.key] + end + + if bonusChip + baseChip > 0 then + + context.scoring_hand[i+1].ability.perma_bonus = (context.scoring_hand[i+1].ability.perma_bonus or 0) + (bonusChip + baseChip)*2 + + totalChipCount = totalChipCount + bonusChip + baseChip + + currentCard.ability.perma_bonus = 0 + + --Flipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('card1', 1); currentCard:juice_up(0.3, 0.3); return true end }) + + --Changing Card Property + + event({trigger = 'after', delay = 0.05, func = function() + + local suit_data = SMODS.Suits[currentCard.base.suit] + local suit_prefix = suit_data.card_key + + currentCard:set_base(G.P_CARDS[suit_prefix .. '_unstb_0' ]) + + --Un-stoned the stone card + --print(currentCard.config.center.key) + --print(chipsAbilityMatch[currentCard.config.center.key]) + if currentCard.config.center.key == 'm_unstb_slop' or chipsAbilityMatch[currentCard.config.center.key] then + currentCard:set_ability(G.P_CENTERS.c_base) + end + + return true end }) + + --Unflipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('tarot2', 1, 0.6); big_juice(card); currentCard:juice_up(0.3, 0.3); return true end }) + forced_message("Double It!", currentCard, G.C.CHIPS, true) + end + + else + if totalChipCount > 0 then + local currentCard = context.scoring_hand[i] + + event({ trigger = 'after', delay = 0.2, func = function() + big_juice(currentCard) + play_sound('multhit1') + return true end }) + + forced_message("Take!", currentCard, G.C.CHIPS, true) + + totalChipCount = 0 + end + end + end + + end + + end +}) + +create_joker({ + name = 'Power of One', id = 13, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'rank_binary'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{mult_rate = '4'}, {mult = '0'}}, + + custom_vars = function(self, info_queue, card) + + --Check the number of 1 in the deck + local one_count=0 + if (G.playing_cards) then + for _, v in pairs(G.playing_cards) do + if v.base.value == 'unstb_1' then one_count = one_count + 1 end + end + return { vars = {card.ability.extra.mult_rate, one_count*card.ability.extra.mult_rate}} + else + return { vars = {card.ability.extra.mult_rate, 0}} + end + end, + + + calculate = function(self, card, context) + --Main context + if context.joker_main then + local one_count=0 + for _, v in pairs(G.playing_cards) do + if v.base.value == 'unstb_1' then one_count = one_count + 1 end + end + + card.ability.extra.mult = one_count*card.ability.extra.mult_rate + + return { + mult_mod = card.ability.extra.mult, + message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult } } + } + end + + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one rank 1 card + for _, v in pairs(G.playing_cards) do + if v.base.value == 'unstb_1' then return true end + end + return false + + end +}) + +local binary_rank = {'unstb_0', 'unstb_1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace', 'unstb_???'} + +create_joker({ + name = 'Binary Number', id = 5, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'rank_binary'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {}, + + + calculate = function(self, card, context) + --Main context + if context.after then + local hand = context.full_hand + + --Parse the hand and check if it's eligible in one go + + --Ineligible hand + if #hand > 4 then return end + + local is_binary = true + local final_rank = 0 + local suit_list = {} + + for i=1, #hand do + local rank = hand[i].base.value + + if rank == 'unstb_1' then + final_rank = final_rank + 2 ^ (#hand-i) + suit_list[#suit_list+1] = hand[1].base.suit + elseif rank == 'unstb_0' then + --Do nothing, really + suit_list[#suit_list+1] = hand[1].base.suit + else + is_binary = false + break + end + end + + --If the hand is binary number, create card accordingly + if is_binary then + + local target_rank = binary_rank[final_rank+1] or 'unstb_???' + + --print(final_rank) + --print(target_rank) + + --Create Card + event({func = function() + local rank = SMODS.Ranks[target_rank].card_key + local suit = SMODS.Suits[pseudorandom_element(suit_list, pseudoseed('binary')..G.SEED)].card_key + + local _card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS[suit..'_'..rank], G.P_CENTERS.c_base, {playing_card = G.playing_card}) + + + --Juice up the Joker + card:juice_up(0.3, 0.3) + + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.play:emplace(_card) + table.insert(G.playing_cards, _card) + + return true end + }) + + event({func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + return true end + }) + + delay(1) + + event({func = function() + draw_card(G.play,G.deck, 90,'up', nil) + return true end + }) + + playing_card_joker_effects({true}) + end + + end + + end, + + custom_in_pool = function(self, args) + --If rank 0 and 1 is unlocked this run + return getPoolRankFlagEnable('unstb_0') and getPoolRankFlagEnable('unstb_1') + + end +}) + +--Enhancement-line Jokers + +--Quintuplets +create_joker({ + name = 'Quintuplets', id = 2, + rarity = 'Uncommon', cost = 6, + + blueprint = true, eternal = true, perishable = true, + + vars = {{scoring_name = ''}, {scoring_hand = {}}}, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = G.P_TAGS.tag_negative + end, + + calculate = function(self, card, context) + --Keep track of scoring hand + --[[if context.after and context.scoring_name ~= nil and context.scoring_hand and not context.blueprint then + card.ability.extra.scoring_name = context.scoring_name + card.ability.extra.scoring_hand = context.scoring_hand + end + + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and card.ability.extra.scoring_name == 'Flush Five' then + + local isActivated = true + + for i = 1, #card.ability.extra.scoring_hand do + if card.ability.extra.scoring_hand[i].config.center == G.P_CENTERS.c_base then + isActivated = false + break + end + end + + if isActivated then + if not context.blueprint then + card.ability.extra.scoring_name = '' + end + + event({ trigger = 'after', delay = 0.5, func = function() + card:juice_up(0.3, 0.3) + + add_tag(Tag('tag_negative')) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + + return true end + } + ) + end + end]] + + if context.after and next(context.poker_hands['Five of a Kind']) then + event({ trigger = 'after', delay = 0.5, func = function() + (context.blueprint_card or card):juice_up(0.3, 0.3) + + add_tag(Tag('tag_negative')) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + + return true end + } + ) + end + end +}) + +--Edition-line Jokers + +--Graphic Card +create_joker({ + name = 'Graphic Card', id = 69, + rarity = 'Rare', cost = 8, + + blueprint = true, eternal = true, perishable = true, + + vars = {{ count = 5 }, {current_count = 0}}, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = G.P_TAGS.tag_double + return {vars = {card.ability.extra.count, card.ability.extra.current_count}} + end, + + --Set sprites and hitbox + + set_sprites = function(self, card, front) + local w_scale, h_scale = 48/71, 95/95 + card.children.center.scale.y = card.children.center.scale.y * h_scale + card.children.center.scale.x = card.children.center.scale.x * w_scale + end, + + set_ability = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 48/71, 95/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + load = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 48/71, 95/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + calculate = function(self, card, context) + + if context.individual and context.cardarea == G.play and not context.repetition then + if context.other_card.edition then + card.ability.extra.current_count = card.ability.extra.current_count + 1 + end + end + + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + + if card.ability.extra.current_count > 0 then + + if card.ability.extra.current_count >= card.ability.extra.count then + event({ trigger = 'after', delay = 0.5, func = function() + + add_tag(Tag('tag_double')) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + + return true end + } + ) + forced_message("Tag!", card, G.C.SECONDARY_SET.Enhanced, true, false) + else + forced_message("Reset", card, G.C.SECONDARY_SET.Enhanced, true, false) + end + + end + card.ability.extra.current_count = 0 + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one card with edition + for _, v in pairs(G.playing_cards) do + if v.edition then return true end + end + return false + + end +}) + +--Connoiseur +create_joker({ + name = 'Connoiseur', id = 45, + rarity = 'Rare', cost = 8, + + gameplay_tag = {'j_powerful'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{ repetitions = 1 }}, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.repetition and not context.repetition_only then + if context.other_card.edition then + return { + message = 'Again!', + repetitions = card.ability.extra.repetitions, + card = context.blueprint_card or card + } + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one card with edition + for _, v in pairs(G.playing_cards) do + if v.edition then return true end + end + return false + + end +}) + +--Jeweler +create_joker({ + name = 'Jeweler', id = 0, + rarity = 'Uncommon', cost = 8, + + gameplay_tag = {'edition_upgrade'}, + + vars = {{odds = 4}}, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'upgrade_edition'} + + local vars + if G.GAME and G.GAME.probabilities.normal then + vars = {G.GAME.probabilities.normal, card.ability.extra.odds} + else + vars = {1, card.ability.extra.odds} + end + return {vars = vars} + end, + + blueprint = false, eternal = true, perishable = true, + + calculate = function(self, card, context) + + if context.before and not context.blueprint then + + if pseudorandom('jeweler'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds then + + forced_message("Upgrade!", card, G.C.PURPLE, true, false) + + local hand_name = context.scoring_name + + if to_big(G.GAME.hands[hand_name].level) > to_big(0) then + level_up_hand(card, hand_name, false, -1) + end + + + + for i = 1, #context.scoring_hand do + local current_card = context.scoring_hand[i] + edition_upgrade(context.scoring_hand[i]) + + --[[event({delay = 0, trigger = 'before', + func = function() + edition_upgrade(current_card) + + return true end} + )]] + end + end + + end + + end +}) + +--New Enhancements Support Stuff + +--Slop Card Lines + +--Joker Diffusion +create_joker({ + name = 'Joker Diffusion', id = 26, + rarity = 'Uncommon', cost = 6, + + gameplay_tag = {'enh_custom'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{count = '3'}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_slop'] + + return { vars = {card.ability.extra.count}} + end, + + + calculate = function(self, card, context) + if context.after then + local is_activate = #context.scoring_hand ~= #context.full_hand + + --Terminate if the condition does not met + if not is_activate then return end + + --Populate Candidate Card + local eligible_card = {} + local converted_card = {} + + for i=1, #G.hand.cards do + if G.hand.cards[i].config.center == G.P_CENTERS.c_base and not G.hand.cards[i].to_convert then eligible_card[#eligible_card+1] = G.hand.cards[i] end + end + + table.sort(eligible_card, function (a, b) return not a.playing_card or not b.playing_card or a.playing_card < b.playing_card end) + pseudoshuffle(eligible_card, pseudoseed('jokerdiffusion'..G.SEED)) + + + for i = 1, card.ability.extra.count do converted_card[#converted_card+1] = eligible_card[i] end + + if #converted_card > 0 then + --Animation from Basegame Tarot + for i=1, #converted_card do + converted_card[i].to_convert = true + + local percent = 1.15 - (i-0.999)/(#converted_card-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() big_juice(context.blueprint_card or card); converted_card[i]:flip();play_sound('card1', percent);converted_card[i]:juice_up(0.3, 0.3);return true end }) + end + delay(0.2) + + --Handle the conversion + for i=1, #converted_card do + event({trigger = 'after',delay = 0.1,func = function() + converted_card[i]:set_ability(G.P_CENTERS.m_unstb_slop) + return true end }) + end + + for i=1, #converted_card do + local percent = 0.85 + (i-0.999)/(#converted_card-0.998)*0.3 + event({trigger = 'after',delay = 0.15,func = function() converted_card[i]:flip();play_sound('tarot2', percent, 0.6);converted_card[i]:juice_up(0.3, 0.3); converted_card[i].to_convert = nil; return true end }) + end + delay(0.5) + end + end + + end, +}) + +--Non-Fungible Joker +create_joker({ + name = 'NonFungible Joker', id = 27, + rarity = 'Uncommon', cost = 6, + + gameplay_tag = {'enh_custom'}, + + blueprint = false, eternal = false, perishable = true, + + vars = {{count = '1'}, {max_payout = 10}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_slop'] + + return { vars = {card.ability.extra.count, card.ability.extra.max_payout}} + end, + + + calculate = function(self, card, context) + if context.pre_discard and not context.blueprint then + local slop_count = 0 + for i=1, #context.full_hand do + if context.full_hand[i].config.center == G.P_CENTERS.m_unstb_slop then slop_count = slop_count + 1 end + end + + card.ability.extra.is_activate = slop_count==1 + end + + if context.discard and not context.other_card.debuff and not context.blueprint then + if card.ability.extra.is_activate and context.other_card.config.center == G.P_CENTERS.m_unstb_slop then + ease_dollars(pseudorandom('nfj'..G.SEED, 0, card.ability.extra.max_payout), true) + + return { + --play_sound('whoosh1', math.random()*0.1 + 0.6,0.3), + message = 'Sold!', + colour = G.C.GOLD, + remove = true, + card = card + } + end + end + + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + if card.ability.extra.max_payout > 0 or card.sell_cost > 0 then + if card.ability.extra.max_payout>0 then + card.ability.extra.max_payout = card.ability.extra.max_payout - 1 + end + + --Reduce its own selling price too, for the funny + + if card.sell_cost > 0 then + card.ability.extra_value = (card.ability.extra_value or 0) - 1 + card:set_cost() + end + + return{ + message = "Price Dropped", + colour = G.C.RED, + } + end + end + + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one slop card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_slop then return true end + end + return false + + end +}) + +--Prompt +create_joker({ + name = 'Prompt', id = 28, + rarity = 'Common', cost = 7, + + gameplay_tag = {'enh_custom'}, + + blueprint = true, eternal = true, perishable = true, + + --vars = { {times = 1}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_slop'] + + return { vars = {}} + end, + + --Prompt's actual ability is in Slop Card's after_play function + --[[calculate = function(self, card, context) + + end,]] + + custom_in_pool = function(self, args) + + --Spawns if there is at least one slop card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_slop then return true end + end + return false + + end +}) + +--Uninterested Primate +create_joker({ + name = 'Uninterested Primate', id = 29, + rarity = 'Common', cost = 5, + + gameplay_tag = {'enh_custom'}, + + blueprint = true, eternal = false, perishable = false, + + vars = { {chips = 50}, {chips_rate = 10}, {slop_scored = 0}, {slop_cycle = 3}, {chance_destroy = 8}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_slop'] + + return { vars = {card.ability.extra.chips_rate, card.ability.extra.slop_cycle, G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.chance_destroy, card.ability.extra.chips, card.ability.extra.slop_cycle - card.ability.extra.slop_scored}} + end, + + calculate = function(self, card, context) + --Main Context + if context.joker_main then + return { + chip_mod = card.ability.extra.chips, + message = localize { type = 'variable', key = 'a_chips', vars = { card.ability.extra.chips } } + } + end + + --Scaling, cannot copy via blueprint + if context.individual and context.cardarea == G.play and not context.blueprint then + if context.other_card.config.center == G.P_CENTERS.m_unstb_slop then + card.ability.extra.slop_scored = card.ability.extra.slop_scored + 1 + + if card.ability.extra.slop_scored >= card.ability.extra.slop_cycle then + card.ability.extra.slop_scored = 0 + + card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chips_rate + + forced_message("+"..card.ability.extra.chips_rate, card, G.C.CHIPS, true) + end + + end + end + + --End of round check, make sure it's checked absolutely once per round + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + + --Reduce its own selling price, for the funny + + if card.sell_cost > 0 then + card.ability.extra_value = (card.ability.extra_value or 0) - 1 + card:set_cost() + end + + if pseudorandom('primate'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.chance_destroy then + event({func = function() + + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + + --Destroy Card + event({trigger = 'after', delay = 0.3, func = function() + + G.jokers:remove_card(card) + card:remove() + card = nil + return true end }) + + return true end }) + + G.GAME.pool_flags.primate_gone = true + return { + message = 'My Primates Gone' + } + else + return { + message = 'Safe!' + } + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one slop card + primate is not gone yet + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_slop then return not G.GAME.pool_flags.primate_gone end + end + return false + + end +}) + +--Lethargic Lion +create_joker({ + name = 'Lethargic Lion', id = 30, + rarity = 'Common', cost = 5, + + gameplay_tag = {'enh_custom'}, + + blueprint = true, eternal = false, perishable = false, + + vars = { {xmult = 2}, {xmult_rate = 0.02}, {slop_scored = 0}, {slop_cycle = 3}, {chance_destroy = 8}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_slop'] + + return { vars = {card.ability.extra.xmult_rate, card.ability.extra.slop_cycle, G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.chance_destroy, card.ability.extra.xmult, card.ability.extra.slop_cycle - card.ability.extra.slop_scored}} + end, + + calculate = function(self, card, context) + --Main Context + if context.joker_main then + return { + Xmult_mod = card.ability.extra.xmult, + message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.xmult } } + } + end + + --Scaling, cannot copy via blueprint + if context.individual and context.cardarea == G.play and not context.blueprint then + if context.other_card.config.center == G.P_CENTERS.m_unstb_slop then + card.ability.extra.slop_scored = card.ability.extra.slop_scored + 1 + + if card.ability.extra.slop_scored >= card.ability.extra.slop_cycle then + card.ability.extra.slop_scored = 0 + + card.ability.extra.xmult = card.ability.extra.xmult + card.ability.extra.xmult_rate + + forced_message("+"..card.ability.extra.xmult_rate, card, G.C.MULT, true) + end + + end + end + + --End of round check, make sure it's checked absolutely once per round + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + + --Reduce its own selling price, for the funny + + if card.sell_cost > 0 then + card.ability.extra_value = (card.ability.extra_value or 0) - 1 + card:set_cost() + end + + if pseudorandom('primate'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.chance_destroy then + event({func = function() + + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + + --Destroy Card + event({trigger = 'after', delay = 0.3, func = function() + + G.jokers:remove_card(card) + card:remove() + card = nil + return true end }) + + return true end }) + return { + message = 'My Lions Gone' + } + else + return { + message = 'Safe!' + } + end + end + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one slop card + primate is gone + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_slop then return G.GAME.pool_flags.primate_gone end + end + return false + + end +}) + + +--Vintage Joker +create_joker({ + name = 'Vintage Joker', id = 23, + rarity = 'Uncommon', cost = 6, + + gameplay_tag = {'enh_custom'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{chance_fix = '4'}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_vintage'] + + return { vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.chance_fix}} + end, + + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.config.center == G.P_CENTERS.m_unstb_vintage then + if pseudorandom('vintagejoker'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.chance_fix then + context.other_card.ability.extra.current_odd = 0 --math.ceil(context.other_card.ability.extra.current_odd * 0.5) + + event({trigger = 'after', func = function() + play_sound('tarot2', 1, 0.6); + big_juice(context.blueprint_card or card) + return true end }) + forced_message("Restored!", context.other_card, G.C.GREEN, true) + end + end + end + + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one vintage card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_vintage then return true end + end + return false + + end +}) + +--Rules Errata +create_joker({ + name = 'Rules Errata', id = 24, + rarity = 'Uncommon', cost = 5, + + gameplay_tag = {'enh_custom'}, + + blueprint = false, eternal = true, perishable = true, + + --vars = {{}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = {set = 'Other', key = 'acorn_tooltip'} + + return { vars = {}} + end, + + --This Joker's effect is in Acorn Mark Card's code itself + --[[calculate = function(self, card, context) + + if context.individual and context.cardarea == G.play then + + end + + end,]] + + custom_in_pool = function(self, args) + + --Spawns if there is at least one acorn card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_acorn then return true end + end + return false + + end +}) + +--Auction Winner +create_joker({ + name = 'Auction Winner', id = 25, + rarity = 'Common', cost = 6, + + gameplay_tag = {'enh_custom'}, + + blueprint = true, eternal = true, perishable = true, + + --vars = {{}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_promo'] + + return { vars = {}} + end, + + calculate = function(self, card, context) + + if context.remove_playing_cards then + + local money = 0 + + for _, v in pairs(context.removed) do + if v.config.center == G.P_CENTERS.m_unstb_promo then + money = money + v.ability.extra.gold + end + end + + if money>0 then + event({trigger = 'after', func = function() + ease_dollars(money, true) + big_juice(context.blueprint_card or card) + return true end }) + forced_message('$'..money, context.blueprint_card or card, G.C.GOLD, true) + end + + end + + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one promo card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_promo then return true end + end + return false + + end +}) + +create_joker({ + name = 'Joker Island', id = 7, + rarity = 'Uncommon', cost = 6, + + gameplay_tag = {'enh_custom'}, + + vars = {{target_rank = 2}, {odds_ticket = 6}}, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'resource_tooltip'} + return {vars = {localize(card.ability.extra.target_rank, 'ranks'), G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_ticket}} + end, + + blueprint = true, eternal = true, perishable = true, + + set_ability = function(self, card, initial, delay_sprites) + --Random possible rank + local rank = '2' + if G.playing_cards then + rank = get_valid_card_from_deck('jokerisland'..G.SEED).rank --pseudorandom_element(G.playing_cards, pseudoseed('jokerisland')..G.SEED).base.value + end + + card.ability.extra.target_rank = rank + end, + + add_to_deck = function(self, card, from_debuff) + if not G.GAME.pool_flags.catan_enabled then + G.GAME.pool_flags.catan_enabled = true + end + end, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + local currentCard = context.other_card + if currentCard.base.value == card.ability.extra.target_rank and not currentCard.config.center.no_rank then + + local isActivated = pseudorandom('jokerisland'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_ticket + + if isActivated then + event({func = function() + local rank = pseudorandom_element(SMODS.Ranks, pseudoseed('jokerisland')..G.SEED).card_key + local suit = SMODS.Suits[currentCard.base.suit].card_key + + local _card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS[suit..'_'..rank], G.P_CENTERS.m_unstb_resource, {playing_card = G.playing_card}) + + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.play:emplace(_card) + table.insert(G.playing_cards, _card) + + return true end + }) + + event({func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + draw_card(G.play,G.deck, 90,'up', nil) + return true end + }) + + playing_card_joker_effects({true}) + end + end + end + + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + card.ability.extra.target_rank = get_valid_card_from_deck('jokerisland'..G.SEED).rank --pseudorandom_element(G.playing_cards, pseudoseed('jokerisland')..G.SEED).base.value --pseudorandom_element(SMODS.Ranks, pseudoseed('jokerisland')..G.SEED).key + return{ + message = "Randomize" + } + end + end +}) + +--New Anti-Enhancement Stuff + +create_joker({ + name = 'Kaiju', id = 17, + rarity = 'Uncommon', cost = 8, + + gameplay_tag = {'enh_disenh'}, + + vars = {{add_slot = 4}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_radioactive'] + + return {vars = {card.ability.extra.add_slot}} + end, + + blueprint = false, eternal = true, perishable = true, + + add_to_deck = function(self, card, from_debuff) + if not G.GAME.pool_flags.radioactive_enabled then + G.GAME.pool_flags.radioactive_enabled = true + end + + G.jokers.config.card_limit = G.jokers.config.card_limit + card.ability.extra.add_slot + end, + + remove_from_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit - card.ability.extra.add_slot + end, + + calculate = function(self, card, context) + if context.first_hand_drawn then + event({delay = 0.4, trigger = 'after', + func = function() + local eligible_list={} + for i=1, #G.hand.cards do + if G.hand.cards[i].config.center ~= G.P_CENTERS.m_unstb_radioactive then table.insert(eligible_list,G.hand.cards[i]) end + end + if #eligible_list>0 then + local enhanced_card = pseudorandom_element(eligible_list, pseudoseed('kaiju'..G.SEED)) + enhanced_card:set_disenhancement(G.P_CENTERS.m_unstb_radioactive , nil, true) + play_sound('tarot1') + enhanced_card:juice_up() + end + + return true end} + ) + end + end +}) + +create_joker({ + name = 'Poison the Well', id = 18, + rarity = 'Uncommon', cost = 8, + + gameplay_tag = {'enh_disenh'}, + + vars = {{discard_size = 4}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = {set = 'Other', key = 'poison_tooltip'} + + return {vars = {card.ability.extra.discard_size}} + end, + + blueprint = false, eternal = true, perishable = true, + + add_to_deck = function(self, card, from_debuff) + if not G.GAME.pool_flags.poison_enabled then + G.GAME.pool_flags.poison_enabled = true + end + + G.GAME.round_resets.discards = G.GAME.round_resets.discards + card.ability.extra.discard_size + ease_discard(3) + end, + + remove_from_deck = function(self, card, from_debuff) + G.GAME.round_resets.discards = G.GAME.round_resets.discards - card.ability.extra.discard_size + ease_discard(-3) + end, + + calculate = function(self, card, context) + if context.pre_discard and not context.blueprint then + local target_card = pseudorandom_element(G.hand.highlighted, pseudoseed(seed or 'poisonwell'..G.GAME.round_resets.ante)) + + event({trigger = 'after', func = function() + play_sound('generic1') + target_card:juice_up(0.3, 0.3); + target_card:set_disenhancement(G.P_CENTERS.m_unstb_poison , nil, true) + + return true end }) + end + end +}) + +create_joker({ + name = 'Petri Dish', id = 19, + rarity = 'Uncommon', cost = 8, + + gameplay_tag = {'enh_disenh'}, + + vars = {{adds_hand = 4}, {odds = 4}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_biohazard'] + + return {vars = {card.ability.extra.adds_hand, G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds}} + end, + + blueprint = false, eternal = true, perishable = true, + + add_to_deck = function(self, card, from_debuff) + if not G.GAME.pool_flags.biohazard_enabled then + G.GAME.pool_flags.biohazard_enabled = true + end + + G.GAME.round_resets.hands = G.GAME.round_resets.hands + card.ability.extra.adds_hand + ease_hands_played(3) + end, + + remove_from_deck = function(self, card, from_debuff) + G.GAME.round_resets.hands = G.GAME.round_resets.hands - card.ability.extra.adds_hand + ease_hands_played(-3) + end, + + calculate = function(self, card, context) + if context.after and not context.blueprint then + if pseudorandom('petridish'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds then + local target_card = pseudorandom_element(context.full_hand, pseudoseed(seed or 'petridishtarget'..G.SEED)) + + event({trigger = 'after', func = function() + play_sound('generic1') + target_card:juice_up(0.3, 0.3); + target_card:set_disenhancement(G.P_CENTERS.m_unstb_biohazard) + + return true end }) + + forced_message("Infected", target_card, G.C.RED, true) + end + end + end +}) + +--Anti-Enhancement Supports +create_joker({ + name = 'Geiger Counter', id = 20, + rarity = 'Uncommon', cost = 6, + + gameplay_tag = {'enh_disenh'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{mult_rate = '6'}, {mult = '0'}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_radioactive'] + + --Check the number of radioactive card in the deck + local count =0 + if (G.playing_cards) then + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_radioactive then count = count + 1 end + end + return { vars = {card.ability.extra.mult_rate, count*card.ability.extra.mult_rate}} + else + return { vars = {card.ability.extra.mult_rate, 0}} + end + end, + + --Set sprites and hitbox + + set_sprites = function(self, card, front) + local w_scale, h_scale = 53/71, 95/95 + card.children.center.scale.y = card.children.center.scale.y * h_scale + card.children.center.scale.x = card.children.center.scale.x * w_scale + end, + + set_ability = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 53/71, 95/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + load = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 53/71, 95/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + calculate = function(self, card, context) + --Main context + if context.joker_main then + local count=0 + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_radioactive then count = count + 1 end + end + + card.ability.extra.mult = count*card.ability.extra.mult_rate + + return { + mult_mod = card.ability.extra.mult, + message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult } } + } + end + + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one radioactive card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_radioactive then return true end + end + return false + + end +}) + +create_joker({ + name = 'Strych Nine', id = 21, + rarity = 'Uncommon', cost = 5, + + gameplay_tag = {'enh_disenh'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{chip_rate = '9'}, {chip = '0'}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = {set = 'Other', key = 'poison_tooltip'} + + --Check the number of poison card in the deck + local count =0 + if (G.playing_cards) then + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_poison then count = count + 1 end + end + return { vars = {card.ability.extra.chip_rate, count*card.ability.extra.chip_rate}} + else + return { vars = {card.ability.extra.chip_rate, 0}} + end + end, + + + calculate = function(self, card, context) + --Main context + if context.joker_main then + local count=0 + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_poison then count = count + 1 end + end + + card.ability.extra.chip = count*card.ability.extra.chip_rate + + return { + chip_mod = card.ability.extra.chip, + message = localize { type = 'variable', key = 'a_chips', vars = { card.ability.extra.chip } } + } + end + + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one poison card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_poison then return true end + end + return false + + end +}) + +create_joker({ + name = 'Vaccination Card', id = 22, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'enh_disenh'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{xmult_rate = '0.5'}, {xmult = '1'}}, + + custom_vars = function(self, info_queue, card) + + info_queue[#info_queue+1] = G.P_CENTERS['m_unstb_biohazard'] + + --Check the number of biohazard card on hand + local count = 0 + if (G.hand) then + for i = 1, #G.hand.cards do + if G.hand.cards[i].config.center == G.P_CENTERS.m_unstb_biohazard and not G.hand.cards[i].healed then + count = count+1 + end + end + return { vars = {card.ability.extra.xmult_rate, 1+count*card.ability.extra.xmult_rate}} + else + return { vars = {card.ability.extra.xmult_rate, 1}} + end + end, + + + calculate = function(self, card, context) + --Main context + if context.joker_main then + local count=0 + + for i = 1, #G.hand.cards do + if G.hand.cards[i].config.center == G.P_CENTERS.m_unstb_biohazard and not G.hand.cards[i].healed then + count = count+1 + end + end + + card.ability.extra.xmult = 1+count*card.ability.extra.xmult_rate + + return { + Xmult_mod = card.ability.extra.xmult, + message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.xmult } } + } + end + + end, + + custom_in_pool = function(self, args) + + --Spawns if there is at least one biohazard card + for _, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_unstb_biohazard then return true end + end + return false + + end +}) + +--Miscellaneous + +-- Joker 2 + +create_joker({ + name = 'Joker2', id = 11, + rarity = 'Common', cost = 4, + + gameplay_tag = {'j_shitpost'}, + + vars = {{mult = 4}, {xmult = 2}, {odds_destroy = 4}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.mult, card.ability.extra.xmult, G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_destroy}} + end, + + blueprint = true, eternal = false, perishable = true, + + calculate = function(self, card, context) + + --Main context + if context.joker_main then + return { + mult = card.ability.extra.mult, + xmult = card.ability.extra.xmult, + --message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.xmult } } + } + end + + --End of round check, make sure it's checked absolutely once per round + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + if pseudorandom('joker2'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_destroy then + event({func = function() + + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + + --Destroy Card + event({trigger = 'after', delay = 0.3, func = function() + + G.jokers:remove_card(card) + card:remove() + card = nil + return true end }) + + return true end }) + return { + message = 'Flopped', + colour = G.C.ORANGE + } + else + return { + message = 'Safe!', + colour = G.C.GREEN + } + end + end + end +}) + +-- Joker Stairs +create_joker({ + name = 'Joker Stairs', id = 12, + rarity = 'Uncommon', cost = 8, + + gameplay_tag = {'j_shitpost'}, + + vars = {{mult_rate = 4}, {mult = 0}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.mult_rate, card.ability.extra.mult}} + end, + + blueprint = true, eternal = true, perishable = false, + + calculate = function(self, card, context) + --Shop + if context.buying_card and context.card ~= card then + if unstb_global.name_joker[context.card.config.center.key] then + --print("Joker Stair Triggered!") + card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_rate + event({ + func = function() + big_juice(card) + forced_message("Upgraded!", card, G.C.MULT, true) + --card_eval_status_text(card,'extra',nil, nil, nil,{message = "Upgraded", colour = G.C.MULT, instant = true}) + return true end}) + end + end + + --Main context + if context.joker_main then + if card.ability.extra.mult > 0 then + return { + mult_mod = card.ability.extra.mult, + message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult } } + } + end + end + + end +}) + +--A special function to check for blueprint compat for both sides +G.FUNCS.blueprint_compat_dside_l = function(e) + if e.config.ref_table.ability.blueprint_compat_l ~= e.config.ref_table.ability.blueprint_compat_check_l then + if e.config.ref_table.ability.blueprint_compat_l == 'compatible' then + e.config.colour = mix_colours(G.C.GREEN, G.C.JOKER_GREY, 0.8) + elseif e.config.ref_table.ability.blueprint_compat_l == 'incompatible' then + e.config.colour = mix_colours(G.C.RED, G.C.JOKER_GREY, 0.8) + end + e.config.ref_table.ability.blueprint_compat_ui_l = ' '..localize('k_blueprint_l_'..e.config.ref_table.ability.blueprint_compat_l)..' ' + e.config.ref_table.ability.blueprint_compat_check_l = e.config.ref_table.ability.blueprint_compat_l + end +end + +G.FUNCS.blueprint_compat_dside_r = function(e) + if e.config.ref_table.ability.blueprint_compat_r ~= e.config.ref_table.ability.blueprint_compat_check_r then + if e.config.ref_table.ability.blueprint_compat_r == 'compatible' then + e.config.colour = mix_colours(G.C.GREEN, G.C.JOKER_GREY, 0.8) + elseif e.config.ref_table.ability.blueprint_compat_r == 'incompatible' then + e.config.colour = mix_colours(G.C.RED, G.C.JOKER_GREY, 0.8) + end + e.config.ref_table.ability.blueprint_compat_ui_r = ' '..localize('k_blueprint_r_'..e.config.ref_table.ability.blueprint_compat_r)..' ' + e.config.ref_table.ability.blueprint_compat_check_r = e.config.ref_table.ability.blueprint_compat_r + end +end + +--Plagiarism +create_joker({ + name = 'Plagiarism', id = 46, + rarity = 'Uncommon', cost = 10, + + gameplay_tag = {'j_shitpost'}, + + vars = {{dir = 0}}, + + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.dir==0 and 'Left' or 'Right'}} + end, + + blueprint = true, eternal = true, perishable = true, + + set_ability = function(self, card, initial, delay_sprites) + --Random direction + card.ability.extra.dir = 0 + if pseudorandom('plagiarism'..G.SEED) > 0.5 then + card.ability.extra.dir = 1 + end + end, + + calculate = function(self, card, context) + --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 + card.ability.extra.dir = 0 + + if pseudorandom('plagiarism'..G.SEED) > 0.5 then + card.ability.extra.dir = 1 + end + + forced_message(card.ability.extra.dir==0 and 'Left' or 'Right', card, G.C.ORANGE, true) + end + + local other_joker = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + if card.ability.extra.dir==0 then + other_joker = G.jokers.cards[i - 1] + else + other_joker = G.jokers.cards[i + 1] + end + end + end + if other_joker and other_joker ~= 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: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 + + --IDK why these are not applied to the return value, it even worked fine when I commented return line out when it shouldn't + + return other_joker_ret + end + end + end +}) + +--Joker Throwing Card +create_joker({ + name = 'Joker Throwing Card', id = 47, + rarity = 'Rare', cost = 8, + + vars = {{rate = 2}, {reduce = 10}, {odds_destroy = 4}}, + + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.reduce, G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_destroy, card.ability.extra.rate}} + end, + + blueprint = false, eternal = true, perishable = false, + + calculate = function(self, card, context) + --Reduce Blind Size + if context.setting_blind and not context.blueprint then + local decrease = math.min(card.ability.extra.reduce, 70) --Failsafe, just in case other value-altering joker did the funnies + + G.GAME.blind.chips = G.GAME.blind.chips * (1 - decrease * 0.01) + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + end + + --Check before hand is played + if context.before and context.scoring_hand and not context.blueprint then + + local isActivated = pseudorandom('throwingcard'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_destroy + + if isActivated and card.ability.extra.reduce < 70 then --Won't activate if the reduction reach the hardcoded limit (to prevent other value-altering joker) + local target_card = pseudorandom_element(context.scoring_hand, pseudoseed('throwingcard'..G.SEED)) + target_card.is_destroying = true + end + end + + --Handle Card Destroy + if context.destroying_card and not context.blueprint then + if context.destroying_card.is_destroying then + if card.ability.extra.reduce < 70 then + card.ability.extra.reduce = card.ability.extra.reduce + card.ability.extra.rate + card.ability.extra.reduce = math.min(card.ability.extra.reduce, 70) + + forced_message("Upgraded!", card, G.C.PURPLE, true) + end + + return true --Destroy the card + end + end + end +}) + +--Jackhammer +create_joker({ + name = 'Jackhammer', id = 16, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'j_powerful'}, + + vars = {{retrigger_times = 5}, {is_activate = false}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.retrigger_times}} + end, + + blueprint = true, eternal = true, perishable = true, + + calculate = function(self, card, context) + + --Pre-hand check + if context.before and not context.blueprint then + card.ability.extra.is_activate = false + + local jack_count = 0 + for i=1, #context.scoring_hand do + if context.scoring_hand[i].base.value == 'Jack' then + jack_count = jack_count + 1 + end + end + + if jack_count==1 then + card.ability.extra.is_activate = true + end + + end + + --Main context + if context.cardarea == G.play and context.repetition and not context.repetition_only and card.ability.extra.is_activate then + if context.other_card.base.value == 'Jack' then + card.ability.extra.target_jack = context.other_card + + return { + message = 'Again!', + repetitions = card.ability.extra.retrigger_times, + card = context.blueprint_card or card + } + end + end + + if context.destroying_card and not context.blueprint then + --This context is called on every single card in the scoring hand + --Check if the card called is the same as target card + if context.destroying_card == card.ability.extra.target_jack then + card.ability.extra.target_jack = nil + return true --Destroy the card + end + end + + end +}) + +--Jack of All Trades +create_joker({ + name = 'Jack of All Trades', id = 60, + rarity = 'Uncommon', cost = 6, + + vars = {{chips = 20}, {mult = 3}, {xmult = 1.25}, {money = 1}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.chips, card.ability.extra.mult, card.ability.extra.xmult, card.ability.extra.money}} + end, + + blueprint = true, eternal = true, perishable = true, + + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.base.value == 'Jack' then + ease_dollars(card.ability.extra.money) + return { + chips = card.ability.extra.chips, + mult = card.ability.extra.mult, + x_mult = card.ability.extra.xmult, + card = card + } + end + end + end +}) + +--Magic Trick Card +create_joker({ + name = 'Magic Trick Card', id = 0, ex_art = true, + rarity = 'Uncommon', cost = 4, + + vars = {{side = 0}}, + custom_vars = function(self, info_queue, card) + + local rank1 = localize(card.ability.extra.source_rank, 'ranks') + local suit1 = localize(card.ability.extra.source_suit, 'suits_plural') + + local rank2 = localize(card.ability.extra.target_rank, 'ranks') + local suit2 = localize(card.ability.extra.target_suit, 'suits_plural') + + local colour1 = G.C.SUITS[card.ability.extra.source_suit] + local colour2 = G.C.SUITS[card.ability.extra.target_suit] + + return {vars = {rank1..' of '..suit1, rank2..' of '..suit2, colours = {colour1, colour2}}} + end, + + blueprint = false, eternal = true, perishable = true, + + set_ability = function(self, card, initial, delay_sprites) + if card.ability.extra.side == 0 then + card.ability.extra.source_suit = 'Hearts' + card.ability.extra.source_rank = 'Queen' + + card.ability.extra.target_suit = 'Clubs' + card.ability.extra.target_rank = '7' + else + card.ability.extra.source_suit = 'Clubs' + card.ability.extra.source_rank = '7' + + card.ability.extra.target_suit = 'Hearts' + card.ability.extra.target_rank = 'Queen' + end + card.children.center:set_sprite_pos({x = card.ability.extra.side, y = 0}) + end, + + calculate = function(self, card, context) + + if context.pre_discard and not context.blueprint then + event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + + card:juice_up(1, 1) + + if card.ability.extra.side == 0 then + card.ability.extra.side = 1 + + card.ability.extra.source_suit = 'Clubs' + card.ability.extra.source_rank = '7' + + card.ability.extra.target_suit = 'Hearts' + card.ability.extra.target_rank = 'Queen' + else + card.ability.extra.side = 0 + + card.ability.extra.source_suit = 'Hearts' + card.ability.extra.source_rank = 'Queen' + + card.ability.extra.target_suit = 'Clubs' + card.ability.extra.target_rank = '7' + end + card.children.center:set_sprite_pos({x = card.ability.extra.side, y = 0}) + + return true end}) + + card_eval_status_text( + card, + 'extra', + nil, nil, nil, + {message = 'Flipped!', colour = G.C.ORANGE, instant = true} + ) + end + + if context.after and not context.blueprint then + for i = 1, #context.scoring_hand do + local currentCard = context.scoring_hand[i] + + local is_activate = currentCard:is_suit(card.ability.extra.source_suit) and currentCard.base.value == card.ability.extra.source_rank + + if is_activate then + --Flipping Animation + event({trigger = 'after', delay = 0.1, func = function() big_juice(card); currentCard:flip(); play_sound('card1', 1); currentCard:juice_up(0.3, 0.3); return true end }) + + --Changing Card Property + + event({trigger = 'after', delay = 0.05, func = function() + SMODS.change_base(currentCard, card.ability.extra.target_suit, card.ability.extra.target_rank) + return true end }) + + --Unflipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('tarot2', 1, 0.6); big_juice(card); currentCard:juice_up(0.3, 0.3); return true end }) + forced_message("Changed!", currentCard, G.C.ORANGE, true) + end + end + end + + end +}) + +--Queensland +create_joker({ + name = 'Queensland', id = 61, + rarity = 'Rare', cost = 7, + + gameplay_tag = {'enh_custom'}, + + vars = {{count_max = 5}, {count = 0}}, + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'resource_tooltip'} + return {vars = {card.ability.extra.count_max, card.ability.extra.count_max - card.ability.extra.count}} + end, + + blueprint = true, eternal = true, perishable = true, + + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.base.value == 'Queen' then + if card.ability.extra.count < card.ability.extra.count_max then + if not context.blueprint then + card.ability.extra.count = card.ability.extra.count + 1 + end + + local other_card = context.other_card + + event({func = function() + local rank = pseudorandom_element(SMODS.Ranks, pseudoseed('queensland')..G.SEED).card_key + local suit = SMODS.Suits[other_card.base.suit].card_key + + local _card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS[suit..'_'..rank], G.P_CENTERS.m_unstb_resource, {playing_card = G.playing_card}) + + big_juice(context.blueprint_card or card) + + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.play:emplace(_card) + table.insert(G.playing_cards, _card) + + return true end + }) + + event({func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + draw_card(G.play,G.deck, 90,'up', nil) + return true end + }) + + playing_card_joker_effects({true}) + + end + end + end + + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + if card.ability.extra.count > 0 then + card.ability.extra.count = 0 + return { + message = 'Reset!' + } + end + end + end +}) + +--King of Pop +create_joker({ + name = 'King of Pop', id = 62, + rarity = 'Rare', cost = 8, + + gameplay_tag = {'j_powerful'}, + + vars = {{odds_destroy = 4}}, + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = G.P_TAGS.tag_double + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_destroy}} + end, + + blueprint = false, eternal = true, perishable = true, + + calculate = function(self, card, context) + --Pre-hand check + if context.before and not context.blueprint then + for i=1, #context.scoring_hand do + if context.scoring_hand[i].base.value == 'King' and context.scoring_hand[i].config.center ~= G.P_CENTERS.c_base and not context.scoring_hand[i].config.center.disenhancement then + if pseudorandom('kingofpop'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_destroy then + context.scoring_hand[i].to_destroy = true + end + end + end + end + + if context.destroying_card and not context.blueprint then + if context.destroying_card.to_destroy then + event({ trigger = 'after', delay = 0.5, func = function() + + add_tag(Tag('tag_double')) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + + return true end + } + ) + forced_message("Tag!", card, G.C.SECONDARY_SET.Enhanced) + + return true --Destroy the card + end + end + end +}) + +--Polychrome Red Seal Steel Joker +create_joker({ + name = 'prssj', id = 63, + rarity = 'Rare', cost = 10, + + gameplay_tag = {'j_shitpost', 'j_powerful', 'edition_upgrade'}, + + vars = {{odds_upgrade = 8}, {odds_retrigger = 4}, {odds_hand = 2}, {hand_xmult = 2}}, + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = {set = 'Other', key = 'upgrade_edition'} + + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds_upgrade, card.ability.extra.odds_retrigger, card.ability.extra.odds_hand, card.ability.extra.hand_xmult}} + end, + + blueprint = true, eternal = true, perishable = true, + + calculate = function(self, card, context) + + --Upgrade Part + if context.before then + + local upgrade_count = 0 + for i = 1, #context.scoring_hand do + local current_card = context.scoring_hand[i] + + if not current_card.debuff and current_card.base.value == 'King' and pseudorandom('prssj1'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_upgrade then + upgrade_count = upgrade_count+1 + edition_upgrade(current_card) + end + end + + if upgrade_count > 0 then + --forced_message("Upgrade!", context.blueprint_card or card, G.C.SECONDARY_SET.Enhanced, true) + return { + message = 'Upgrade!', + colour = G.C.SECONDARY_SET.Enhanced, + card = context.blueprint_card or card, + } + end + + end + + if context.cardarea == G.play and context.repetition and not context.repetition_only and not context.other_card.debuff and context.other_card.base.value == 'King' then + if pseudorandom('prssj2'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_retrigger then + return { + message = 'Again!', + repetitions = 1, + card = context.blueprint_card or card + } + end + end + + if context.individual and context.cardarea == G.hand and not context.end_of_round and context.other_card.base.value == 'King' then + if pseudorandom('prssj3'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_hand then + if context.other_card.debuff then + return { + message = localize('k_debuffed'), + colour = G.C.RED, + card = context.blueprint_card or card, + } + else + return { + x_mult = card.ability.extra.hand_xmult, + card = context.blueprint_card or card + } + end + end + end + end +}) + +--Master of One +create_joker({ + name = 'Master of One', id = 64, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'rank_binary', 'j_alter'}, + + --vars = {}, + custom_vars = function(self, info_queue, card) + return {vars = {}} + end, + + blueprint = true, eternal = true, perishable = true, + + add_to_deck = function(self, card, from_debuff) + --Enable rank 1 card in pools + if not from_debuff then + setPoolRankFlagEnable('unstb_1', true); + end + end, + + calculate = function(self, card, context) + + if context.setting_blind then + --Adds a card + event({trigger = 'after', func = function() + local rank = 'unstb_1' + local suit = pseudorandom_element(SMODS.Suits, pseudoseed('masterofone')..G.SEED).card_key + + --Pooling Enhancements + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if not v.replace_base_card and not v.disenhancement then + cen_pool[#cen_pool+1] = v + end + end + + local _card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, G.P_CARDS[suit..'_'..rank], pseudorandom_element(cen_pool, pseudoseed('masterofone')..G.SEED), {playing_card = G.playing_card}) + + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.play:emplace(_card) + table.insert(G.playing_cards, _card) + + big_juice(context.blueprint_card or card) + card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = 'One!', colour = G.C.SECONDARY_SET.Enhanced}) + + return true end + }) + + event({trigger = 'after', func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + draw_card(G.play,G.deck, 90,'up', nil) + return true end + }) + + playing_card_joker_effects({true}) + end + + end +}) + +--Spectre +create_joker({ + name = 'Spectre', id = 9, + rarity = 'Uncommon', cost = 7, + + vars = {{xmult_rate = 0.25}, {xmult = 1}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.xmult_rate, card.ability.extra.xmult}} + end, + + blueprint = true, eternal = true, perishable = true, + + set_ability = function(self, card, initial, delay_sprites) + if G.GAME and G.GAME.consumeable_usage_total then + card.ability.extra.xmult = 1 + G.GAME.consumeable_usage_total.spectral * card.ability.extra.xmult_rate + else + card.ability.extra.xmult = 1 + end + end, + + calculate = function(self, card, context) + + --Upgrades + if context.using_consumeable and not context.blueprint then + if context.consumeable.ability.set == "Spectral" then + card.ability.extra.xmult = card.ability.extra.xmult + card.ability.extra.xmult_rate + event({ + func = function() + big_juice(card) + forced_message("Upgraded!", card, G.C.MULT, true) + --card_eval_status_text(card,'extra',nil, nil, nil,{message = "Upgraded", colour = G.C.MULT, instant = true}) + return true end}) + end + end + + --Main context + if context.joker_main then + return { + Xmult_mod = card.ability.extra.xmult, + message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.xmult } } + } + end + + end +}) + +--Library Card +create_joker({ + name = 'Library Card', id = 52, + rarity = 'Uncommon', cost = 8, + + vars = {{chips_rate = 5}, {mult_rate = 2}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.chips_rate, card.ability.extra.mult_rate}} + end, + + blueprint = true, eternal = true, perishable = true, + + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if not context.other_card.config.center.no_suit and unstb_global.name_suit[context.other_card.base.suit] then + local suit_info = unstb_global.name_suit[context.other_card.base.suit] + + local totalChips = card.ability.extra.chips_rate * suit_info.count_consonant + local totalMult = card.ability.extra.mult_rate * suit_info.count_vowel + + return { + chips = totalChips, + mult = totalMult, + card = card + } + end + end + end +}) + +--Collector's Album +create_joker({ + name = 'Collector Album', id = 53, + rarity = 'Uncommon', cost = 8, + + vars = {{chips_rate = 120}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.chips_rate, card.ability.extra.mult_rate}} + end, + + blueprint = true, eternal = true, perishable = true, + + + calculate = function(self, card, context) + + if context.other_joker then + --trigger on only joker with "Card" in the name + if unstb_global.name_card[context.other_joker.config.center.key] then + event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end + }) + return { + message = localize{type='variable',key='a_chips',vars={card.ability.extra.chips_rate}}, + chip_mod = card.ability.extra.chips_rate + } + end + end + end +}) + +local function get_random_hand(current_hand, seed) + local _poker_hands = {} + + --Populate list + for k, v in pairs(G.GAME.hands) do + if v.visible then _poker_hands[#_poker_hands+1] = k end + end + + local new_hand = nil + while not new_hand do + new_hand = pseudorandom_element(_poker_hands, pseudoseed(seed or 'rand_hand')) + if new_hand == current_hand then new_hand = nil end + end + + return new_hand +end + +--Throwing Hands +create_joker({ + name = 'Throwing Hands', id = 8, + rarity = 'Common', cost = 5, + + vars = {{target_hand = 'High Card'}, {odds_base = 2}, {odds_destroy = 3}, {xmult = 4}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.xmult, card.ability.extra.odds_base * (G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds_destroy, localize(card.ability.extra.target_hand, 'poker_hands')}} + end, + + blueprint = true, eternal = true, perishable = true, + + set_ability = function(self,card, initial, delay_sprites) + + --Random Poker Hand + card.ability.extra.target_hand = get_random_hand(card.ability.extra.target_hand, 'throwinghands'..G.SEED) + end, + + calculate = function(self, card, context) + + if context.joker_main and context.scoring_name ~= nil and context.scoring_hand then + local is_destroyed = false; + + if context.scoring_name ~= card.ability.extra.target_hand then + if pseudorandom('throwinghands'..G.SEED) < G.GAME.probabilities.normal * card.ability.extra.odds_base / card.ability.extra.odds_destroy then + is_destroyed = true + end + end + + if not is_destroyed then + return { + xmult = card.ability.extra.xmult, + } + else + if not context.blueprint then + event({func = function() + + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + + --Destroy Card + event({trigger = 'after', delay = 0.3, func = function() + + G.jokers:remove_card(card) + card:remove() + card = nil + return true end }) + + return true end }) + return { + message = 'Bye', + } + end + end + + + end + + if context.end_of_round and not (context.individual or context.repetition or context.blueprint) then + card.ability.extra.target_hand = get_random_hand(card.ability.extra.target_hand, 'throwinghands'..G.SEED) + + return { + message = card.ability.extra.target_hand + } + end + end +}) + +--Imperial Bower +create_joker({ + name = 'Imperial Bower', id = 58, + rarity = 'Common', cost = 7, + + blueprint = true, eternal = true, perishable = true, + + vars = { {xmult = 3}}, + + custom_vars = function(self, info_queue, card) + + return { vars = {card.ability.extra.xmult}} + end, + + calculate = function(self, card, context) + + if context.joker_main and next(context.poker_hands['Straight']) then + + local face_count = 0 + + for i=1, #context.scoring_hand do + if context.scoring_hand[i]:is_face() then + face_count = face_count+1 + end + end + + if face_count > 0 then + return { + Xmult_mod = card.ability.extra.xmult, + message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.xmult } } + } + end + + end + + end, +}) + +--The Jolly Joker +create_joker({ + name = 'The Jolly Joker', id = 59, + rarity = 'Uncommon', cost = 6, + + blueprint = true, eternal = true, perishable = false, + + vars = { {mult_rate = 8}, {mult = 0}}, + + custom_vars = function(self, info_queue, card) + + return { vars = {card.ability.extra.mult_rate, card.ability.extra.mult}} + end, + + calculate = function(self, card, context) + + if context.before and context.scoring_hand and not context.blueprint then + if next(context.poker_hands['Pair']) then + + card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_rate + + return { + message = 'Upgraded!', + colour = G.C.MULT, + card = card + } + + elseif card.ability.extra.mult > 0 then + card.ability.extra.mult = 0 + + event({ trigger = 'after', delay = 0.2, func = function() + play_sound('tarot1') + return true end }) + + return { + message = 'Reset', + card = card + } + end + end + + if context.joker_main then + if card.ability.extra.mult > 0 then + return { + mult_mod = card.ability.extra.mult, + message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult } } + } + end + + end + + end, +}) + +--Get Out of Jail Free Card +create_joker({ + name = 'Get Out of Jail Free Card', id = 48, + rarity = 'Rare', cost = 10, + + blueprint = false, eternal = false, perishable = true, + + --vars = { {times = 1}}, + + custom_vars = function(self, info_queue, card) + + local activate_text = 'Inactive' + local activate_color = G.C.RED + if G.STATE == G.STATES.SELECTING_HAND then + activate_text = 'Active' + activate_color = G.C.GREEN + end + + return {vars = {activate_text, + colours = {activate_color} }} + end, + + --Set sprites and hitbox + + set_sprites = function(self, card, front) + local w_scale, h_scale = 64/71, 93/95 + card.children.center.scale.y = card.children.center.scale.y * h_scale + card.children.center.scale.x = card.children.center.scale.x * w_scale + end, + + set_ability = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 64/71, 93/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + load = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 64/71, 93/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + add_to_deck = function(self, card, from_debuff) + --Set the flag to true immediately once this joker has been picked up, it can't spawn again for the rest of the session + G.GAME.pool_flags.jailfree_get = true + end, + + calculate = function(self, card, context) + --Referenced from DebugPlus's "Win Blind" function + if context.selling_self and not context.blueprint then + if G.STATE ~= G.STATES.SELECTING_HAND then + return + end + G.GAME.chips = G.GAME.blind.chips + G.STATE = G.STATES.HAND_PLAYED + G.STATE_COMPLETE = true + end_round() + end + end, + + custom_in_pool = function(self, args) + + --Spawns if this card has not been picked before + return not G.GAME.pool_flags.jailfree_get + + end + +}) + +--Tanzaku +create_joker({ + name = 'Tanzaku', id = 70, + rarity = 'Rare', cost = 8, + + gameplay_tag = {'j_powerful'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{ repetitions = 0 }, { repetition_rate = 1 } }, + + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.repetitions, card.ability.extra.repetition_rate}} + end, + + --Set sprites and hitbox + + set_sprites = function(self, card, front) + local w_scale, h_scale = 50/71, 80/95 + card.children.center.scale.y = card.children.center.scale.y * h_scale + card.children.center.scale.x = card.children.center.scale.x * w_scale + end, + + set_ability = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 50/71, 80/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + load = function(self, card, initial, delay_sprites) + local w_scale, h_scale = 50/71, 80/95 + card.T.h = card.T.h * h_scale + card.T.w = card.T.w * w_scale + end, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.repetition and not context.repetition_only then + if context.other_card.seal then + return { + message = 'Again!', + repetitions = card.ability.extra.repetitions, + card = context.blueprint_card or card + } + end + end + + --The scaling part is not copyable by Blueprint + if context.discard and not context.blueprint then + + local currentCard = context.other_card + + --Not debuffed, and has seal + if not currentCard.debuff and currentCard.seal then + card.ability.extra.repetitions = card.ability.extra.repetitions + card.ability.extra.repetition_rate + + --forced_message('+'..card.ability.extra.repetition_rate, card, G.C.ORANGE, true) + return { + message = '+'..card.ability.extra.repetition_rate, + card = card + } + end + + end + + --End of round check, make sure it's checked absolutely once per round + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + if card.ability.extra.repetitions > 0 then + card.ability.extra.repetitions = 0 + return { + message = 'Reset!' + } + end + end + end +}) + +--Glass Cannon +create_joker({ + name = 'Glass Cannon', id = 68, + rarity = 'Rare', cost = 8, + + gameplay_tag = {'j_powerful'}, + + blueprint = true, eternal = true, perishable = true, + + vars = {{ repetitions = 1 }}, + + custom_vars = function(self, info_queue, card) + info_queue[#info_queue+1] = G.P_CENTERS.m_glass + return {vars = {card.ability.extra.repetitions}} + end, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.repetition and not context.repetition_only then + if context.other_card.config.center == G.P_CENTERS.m_glass then + return { + message = 'Again!', + repetitions = card.ability.extra.repetitions, + card = context.blueprint_card or card + } + end + end + + if context.destroying_card and not context.blueprint then + if context.destroying_card.config.center == G.P_CENTERS.m_glass then + return true --Destroy the card + end + end + end +}) + +--Pity Rate Drop +create_joker({ + name = 'Pity Rate Drop', id = 71, + rarity = 'Rare', cost = 8, + + blueprint = true, eternal = true, perishable = true, + + vars = {{ odds = 8 }, {odds_rate = 1}, {odds_current = 1}}, + + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.odds_current * (G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds, card.ability.extra.odds_rate * (G.GAME and G.GAME.probabilities.normal or 1), G.GAME and G.GAME.probabilities.normal or 1}} + end, + + calculate = function(self, card, context) + + if context.setting_blind and #G.jokers.cards + G.GAME.joker_buffer < G.jokers.config.card_limit then + if pseudorandom('pity_rate_drop'..G.SEED) < G.GAME.probabilities.normal * card.ability.extra.odds_current / card.ability.extra.odds then + + if context.blueprint then + card.ability.extra.blueprint_created = true + end + card.ability.extra.odds_current = 1 + + local jokers_to_create = math.min(1, G.jokers.config.card_limit - (#G.jokers.cards + G.GAME.joker_buffer)) + G.GAME.joker_buffer = G.GAME.joker_buffer + jokers_to_create + event({ + func = function() + for i = 1, jokers_to_create do + local card = create_card('Joker', G.jokers, nil, 1, nil, nil, nil, 'pity_rate_drop') + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + end + G.GAME.joker_buffer = 0 + return true + end}) + card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = 'Create!', colour = G.C.RED}) + else + --Odds increments part are not copyable by Blueprint + if not context.blueprint then + --Increase odds, only if a card has not been created by blueprint prior + + if not card.ability.extra.blueprint_created then + card.ability.extra.odds_current = card.ability.extra.odds_current + card.ability.extra.odds_rate + else + card.ability.extra.blueprint_created = nil + end + + play_nope_sound() + card_eval_status_text(card, 'extra', nil, nil, nil, {message = 'Nope', colour = G.C.RED}) + end + end + end + end +}) + +--Salmon Run +create_joker({ + name = 'Salmon Run', id = 10, + rarity = 'Uncommon', cost = 7, + + vars = {{odds = 7}}, + custom_vars = function(self, info_queue, card) + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds}} + end, + + blueprint = true, eternal = true, perishable = true, + + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + local currentCard = context.other_card + + if not currentCard.config.center.no_rank and currentCard.base.value == '7' then + local isActivated = pseudorandom('salmonrun'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds + + if isActivated then + event({func = function() + --Copy Card + local _card = copy_card(currentCard, nil, nil, G.playing_card) + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.play:emplace(_card) + table.insert(G.playing_cards, _card) + + return true end + }) + + event({func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + draw_card(G.play,G.deck, 90,'up', nil) + return true end + }) + + playing_card_joker_effects({true}) + end + end + end + end +}) + +--Cool S +create_joker({ + name = 'Cool S', id = 66, + rarity = 'Common', cost = 8, + + vars = {}, + custom_vars = function(self, info_queue, card) + return {vars = {}} + end, + + blueprint = false, eternal = true, perishable = true, + + calculate = function(self, card, context) + if context.after and not context.blueprint then + for i = 1, #context.scoring_hand do + local currentCard = context.scoring_hand[i] + + if currentCard.base.value == '8' then + --Pooling Enhancements + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if not v.replace_base_card and not v.disenhancement then + cen_pool[#cen_pool+1] = v + end + end + + --Flipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('card1', 1); currentCard:juice_up(0.3, 0.3); return true end }) + + --Changing Card Property + + event({trigger = 'after', delay = 0.05, func = function() + + currentCard:set_ability(pseudorandom_element(cen_pool, pseudoseed('cool_s'..G.SEED))) + + return true end }) + + --Unflipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('tarot2', 1, 0.6); big_juice(card); currentCard:juice_up(0.3, 0.3); return true end }) + + delay(0.1) + end + end + end + end +}) + +--Memoriam Photo +create_joker({ + name = 'Memoriam Photo', id = 72, + rarity = 'Uncommon', cost = 6, + + vars = {{chips = 0}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.chips}} + end, + + blueprint = true, eternal = true, perishable = false, + + calculate = function(self, card, context) + --Upgrades + if context.remove_playing_cards and not context.blueprint then + local added_total = 0 + for i=1, #context.removed do + if not context.removed[i].config.center.no_rank then + added_total = added_total + context.removed[i].base.nominal + end + end + + if added_total>0 then + card.ability.extra.chips = card.ability.extra.chips + (2 * added_total) + event({ + func = function() + big_juice(card) + forced_message("Upgraded!", card, G.C.CHIPS, true) + --card_eval_status_text(card,'extra',nil, nil, nil,{message = "Upgraded", colour = G.C.MULT, instant = true}) + return true end + }) + end + end + + --Main context + if context.joker_main then + if card.ability.extra.chips > 0 then + return { + chip_mod = card.ability.extra.chips, + message = localize { type = 'variable', key = 'a_chips', vars = { card.ability.extra.chips } } + } + end + end + end, +}) + +--Schrodinger Cat +create_joker({ + name = 'Schrodinger Cat', id = 73, + rarity = 'Uncommon', cost = 8, + + vars = {{odds = 3}}, + custom_vars = function(self, info_queue, card) + return {vars = {G.GAME and G.GAME.probabilities.normal or 1, card.ability.extra.odds}} + end, + + blueprint = true, eternal = true, perishable = true, + + calculate = function(self, card, context) + --Upgrades + if context.remove_playing_cards then + for i=1, #context.removed do + local isActivated = pseudorandom('schrodingercat'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds + + if isActivated then + event({func = function() + --Copy Card + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local _card = copy_card(context.removed[i], nil, nil, G.playing_card) + + --Special interaction: if it is a vintage card, resets the odds + if _card.config.center.key == 'm_unstb_vintage' then + _card.ability.extra.current_odd = 0 + end + + _card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + _card:add_to_deck() + G.deck:emplace(_card) + table.insert(G.playing_cards, _card) + return true end + }) + + event({func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + return true end + }) + + playing_card_joker_effects({true}) + + forced_message(localize('k_copied_ex'), context.blueprint_card or card, G.C.ORANGE, 0.25) + end + end + end + end, +}) + +--Cashback Card +create_joker({ + name = 'Cashback Card', id = 51, + rarity = 'Rare', cost = 9, + + vars = {{payout = 0}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.payout}} + end, + + blueprint = false, eternal = true, perishable = true, + + calculate = function(self, card, context) + if context.using_consumeable and not context.blueprint then + card.ability.extra.payout = card.ability.extra.payout + 1 + event({ + func = function() + big_juice(card) + forced_message("Upgrade!", card, G.C.GOLD, true) + --card_eval_status_text(card,'extra',nil, nil, nil,{message = "Upgraded", colour = G.C.MULT, instant = true}) + return true end}) + end + end, + calc_dollar_bonus = function(self, card) + if G.GAME.blind.boss then + local bonus = card.ability.extra.payout + + card.ability.extra.payout = 0 + + if bonus > 0 then return bonus end + end + end +}) + +--Raffle +create_joker({ + name = 'Raffle', id = 50, + rarity = 'Common', cost = 8, + + vars = {{odds = 20}, {prize = 20}, {current_odds = 0}}, + custom_vars = function(self, info_queue, card) + return {vars = {(G.GAME and G.GAME.probabilities.normal or 1)*card.ability.extra.current_odds, card.ability.extra.odds, card.ability.extra.prize, G.GAME and G.GAME.probabilities.normal or 1, }} + end, + + blueprint = false, eternal = true, perishable = true, + + calculate = function(self, card, context) + --All upgraded part + if context.buying_card and not context.blueprint then + card.ability.extra.current_odds = card.ability.extra.current_odds + 1 + forced_message('Upgrade!', card, G.C.ORANGE, true) + end + + if context.reroll_shop and not context.blueprint then + card.ability.extra.current_odds = card.ability.extra.current_odds + 1 + forced_message('Upgrade!', card, G.C.ORANGE, true) + end + + if context.open_booster and not context.blueprint then + card.ability.extra.current_odds = card.ability.extra.current_odds + 1 + forced_message('Upgrade!', card, G.C.ORANGE, true) + end + + --Payout + if context.ending_shop and not context.blueprint then + if pseudorandom('raffle'..G.SEED) < G.GAME.probabilities.normal * card.ability.extra.current_odds / card.ability.extra.odds then + event({trigger = 'after', func = function() + ease_dollars(card.ability.extra.prize, true) + big_juice(context.blueprint_card or card) + return true end }) + forced_message('$'..card.ability.extra.prize, card, G.C.GOLD, true) + else + play_nope_sound() + forced_message('Nope', card, G.C.RED, true) + end + + card.ability.extra.current_odds = 0 + end + + --Make sure the chance resets again when round ends, cause booster opening from tags currently still trigger it and idk how to prevent that + if context.end_of_round and not context.other_card and not context.repetition and not context.game_over and not context.blueprint then + card.ability.extra.current_odds = 0 + end + end +}) + +--ease_dollars hook, for IC Card +local ref_ease_dollars = ease_dollars +function ease_dollars(mod, instant) + + if to_big(mod) < to_big(0) then + + local iccard_list = SMODS.find_card('j_unstb_ic_card') + + if next(iccard_list) then + for i=1, #iccard_list do + if iccard_list[i].ability.extra.balance > 0 then + + --Note: Spend amount is negative + local spendamount = math.max(mod, -iccard_list[i].ability.extra.balance) + + mod = mod - spendamount + iccard_list[i].ability.extra.balance = iccard_list[i].ability.extra.balance + spendamount + G.GAME.virtual_dollars = G.GAME.virtual_dollars + spendamount + + if not instant then + forced_message("-$"..math.abs(spendamount), iccard_list[i], G.C.red, 0.5) + end + + if mod >= 0 then + return + end + end + end + end + end + + ref_ease_dollars(mod, instant) +end + +--IC Card +create_joker({ + name = 'IC Card', id = 49, + rarity = 'Uncommon', cost = 6, + + vars = {{money_rate = 3},{balance = 0}, {round_max = 9}}, + custom_vars = function(self, info_queue, card) + return {vars = {card.ability.extra.money_rate, card.ability.extra.round_max, card.ability.extra.balance, card.ability.extra.round_left}} + end, + + blueprint = false, eternal = false, perishable = false, + + set_ability = function(self, card, initial, delay_sprites) + card.ability.extra.round_left = card.ability.extra.round_max + + if G.STATE ~= G.STATES.SHOP then --Joker obtained not during the shop + card.ability.extra.start_counting = true + end + end, + + add_to_deck = function(self, card, from_debuff) + G.GAME.virtual_dollars = (G.GAME.virtual_dollars or 0) + card.ability.extra.balance + end, + + remove_from_deck = function(self, card, from_debuff) + G.GAME.virtual_dollars = (G.GAME.virtual_dollars or 0) - card.ability.extra.balance + end, + + calculate = function(self, card, context) + + --When entering the round, start counting the round remaining + --(If the Joker is bought in the shop, the first shop leave will not be counted down) + if context.first_hand_drawn then + card.ability.extra.start_counting = true + end + + if context.before and context.scoring_hand and not context.blueprint then + card.ability.extra.balance = card.ability.extra.balance + card.ability.extra.money_rate + + --Update the game's global virtual money at the same time + G.GAME.virtual_dollars = (G.GAME.virtual_dollars or 0) + card.ability.extra.money_rate + + forced_message("$"..card.ability.extra.money_rate, card, G.C.GOLD, true) + end + + if context.ending_shop and not context.blueprint then + + if card.ability.extra.start_counting then + card.ability.extra.round_left = card.ability.extra.round_left - 1 + + if card.ability.extra.round_left <= 0 then + event({func = function() + + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + + --Destroy Card + event({trigger = 'after', delay = 0.3, func = function() + + G.jokers:remove_card(card) + card:remove() + card = nil + return true end }) + + return true end }) + card_eval_status_text(card,'extra',nil, nil, nil,{message = 'Expired', colour = G.C.ORANGE, instant = true}) + else + forced_message(card.ability.extra.round_left..' Round', card, G.C.ORANGE, true) + end + + end + + card.ability.extra.start_counting = false + end + end +}) + + + +--local rank_2048 = { unstb_0 = true, unstb_1 = true, ['2'] = true, ['4'] = true, ['8'] = true} + +--2048 +create_joker({ + name = 'j2048', id = 3, + rarity = 'Uncommon', cost = 7, + + gameplay_tag = {'j_alter'}, + + vars = {{card_to_destroy = {}}}, + + custom_vars = function(self, info_queue, card) + + --[[local key = self.key + if getPoolRankFlagEnable('unstb_0') or getPoolRankFlagEnable('unstb_1') then + key = self.key..'_ex' + end]] + + return { vars = {} } + end, + + blueprint = false, eternal = true, perishable = true, + + calculate = function(self, card, context) + if context.before and context.scoring_hand and not context.blueprint then + local card_list = {} + local card_to_destroy = {} + + for i = 1, #context.scoring_hand do + local c = context.scoring_hand[i] + local key = c.base.value + + --print(rank_2048[c.base.value]) + + + if card_list[key] then + --print('found the card list: '..c.base.value) + + local prev_card = card_list[key] + card_to_destroy[#card_to_destroy+1] = prev_card + + --Transfer chips + local bonusChip = prev_card.ability.perma_bonus or 0 + local baseChip = SMODS.Ranks[prev_card.base.value].nominal + + --Delay the chip adding, so it does not get evaluated + event({delay = 0.1, func = function() + c.ability.perma_bonus = (c.ability.perma_bonus or 0) + baseChip + bonusChip + return true end + }) + + --Erase the slot, if there's the next one then it counts as a new pair + card_list[key] = nil + else + --print('adding to card list: '..c.base.value) + card_list[key] = c + end + + end + + --print('done') + --print(inspectDepth(card_to_destroy, nil, 1)) + + card.ability.extra.card_to_destroy = card_to_destroy + end + + if context.destroying_card and not context.blueprint then + --print('check card to destroy main') + for i = 1, #card.ability.extra.card_to_destroy do + --print('check card to destroy') + --print(context.destroying_card == card.ability.extra.card_to_destroy[i]) + if context.destroying_card == card.ability.extra.card_to_destroy[i] then + return true + end + end + end + end +}) + +--Inductor +create_joker({ + name = 'Inductor', id = 1, + rarity = 'Rare', cost = 8, + + gameplay_tag = {'j_powerful'}, + + vars = {{odds_en = 2}, {odds_ed = 4}, {odds_s = 8}}, + + custom_vars = function(self, info_queue, card) + local vars + if G.GAME and G.GAME.probabilities.normal then + vars = {G.GAME.probabilities.normal, card.ability.extra.odds_en, card.ability.extra.odds_ed, card.ability.extra.odds_s} + else + vars = {1, card.ability.extra.odds_en, card.ability.extra.odds_ed, card.ability.extra.odds_s} + end + return {vars = vars} + end, + + blueprint = false, eternal = true, perishable = true, + + calculate = function(self, card, context) + + if context.after and context.scoring_hand and #context.scoring_hand > 1 and not context.blueprint then + local sourceCard = {} + + for i = 1, #context.scoring_hand do + if not (context.scoring_hand[i].config.center == G.P_CENTERS.m_stone or context.scoring_hand[i].config.center.replace_base_card) then --Check if it is not a Stone card or have any weird enhancement + if sourceCard[context.scoring_hand[i].base.value..context.scoring_hand[i].base.suit] then --targetCard exists + + local currentCard = context.scoring_hand[i] + local targetCard = sourceCard[context.scoring_hand[i].base.value..context.scoring_hand[i].base.suit] + + + local isCopyEnhancement = pseudorandom('prop_enh'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_en + local isCopyEdition = pseudorandom('prop_ed'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_ed + local isCopySeal = pseudorandom('prop_s'..G.SEED) < G.GAME.probabilities.normal / card.ability.extra.odds_s + + --Extra check, if the current card and target card have the same status, don't play animation + + if currentCard.config.center == targetCard.config.center then + isCopyEnhancement = false + + end + if (currentCard.edition or {}).key == (targetCard.edition or {}).key then + isCopyEdition = false + + end + if currentCard.seal == targetCard.seal then + isCopySeal = false + end + + local isPlayingAnimation = isCopyEnhancement or isCopyEdition or isCopySeal + + if isPlayingAnimation then + --Flipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('card1', 1); currentCard:juice_up(0.3, 0.3); return true end }) + + --Changing Card Property + event({trigger = 'after', delay = 0.05, func = function() + + --Copy enhancement + if isCopyEnhancement then + currentCard:set_ability(targetCard.config.center) + end + + --Copy edition + if isCopyEdition then + currentCard:set_edition(targetCard.edition, true, true) + end + + --Copy seal + if isCopySeal then + currentCard:set_seal(targetCard.seal, true, true) + end + + return true end }) + + --Unflipping Animation + event({trigger = 'after', delay = 0.1, func = function() currentCard:flip(); play_sound('tarot2', 1, 0.6); big_juice(card); currentCard:juice_up(0.3, 0.3); return true end }) + forced_message("Copied!", currentCard, G.C.RED, true) + end + else --set the target card to the following + sourceCard[context.scoring_hand[i].base.value..context.scoring_hand[i].base.suit] = context.scoring_hand[i] + end + end + end + + end + + end +}) + +--Decks + +if unstb_config.gameplay.c_aux then + +SMODS.Back{ -- Utility Deck + key = "utility", + pos = {x = 0, y = 0}, + + unlocked = true, + + config = {vouchers = {'v_unstb_aux1'}, aux_amount = 1, aux_chance = 2}, + loc_vars = function(self) + return {vars = {}} + end, + + apply = function(self) + G.GAME.auxiliary_rate = (G.GAME.auxiliary_rate or 0) + self.config.aux_chance + event({ + func = function() + for i = 1, self.config.aux_amount do + local card = create_card('Auxiliary', G.consumeables, nil, nil, nil, nil, 'c_unstb_aux_random', 'utildeck') + card:add_to_deck() + G.consumeables:emplace(card) + end + return true + end + }) + end, + + atlas = 'unstb_deck' +} + +end + +if check_enable_taglist({'rank_binary', 'rank_decimal'}) then + +unstb.lowkey_rankList = {'2', '3', '4', '5', 'unstb_0', 'unstb_0.5', 'unstb_1', 'unstb_r2', 'unstb_e', 'unstb_Pi'} + +unstb.lowkey_blacklisted = {} +unstb.lowkey_ranklist = {} + +--Populate the Lowkey Deck rank blacklist, called at the Splash Screen to support modded ranks as well +function init_lowkey_blacklist() + for k, v in pairs(SMODS.Ranks) do + if not table_has_value(unstb.lowkey_rankList, k) then + unstb.lowkey_blacklisted[k] = true + end + end +end + +SMODS.Back{ -- Lowkey Deck + key = "lowkey", + pos = {x = 1, y = 0}, + + unlocked = true, + + config = {}, + loc_vars = function(self) + return {vars = {}} + end, + + apply = function(self) + + --Enable all the pool flags + setPoolRankFlagEnable('unstb_0', true); + setPoolRankFlagEnable('unstb_0.5', true); + setPoolRankFlagEnable('unstb_1', true); + setPoolRankFlagEnable('unstb_r2', true); + setPoolRankFlagEnable('unstb_e', true); + setPoolRankFlagEnable('unstb_Pi', true); + + --Notice: used card_key version and not standard key + local added_rank = {'unstb_0', 'unstb_0.5', 'unstb_1', 'unstb_R', 'unstb_E', 'unstb_P'} + + local all_suit = {} + + for k, v in pairs(SMODS.Suits) do + --If has in_pool, check in_pool + if type(v) == 'table' and type(v.in_pool) == 'function' and v.in_pool then + if v:in_pool({initial_deck = true}) then + all_suit[#all_suit+1] = v.card_key + end + else --Otherwise, added by default + all_suit[#all_suit+1] = v.card_key + end + + end + + --print(inspect(all_suit)) + + local extra_cards = {} + + for i=1, #all_suit do + for j=1, #added_rank do + extra_cards[#extra_cards+1] = {s = all_suit[i], r = added_rank[j]} + end + end + + G.GAME.starting_params.extra_cards = extra_cards + + G.GAME.starting_params.blacklisted_ranks = unstb.lowkey_blacklisted + + end, + + atlas = 'unstb_deck' +} +end + + +--Compatibility / Tweaks / Rework Stuff + +--CardSleeves Support +if CardSleeves then + +--Sleeves +SMODS.Atlas { + -- Key for code to find it with + key = "sleeve", + path = "sleeve.png", + px = 73, + py = 95 +} + +if unstb_config.gameplay.c_aux then + +--Utility Sleeve +CardSleeves.Sleeve({ + name = "Utility Sleeve", + key="utility", + atlas="sleeve", + pos = { x = 0, y = 0 }, + unlocked = true, + loc_vars = function(self) + local key + + if self.get_current_deck_key() ~= "b_unstb_utility" then + key = self.key + self.config = { voucher = 'v_unstb_aux1', aux_chance = 1} + else + key = self.key .. "_alt" + self.config = { voucher = 'v_unstb_aux2', aux_chance = 1} + end + return { key = key } + end, + apply = function(self) + --Call main apply function from CardSleeve first to apply vouchers + CardSleeves.Sleeve.apply(self) + + G.GAME.auxiliary_rate = (G.GAME.auxiliary_rate or 0) + self.config.aux_chance + + event({ + func = function() + + if self.get_current_deck_key() ~= "b_unstb_utility" then + local card = create_card('Auxiliary', G.consumeables, nil, nil, nil, nil, 'c_unstb_aux_random', 'utildeck') + card:add_to_deck() + G.consumeables:emplace(card) + else + local card = create_card('Joker', G.jokers, nil, nil, nil, nil, 'j_unstb_free_trial', nil) + card:set_edition({negative = true}, true) + card:add_to_deck() + G.jokers:emplace(card) + end + + return true + end + }) + end +}) + +end + +if check_enable_taglist({'rank_binary', 'rank_decimal'}) then + +--Lowkey Sleeve +CardSleeves.Sleeve({ + name = "Lowkey Sleeve", + key="lowkey", + atlas="sleeve", + pos = { x = 1, y = 0 }, + unlocked = true, + loc_vars = function(self) + local key + + if self.get_current_deck_key() ~= "b_unstb_lowkey" then + key = self.key + self.config = { remove_ranks = true } + else + key = self.key .. "_alt" + self.config = { prevent_ranks = true } + end + return { key = key } + end, + apply = function(self) + --Call main apply function from CardSleeve first to apply vouchers + CardSleeves.Sleeve.apply(self) + + --Regular sleeve effects + + if self.config.remove_ranks then + --Enable all the pool flags + setPoolRankFlagEnable('unstb_0', true); + setPoolRankFlagEnable('unstb_0.5', true); + setPoolRankFlagEnable('unstb_1', true); + setPoolRankFlagEnable('unstb_r2', true); + setPoolRankFlagEnable('unstb_e', true); + setPoolRankFlagEnable('unstb_Pi', true); + + --Notice: used card_key version and not standard key + local added_rank = {'unstb_0', 'unstb_0.5', 'unstb_1', 'unstb_R', 'unstb_E', 'unstb_P'} + + local all_suit = {} + + for k, v in pairs(SMODS.Suits) do + --If has in_pool, check in_pool + if type(v) == 'table' and type(v.in_pool) == 'function' and v.in_pool then + if v:in_pool({initial_deck = true}) then + all_suit[#all_suit+1] = v.card_key + end + else --Otherwise, added by default + all_suit[#all_suit+1] = v.card_key + end + + end + + --print(inspect(all_suit)) + + local extra_cards = {} + + for i=1, #all_suit do + for j=1, #added_rank do + extra_cards[#extra_cards+1] = {s = all_suit[i], r = added_rank[j]} + end + end + + G.GAME.starting_params.extra_cards = extra_cards + + G.GAME.starting_params.blacklisted_ranks = unstb.lowkey_blacklisted + end + + --Set the game flags to prevent high rank, this affact premium booster + if self.config.prevent_ranks then + G.GAME.prevent_high_rank = true + end + + --Code based on Abandoned Sleeve from CardSleeve itself, for special effect + if self.config.prevent_ranks and self.allowed_card_centers == nil then + self.allowed_card_centers = {} + self.skip_trigger_effect = true + + for _, card_center in pairs(G.P_CARDS) do + local card_instance = Card(0, 0, 0, 0, card_center, G.P_CENTERS.c_base) + + if not unstb.lowkey_blacklisted[card_instance.base.value] then + self.allowed_card_centers[#self.allowed_card_centers+1] = card_center + end + card_instance:remove() + end + + -- Make it loops around to 0 immediately after using strength on 5 + self.get_rank_after_5 = function() return "unstb_0" end + self.skip_trigger_effect = false + end + end, + trigger_effect = function(self, args) + + --Code based on Abandoned Sleeve from CardSleeve itself + if not self.config.prevent_ranks then + return + end + if self.skip_trigger_effect then + return + end + if self.allowed_card_centers == nil then + self:apply() + end + + -- handle Strength, Ouija, Grim, Familiar + local card = args.context and args.context.card + local is_playing_card = card and (card.ability.set == "Default" or card.ability.set == "Enhanced") and card.config.card_key + if args.context and args.context.before_use_consumable and card then + if card.ability.name == 'Strength' then + self.in_strength = true + elseif card.ability.name == "Ouija" then + self.in_ouija = true + elseif card.ability.name == "Grim" then + self.in_grim = true + elseif card.ability.name == "Familiar" then + self.in_familiar = true + end + + elseif args.context and args.context.after_use_consumable then + self.in_strength = nil + self.in_ouija = nil + self.ouija_rank = nil + + self.in_grim = nil + self.in_familiar = nil + elseif G.GAME and G.GAME.blind and args.context and (args.context.create_card or args.context.modify_playing_card) and card and is_playing_card then + if unstb.lowkey_blacklisted[card.base.value] then + local initial = G.GAME.blind == nil or args.context.create_card + + if self.in_strength then + local base_key = SMODS.Suits[card.base.suit].card_key .. "_" .. self.get_rank_after_5() + card:set_base(G.P_CARDS[base_key], initial) + elseif self.in_grim then --Grim will always create 1 (because Ace) + local base_key = SMODS.Suits[card.base.suit].card_key .. "_unstb_1" + card:set_base(G.P_CARDS[base_key], initial) + elseif self.in_ouija then + if self.ouija_rank == nil then + local random_base = pseudorandom_element(self.allowed_card_centers, pseudoseed("slv")) + local card_instance = Card(0, 0, 0, 0, random_base, G.P_CENTERS.c_base) + self.ouija_rank = SMODS.Ranks[card_instance.base.value] + card_instance:remove() + end + local base_key = SMODS.Suits[card.base.suit].card_key .. "_" .. self.ouija_rank.card_key + card:set_base(G.P_CARDS[base_key], initial) + else + local new_rank = pseudorandom_element(unstb.lowkey_rankList, pseudoseed("slv")) + --Use smods change_base so we can still keep the suit + SMODS.change_base(card, nil, new_rank) + + if self.in_familiar and unstb_config.gameplay.seal_suit then --For Familiar, adds face seal to the created card (if the mechanic is enabled) + card:set_seal('unstb_face', true, true) + end + end + end + end + end, +}) + +end + +end + +--Reworked Vanilla Joker to support new features +if unstb_config.joker.vanilla then + +filesystem.load(unstb.path..'/override/vanilla_joker.lua')() + +end + +--Suits, supports for Suit Seals, a lot of suit-based Joker, and modded suits support for Smeared +filesystem.load(unstb.path..'/override/suits.lua')() + +--JokerDisplay (Partial) Support +if JokerDisplay then + SMODS.load_file("/override/jokerdisplay.lua")() +end + +--Manually load localizations and populate necessary info needed for the mod + +--Global table entry to handle trigger for name-related stuff +unstb_global.name_joker = {} +unstb_global.name_card = {} +unstb_global.name_suit = {} + +function unstb_process_english_loc() + local function populateList(temp_loc) + if not temp_loc or not temp_loc.descriptions then return end + + local jokers = temp_loc.descriptions.Joker or {} + for k,v in pairs(jokers) do + if not unstb_global.name_joker[k] and not unstb_global.name_card[k] then + if v.name and string.match(string.lower(v.name), "joker") then + unstb_global.name_joker[k] = v.name + end + + if v.name and string.match(string.lower(v.name), "card") then + unstb_global.name_card[k] = v.name + end + end + end + + --Process suit loc if exists + local suit = (temp_loc.misc and temp_loc.misc.suits_singular) or {} + + for k,v in pairs(suit) do + if not unstb_global.name_suit[k] and v then + local suit_name = string.lower(v) + + local vowel = suit_name:gsub("[^aeiou]","") + local consonant = suit_name:gsub("[^bcdfghjklmnpqrstvwxyz]","") --To be safe, list alphabet only, no symbol, space, or numbers + + unstb_global.name_suit[k] = { + name = v, + count_vowel = string.len(vowel), + count_consonant = string.len(consonant) + } + end + end + + end + + local function loc_fallback() + --print("Called loc fallback") + return {} + end + + print("Start initializing localization-independent info") + --Basegame Jokers + local temp_loc = assert(loadstring(love.filesystem.read('localization/en-us.lua')))() + populateList(temp_loc) + + --Modded Jokers + for k, _ in pairs(SMODS.Mods) do + if SMODS.Mods[k].can_load and SMODS.Mods[k].path then + temp_loc = (SMODS.load_file('/localization/en-us.lua', k) or loc_fallback)() + if temp_loc then + populateList(temp_loc) + end + end + end + + --Fallback, handle the rest of the mod without proper localization (eg. uses loc_txt) + --these mods tend to not have proper localization so it should be fine to look at the game's raw table + populateList(G.localization) + + print("Finished initializing localization-independent info") + +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) + + --set_consumeable_usage hook to keep track of UnStable's own consumable count + --Certain mod like Aurinko made this hook doesn't work fsr + local set_consumeable_usage_ref = set_consumeable_usage + + function set_consumeable_usage(card) + if card.config.center_key and card.ability.consumeable then + if card.config.center.set == 'Auxiliary' then + --Initialize it if not, basically what basegame does but have to put here bc it runs before basegame's + G.GAME.consumeable_usage_total = G.GAME.consumeable_usage_total or {tarot = 0, planet = 0, spectral = 0, tarot_planet = 0, all = 0} + + G.GAME.consumeable_usage_total.auxiliary = (G.GAME.consumeable_usage_total.auxiliary or 0) + 1 + end + end + + return set_consumeable_usage_ref(card) + end + + init_prev_rank_data() + + if init_lowkey_blacklist then + init_lowkey_blacklist() + end + + unstb_process_english_loc() +end \ No newline at end of file diff --git a/UnStable-main/assets/1x/auxiliary.png b/UnStable-main/assets/1x/auxiliary.png new file mode 100644 index 0000000..f29c4f4 Binary files /dev/null and b/UnStable-main/assets/1x/auxiliary.png differ diff --git a/UnStable-main/assets/1x/auxiliary_undiscovered.png b/UnStable-main/assets/1x/auxiliary_undiscovered.png new file mode 100644 index 0000000..7d6e6f7 Binary files /dev/null and b/UnStable-main/assets/1x/auxiliary_undiscovered.png differ diff --git a/UnStable-main/assets/1x/back.png b/UnStable-main/assets/1x/back.png new file mode 100644 index 0000000..21a4c29 Binary files /dev/null and b/UnStable-main/assets/1x/back.png differ diff --git a/UnStable-main/assets/1x/booster.png b/UnStable-main/assets/1x/booster.png new file mode 100644 index 0000000..144f9fc Binary files /dev/null and b/UnStable-main/assets/1x/booster.png differ diff --git a/UnStable-main/assets/1x/deck.png b/UnStable-main/assets/1x/deck.png new file mode 100644 index 0000000..01736fd Binary files /dev/null and b/UnStable-main/assets/1x/deck.png differ diff --git a/UnStable-main/assets/1x/enh_res.png b/UnStable-main/assets/1x/enh_res.png new file mode 100644 index 0000000..b8e78c4 Binary files /dev/null and b/UnStable-main/assets/1x/enh_res.png differ diff --git a/UnStable-main/assets/1x/enh_slop.png b/UnStable-main/assets/1x/enh_slop.png new file mode 100644 index 0000000..bacbfcb Binary files /dev/null and b/UnStable-main/assets/1x/enh_slop.png differ diff --git a/UnStable-main/assets/1x/enh_slop_hc.png b/UnStable-main/assets/1x/enh_slop_hc.png new file mode 100644 index 0000000..5b9ecd1 Binary files /dev/null and b/UnStable-main/assets/1x/enh_slop_hc.png differ diff --git a/UnStable-main/assets/1x/jokers.png b/UnStable-main/assets/1x/jokers.png new file mode 100644 index 0000000..9d41e66 Binary files /dev/null and b/UnStable-main/assets/1x/jokers.png differ diff --git a/UnStable-main/assets/1x/jokers_ex.png b/UnStable-main/assets/1x/jokers_ex.png new file mode 100644 index 0000000..ec6010c Binary files /dev/null and b/UnStable-main/assets/1x/jokers_ex.png differ diff --git a/UnStable-main/assets/1x/jokers_wip.png b/UnStable-main/assets/1x/jokers_wip.png new file mode 100644 index 0000000..a82ab67 Binary files /dev/null and b/UnStable-main/assets/1x/jokers_wip.png differ diff --git a/UnStable-main/assets/1x/modicon.png b/UnStable-main/assets/1x/modicon.png new file mode 100644 index 0000000..f46ef30 Binary files /dev/null and b/UnStable-main/assets/1x/modicon.png differ diff --git a/UnStable-main/assets/1x/rank_ex.png b/UnStable-main/assets/1x/rank_ex.png new file mode 100644 index 0000000..349ce5d Binary files /dev/null and b/UnStable-main/assets/1x/rank_ex.png differ diff --git a/UnStable-main/assets/1x/rank_ex2.png b/UnStable-main/assets/1x/rank_ex2.png new file mode 100644 index 0000000..7339989 Binary files /dev/null and b/UnStable-main/assets/1x/rank_ex2.png differ diff --git a/UnStable-main/assets/1x/rank_ex2_hc.png b/UnStable-main/assets/1x/rank_ex2_hc.png new file mode 100644 index 0000000..b0a57a8 Binary files /dev/null and b/UnStable-main/assets/1x/rank_ex2_hc.png differ diff --git a/UnStable-main/assets/1x/rank_ex_hc.png b/UnStable-main/assets/1x/rank_ex_hc.png new file mode 100644 index 0000000..91d6bb0 Binary files /dev/null and b/UnStable-main/assets/1x/rank_ex_hc.png differ diff --git a/UnStable-main/assets/1x/sleeve.png b/UnStable-main/assets/1x/sleeve.png new file mode 100644 index 0000000..6782940 Binary files /dev/null and b/UnStable-main/assets/1x/sleeve.png differ diff --git a/UnStable-main/assets/1x/spectral.png b/UnStable-main/assets/1x/spectral.png new file mode 100644 index 0000000..d509ee9 Binary files /dev/null and b/UnStable-main/assets/1x/spectral.png differ diff --git a/UnStable-main/assets/1x/suit_seal.png b/UnStable-main/assets/1x/suit_seal.png new file mode 100644 index 0000000..2692328 Binary files /dev/null and b/UnStable-main/assets/1x/suit_seal.png differ diff --git a/UnStable-main/assets/1x/tarot.png b/UnStable-main/assets/1x/tarot.png new file mode 100644 index 0000000..18db774 Binary files /dev/null and b/UnStable-main/assets/1x/tarot.png differ diff --git a/UnStable-main/assets/1x/voucher.png b/UnStable-main/assets/1x/voucher.png new file mode 100644 index 0000000..8cb2eb0 Binary files /dev/null and b/UnStable-main/assets/1x/voucher.png differ diff --git a/UnStable-main/assets/2x/auxiliary.png b/UnStable-main/assets/2x/auxiliary.png new file mode 100644 index 0000000..5b5cb63 Binary files /dev/null and b/UnStable-main/assets/2x/auxiliary.png differ diff --git a/UnStable-main/assets/2x/auxiliary_undiscovered.png b/UnStable-main/assets/2x/auxiliary_undiscovered.png new file mode 100644 index 0000000..6dd09fe Binary files /dev/null and b/UnStable-main/assets/2x/auxiliary_undiscovered.png differ diff --git a/UnStable-main/assets/2x/back.png b/UnStable-main/assets/2x/back.png new file mode 100644 index 0000000..e325aea Binary files /dev/null and b/UnStable-main/assets/2x/back.png differ diff --git a/UnStable-main/assets/2x/booster.png b/UnStable-main/assets/2x/booster.png new file mode 100644 index 0000000..73a688f Binary files /dev/null and b/UnStable-main/assets/2x/booster.png differ diff --git a/UnStable-main/assets/2x/deck.png b/UnStable-main/assets/2x/deck.png new file mode 100644 index 0000000..8d13736 Binary files /dev/null and b/UnStable-main/assets/2x/deck.png differ diff --git a/UnStable-main/assets/2x/enh_res.png b/UnStable-main/assets/2x/enh_res.png new file mode 100644 index 0000000..2788c3f Binary files /dev/null and b/UnStable-main/assets/2x/enh_res.png differ diff --git a/UnStable-main/assets/2x/enh_slop.png b/UnStable-main/assets/2x/enh_slop.png new file mode 100644 index 0000000..93b9f97 Binary files /dev/null and b/UnStable-main/assets/2x/enh_slop.png differ diff --git a/UnStable-main/assets/2x/enh_slop_hc.png b/UnStable-main/assets/2x/enh_slop_hc.png new file mode 100644 index 0000000..ac1e0e7 Binary files /dev/null and b/UnStable-main/assets/2x/enh_slop_hc.png differ diff --git a/UnStable-main/assets/2x/jokers.png b/UnStable-main/assets/2x/jokers.png new file mode 100644 index 0000000..9023fb8 Binary files /dev/null and b/UnStable-main/assets/2x/jokers.png differ diff --git a/UnStable-main/assets/2x/jokers_ex.png b/UnStable-main/assets/2x/jokers_ex.png new file mode 100644 index 0000000..9df15ab Binary files /dev/null and b/UnStable-main/assets/2x/jokers_ex.png differ diff --git a/UnStable-main/assets/2x/jokers_wip.png b/UnStable-main/assets/2x/jokers_wip.png new file mode 100644 index 0000000..00268ce Binary files /dev/null and b/UnStable-main/assets/2x/jokers_wip.png differ diff --git a/UnStable-main/assets/2x/modicon.png b/UnStable-main/assets/2x/modicon.png new file mode 100644 index 0000000..a760ce6 Binary files /dev/null and b/UnStable-main/assets/2x/modicon.png differ diff --git a/UnStable-main/assets/2x/rank_ex.png b/UnStable-main/assets/2x/rank_ex.png new file mode 100644 index 0000000..a9c9c66 Binary files /dev/null and b/UnStable-main/assets/2x/rank_ex.png differ diff --git a/UnStable-main/assets/2x/rank_ex2.png b/UnStable-main/assets/2x/rank_ex2.png new file mode 100644 index 0000000..db92a71 Binary files /dev/null and b/UnStable-main/assets/2x/rank_ex2.png differ diff --git a/UnStable-main/assets/2x/rank_ex2_hc.png b/UnStable-main/assets/2x/rank_ex2_hc.png new file mode 100644 index 0000000..61f4d01 Binary files /dev/null and b/UnStable-main/assets/2x/rank_ex2_hc.png differ diff --git a/UnStable-main/assets/2x/rank_ex_hc.png b/UnStable-main/assets/2x/rank_ex_hc.png new file mode 100644 index 0000000..46f2fd2 Binary files /dev/null and b/UnStable-main/assets/2x/rank_ex_hc.png differ diff --git a/UnStable-main/assets/2x/sleeve.png b/UnStable-main/assets/2x/sleeve.png new file mode 100644 index 0000000..66951e1 Binary files /dev/null and b/UnStable-main/assets/2x/sleeve.png differ diff --git a/UnStable-main/assets/2x/spectral.png b/UnStable-main/assets/2x/spectral.png new file mode 100644 index 0000000..88a468f Binary files /dev/null and b/UnStable-main/assets/2x/spectral.png differ diff --git a/UnStable-main/assets/2x/suit_seal.png b/UnStable-main/assets/2x/suit_seal.png new file mode 100644 index 0000000..cc8a15a Binary files /dev/null and b/UnStable-main/assets/2x/suit_seal.png differ diff --git a/UnStable-main/assets/2x/tarot.png b/UnStable-main/assets/2x/tarot.png new file mode 100644 index 0000000..ca091b6 Binary files /dev/null and b/UnStable-main/assets/2x/tarot.png differ diff --git a/UnStable-main/assets/2x/voucher.png b/UnStable-main/assets/2x/voucher.png new file mode 100644 index 0000000..5ddcef7 Binary files /dev/null and b/UnStable-main/assets/2x/voucher.png differ diff --git a/UnStable-main/assets/sounds/heal.ogg b/UnStable-main/assets/sounds/heal.ogg new file mode 100644 index 0000000..1b9e43f Binary files /dev/null and b/UnStable-main/assets/sounds/heal.ogg differ diff --git a/UnStable-main/assets/sounds/music_aux.ogg b/UnStable-main/assets/sounds/music_aux.ogg new file mode 100644 index 0000000..093723d Binary files /dev/null and b/UnStable-main/assets/sounds/music_aux.ogg differ diff --git a/UnStable-main/assets/sounds/poison.ogg b/UnStable-main/assets/sounds/poison.ogg new file mode 100644 index 0000000..4b07459 Binary files /dev/null and b/UnStable-main/assets/sounds/poison.ogg differ diff --git a/UnStable-main/assets/sounds/source.txt b/UnStable-main/assets/sounds/source.txt new file mode 100644 index 0000000..d8177af --- /dev/null +++ b/UnStable-main/assets/sounds/source.txt @@ -0,0 +1,3 @@ +http://www.kurage-kosho.info/battle.html +heal: magic04 +poison: status02 \ No newline at end of file diff --git a/UnStable-main/config.lua b/UnStable-main/config.lua new file mode 100644 index 0000000..c81a47a --- /dev/null +++ b/UnStable-main/config.lua @@ -0,0 +1,25 @@ +return { + ["rank"] = { + ["rank_21"] = true, + ["rank_binary"] = true, + ["rank_decimal"] = true, + }, + ["enh"] = { + ["enh_custom"] = true, + ["enh_disenh"] = true, + }, + ["gameplay"] = { + ["edition_upgrade"] = true, + ["seal_suit"] = true, + ["c_aux"] = true, + ["music"] = true, + ["c_rebundant"] = true, + ["new_spectrals"] = true, + }, + ["joker"] = { + ["vanilla"] = true, + ["shitpost"] = true, + ["alter"] = true, + ["powerful"] = true, + } +} \ No newline at end of file diff --git a/UnStable-main/localization/en-us.lua b/UnStable-main/localization/en-us.lua new file mode 100644 index 0000000..8a9b074 --- /dev/null +++ b/UnStable-main/localization/en-us.lua @@ -0,0 +1,1634 @@ +return { + ["misc"] = { + ["dictionary"] = { + + --Config Stuff + + ["unstb_config_requires_restart"] = "Requires Restart to Apply Effects", + + ["unstb_config_header_mech_setting"] = "Mechanics Settings", + + ["unstb_config_header_rank"] = "Rank", + ["unstb_config_rank21"] = "High-Numbered Ranks", + ["unstb_config_rank_bi"] = "Rank 0 and 1", + ["unstb_config_rank_decimal"] = "Decimal Ranks", + + ["unstb_config_header_enh"] = "Enhancement", + ["unstb_config_enh_custom"] = "New Enhancement", + ["unstb_config_enh_disenh"] = "Unhancement", + + ["unstb_config_header_mechanics"] = "New Mechanics", + ["unstb_config_mech_upgrade"] = "Edition Upgrade", + ["unstb_config_mech_suitseal"] = "Suit and Face Seal", + ["unstb_config_mech_aux"] = "Auxiliary Card", + ["unstb_config_mech_music"] = "Custom Music", + ["unstb_config_mech_fallback"] = "Fallback Spectrals", + ["unstb_config_mech_fallback_desc"] = { + "Contains same feature as Auxiliary Card, ", + "recommended to turn on if Auxiliary Card is turned off", + }, + ["unstb_config_mech_new_spectral"] = "New Spectral Cards", + + ["unstb_config_header_joker_settings"] = "Joker Settings", + + --Extra Flavour Badge Text + ["k_tarot_exclaim"] = "Tarot!?", + + ["k_blueprint_l_compatible"] = "Left: compatible", + ["k_blueprint_l_incompatible"] = "Left: incompatible", + ["k_blueprint_r_compatible"] = "Right: compatible", + ["k_blueprint_r_incompatible"] = "Right: incompatible", + + --Auxiliary Card stuff + --Category Name + ["k_auxiliary"] = "Auxiliary", + + --Gallery Button + ["b_auxiliary_cards"] = "Auxiliary Cards", + + --Booster Packs + ["k_booster_group_p_unstb_aux_1"] = "Expansion Pack", + ["k_booster_group_p_unstb_aux_2"] = "Expansion Pack", + ["k_booster_group_p_unstb_aux_mega"] = "Expansion Pack", + ["k_booster_group_p_unstb_aux_jumbo"] = "Expansion Pack", + + ["k_booster_group_p_unstb_prem_1"] = "Premium Standard Pack", + ["k_booster_group_p_unstb_prem_2"] = "Premium Standard Pack", + ["k_booster_group_p_unstb_prem_mega"] = "Premium Standard Pack", + ["k_booster_group_p_unstb_prem_jumbo"] = "Premium Standard Pack", + }, + ["ranks"] = { + + ["unstb_21"] = "21", + ["unstb_???"] = "???", + + ["unstb_0"] = "0", + ["unstb_1"] = "1", + + ["unstb_0.5"] = "Half", + ["unstb_r2"] = "Root 2", + ["unstb_e"] = "e", + ["unstb_Pi"] = "Pi", + + ["unstb_11"] = "11", + ["unstb_12"] = "12", + ["unstb_13"] = "13", + ["unstb_25"] = "25", + ["unstb_161"] = "161", + + }, + ["labels"] = { + + ["unstb_spades_seal"] = "Spade Seal", + ["unstb_hearts_seal"] = "Heart Seal", + ["unstb_clubs_seal"] = "Club Seal", + ["unstb_diamonds_seal"] = "Diamond Seal", + + ["unstb_face_seal"] = "Face Seal", + ["unstb_heal_seal"] = "Heal Seal", + + }, + }, + ["descriptions"] = { + + ["Back"] = { + + ["b_unstb_utility"] = { + ["name"] = "Utility Deck", + ["text"] = { + "Start run with the", + "{C:auxiliary}Hardware Store{} voucher", + "and {C:attention}1{} copy of {C:auxiliary}The Question{},", + "{C:auxiliary}Auxiliary Cards{} can appear", + "in the {C:attention}shop{} more often", + }, + }, + + ["b_unstb_lowkey"] = { + ["name"] = "Lowkey Deck", + ["text"] = { + "Start run with only", + "{C:attention}rank 0-5{} cards", + "and all {C:attention}decimal", + "{C:attention}ranks{} in between", + }, + }, + + }, + + ["Sleeve"] = { + + ["sleeve_unstb_utility"] = { + ["name"] = "Utility Sleeve", + ["text"] = { + "Start run with the", + "{C:auxiliary}Hardware Store{} voucher", + "and {C:attention}1{} copy of {C:auxiliary}The Question{},", + "{C:auxiliary}Auxiliary Cards{} can appear", + "in the {C:attention}shop{} more often", + }, + }, + + ["sleeve_unstb_utility_alt"] = { + ["name"] = "Utility Sleeve", + ["text"] = { + "Start run with the", + "{C:auxiliary}Heavy Duty Tools{} voucher", + "and a {C:dark_edition}Negative{} {C:attenttion}Free Trial{}", + }, + }, + + ["sleeve_unstb_lowkey"] = { + ["name"] = "Lowkey Sleeve", + ["text"] = { + "Start run with only", + "{C:attention}rank 0-5{} cards", + "and all {C:attention}decimal", + "{C:attention}ranks{} in between", + }, + }, + + ["sleeve_unstb_lowkey_alt"] = { + ["name"] = "Lowkey Sleeve", + ["text"] = { + "Card with rank higher than", + "{C:attention}5{} cannot appear in the run", + }, + }, + + }, + + ["Other"] = { + + --Playing Card Ability Text + + ["no_chip"] = { + ["text"] = { + "No chips", + }, + }, + ["decimal_rank_ability"] = { + ["text"] = { + "Can act as {C:attention}different rank{}", + "when played in {C:purple}Straight{} hand", + "{C:inactive}[#1#, #2#, #3#]{}", + }, + }, + ["decimal_rank_ability_2"] = { + ["text"] = { + "Can act as {C:attention}different rank{}", + "when played in {C:purple}Straight{} hand", + "{C:inactive}[#1#, #2#]{}", + }, + }, + + -- Other Mechanics + + ["upgrade_edition"] = { + ["name"] = "Upgrade Edition", + ["text"] = { + "Change the card's {C:attention}edition{}", + "by {C:attention}1{} step from", + "{C:inactive}(None){} -> {C:dark_edition}Foil{} ->", + "{C:dark_edition}Hologram{} -> {C:dark_edition}Polychrome{}", + }, + }, + + ["disenhancement"] = { + ["name"] = "Unhancement", + ["text"] = { + "Enhancement that have", + "negative effects", + "{C:attention}(Radioactive, Biohazard, Poison){}", + }, + }, + + --Extra Tooltip for description that does not lend itself well in tooltip form + + ["acorn_tooltip"] = { + ["name"] = "Acorn Mark Card", + ["text"] = { + "Gives {C:chips}double amount{}", + "of chips when in hand,", + "does not give any chips when scored", + }, + }, + ["resource_tooltip"] = { + ["name"] = "Resource Card", + ["text"] = { + "Has no rank, always scored.", + "Gives {X:mult,C:white}X5{} Mult", + "if the played hand has at least", + "one card with {C:attention}(Corresponding Suit){} suit", + "{C:red}Destroys itself after scored{}", + "{C:inactive}(Does not count other Resource Cards){}", + }, + }, + ["poison_tooltip"] = { + ["name"] = "Poison Card", + ["text"] = { + "Gives {C:red}negative amount{} of chips,", + "discarded {C:attention}Poison Cards{}", + "go back into the deck", + }, + }, + + --Seal + + ["suit_seal"] = { + ["name"] = "Suit Seal", + ["label"] = "Suit Seal", + ["text"] = { + "Makes the card also", + "count as {C:attention}(Respective Suit){}", + }, + }, + ["unstb_spades_seal"] = { + ["name"] = "Spade Seal", + ["label"] = "Spade Seal", + ["text"] = { + "This card also", + "counts as {V:1}#1#{}", + }, + }, + ["unstb_hearts_seal"] = { + ["name"] = "Heart Seal", + ["label"] = "Heart Seal", + ["text"] = { + "This card also", + "counts as {V:1}#1#{}", + }, + }, + ["unstb_clubs_seal"] = { + ["name"] = "Club Seal", + ["label"] = "Club Seal", + ["text"] = { + "This card also", + "counts as {V:1}#1#{}", + }, + }, + ["unstb_diamonds_seal"] = { + ["name"] = "Diamond Seal", + ["label"] = "Diamond Seal", + ["text"] = { + "This card also", + "counts as {V:1}#1#{}", + }, + }, + ["unstb_face_seal"] = { + ["name"] = "Face Seal", + ["label"] = "Face Seal", + ["text"] = { + "This card also", + "counts as {C:attention}face{} card", + }, + }, + ["unstb_heal_seal"] = { + ["name"] = "Heal Seal", + ["label"] = "Heal Seal", + ["text"] = { + "Removes {C:red}Unhancement{} from one", + "random card held in hand when scored.", + "This card also cannot be {C:red}Unhanced{}.", + }, + }, + + --Auxiliary Card-related stuff + + ["undiscovered_auxiliary"] = { + ["name"] = "Not Discovered", + ["text"] = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does", + }, + }, + + ["p_unstb_aux_1"] = { + ["group_name"] = "Expansion Pack", + ["name"] = "Expansion Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:auxiliary} Auxiliary{} cards to", + "add to your consumable area", + }, + }, + ["p_unstb_aux_2"] = { + ["group_name"] = "Expansion Pack", + ["name"] = "Expansion Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:auxiliary} Auxiliary{} cards to", + "add to your consumable area", + }, + }, + ["p_unstb_aux_mega"] = { + ["group_name"] = "Expansion Pack", + ["name"] = "Mega Expansion Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:auxiliary} Auxiliary{} cards to", + "add to your consumable area", + }, + }, + ["p_unstb_aux_jumbo"] = { + ["group_name"] = "Expansion Pack", + ["name"] = "Jumbo Expansion Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:auxiliary} Auxiliary{} cards to", + "add to your consumable area", + }, + }, + + ["p_unstb_prem_1"] = { + ["group_name"] = "Premium Standard Pack", + ["name"] = "Premium Standard Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:attention} Enhanced Playing{} cards with", + "{C:attention}special rank{} to add to your deck" + }, + }, + ["p_unstb_prem_2"] = { + ["group_name"] = "Premium Standard Pack", + ["name"] = "Premium Standard Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:attention} Enhanced Playing{} cards with", + "{C:attention}special rank{} to add to your deck" + }, + }, + ["p_unstb_prem_mega"] = { + ["group_name"] = "Premium Standard Pack", + ["name"] = "Mega Premium Standard Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:attention} Enhanced Playing{} cards with", + "{C:attention}special rank{} to add to your deck" + }, + }, + ["p_unstb_prem_jumbo"] = { + ["group_name"] = "Premium Standard Pack", + ["name"] = "Jumbo Premium Standard Pack", + ["text"] = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:attention} Enhanced Playing{} cards with", + "{C:attention}special rank{} to add to your deck" + }, + }, + }, + + ["Spectral"] = { + + --Fallback Spectral Cards + + ["c_unstb_spc_elixir"] = { + ["name"] = "Elixir of Life", + ["text"] = { + "Heal all {C:red}Unhanced Cards{} in the deck", + "Reduce money by half {C:inactive}(rounded up){}", + }, + }, + ["c_unstb_spc_vessel"] = { + ["name"] = "Vessel", + ["text"] = { + "Select up to {C:attention}#1#{} cards,", + "destroy the {C:attention}leftmost{} card", + "and give the corresponding {C:red}Suit Seal{}", + "to all {C:attention}remaining{} cards", + "{C:inactive}(Drag to rearrange){}", + }, + }, + ["c_unstb_spc_conferment"] = { + ["name"] = "Conferment", + ["text"] = { + "Add a {C:attention}Face Seal{}", + "to {C:attention}#1#{} random cards", + "in your hand,", + "{C:red}-$#2#{} {C:inactive}(no debt limit){}", + }, + }, + ["c_unstb_spc_amnesia"] = { + ["name"] = "Amnesia", + ["text"] = { + "Converts {C:attention}#1#{} random", + "cards in your hand", + "to {C:attention}Rank 0{}", + }, + }, + ["c_unstb_spc_altar"] = { + ["name"] = "Altar", + ["text"] = { + "Destroy {C:attention}#1#{} random", + "cards in your hand,", + "add {C:attention}#2#{} random {C:attention}Enhanced{}", + "{C:attention}21s{} to your hand", + }, + }, + ["c_unstb_spc_contract"] = { + ["name"] = "Devil's Contract", + ["text"] = { + "{C:purple}Upgrades {C:attention}#1#{} random", + "card in your hand", + "Randomly {C:red}unhances {C:attention}#2#{}", + "random cards in your hand", + }, + }, + + --Other New Spectral Cards + + ["c_unstb_spc_poltergeist"] = { + ["name"] = "Poltergeist", + ["text"] = { + "Shuffles all", + "Jokers' {C:dark_edition}editions{}", + }, + }, + ["c_unstb_spc_projection"] = { + ["name"] = "Astral Projection", + ["text"] = { + "Swap {C:dark_edition}editions{} between", + "the {C:attention}selected Joker{}", + "and the one {C:attention}to its right{}", + "{C:green}#1# in #2#{} chance that", + "one of the Jokers are {C:red}destroyed{}", + }, + }, + ["c_unstb_spc_siphon"] = { + ["name"] = "Siphon", + ["text"] = { + "Destroys {C:attention}1{} selected {C:attention}Joker{}", + "with an {C:dark_edition}edition{}", + "Gives the {C:attention}Joker's {C:dark_edition}edition{} to", + "{C:attention}#1#{} random card in your hand", + "{C:inactive}({C:dark_edition}Negative{} {C:inactive}excluded){}", + }, + }, + }, + + ["Tarot"] = { + + --Enhancement Conversion Tarot + + ["c_unstb_trt_time"] = { + ["name"] = "Time", + ["text"] = { + "Enhances {C:attention}#1#{} ", + "selected cards to", + "{C:attention}Vintage Cards{}", + }, + }, + ["c_unstb_trt_acorn"] = { + ["name"] = "The Acorn", + ["text"] = { + "Enhances {C:attention}#1#{} selected", + "card into an", + "{C:attention}Acorn Mark Card{}", + }, + }, + ["c_unstb_trt_greed"] = { + ["name"] = "Greed", + ["text"] = { + "Enhances {C:attention}#1#{} ", + "selected cards to", + "{C:attention}Promo Cards{}", + }, + }, + + --Rank-addition Tarot + + ["c_unstb_trt_half"] = { + ["name"] = "The Half", + ["text"] = { + "Splits {C:attention}1{} selected {C:attention}integer-numbered rank{} card", + "into {C:attention}2{} cards with {C:attention}half the rank{} (rounded down).", + "If the initial rank is {C:attention}odd{}, also adds {C:attention}1{}", + "{C:attention}0.5 rank{} card of the same {C:attention}enhancement{}.", + }, + }, + ["c_unstb_trt_knowledge"] = { + ["name"] = "Knowledge", + ["text"] = { + "Adds one random {C:attention}decimal-rank{} card", + "of {C:attention}1{} selected card's suit to hand", + }, + }, + + }, + + ["Enhanced"] = { + + --Standard Enhancement + + ["m_unstb_vintage"] = { + ["name"] = "Vintage Card", + ["text"] = { + "Gains {C:chips}+#1#{} extra chips when scored", + "{C:green}#2# in #3#{} chance to destroy itself", + "{C:inactive}Chance increases by {C:green}+#4#{C:inactive} when scored{}", + }, + }, + ["m_unstb_acorn"] = { + ["name"] = "Acorn Mark Card", + ["text"] = { + "Gives {C:chips}+#1#{} chips when in hand,", + "does not give any chips when scored", + }, + }, + ["m_unstb_promo"] = { + ["name"] = "Promo Card", + ["text"] = { + "Gives {C:gold}$#1#{} while this card stays in hand", + "Gains {C:gold}+$#2#{} when scored", + "{C:green}#3# in #4#{} chance to destroy when scored", + }, + }, + + --Special Joker-centric Enhancement + + ["m_unstb_resource"] = { + ["name"] = "Resource Card", + ["text"] = { + "Has no rank, always scored.", + "Gives {X:mult,C:white}X#1#{} Mult", + "if the played hand has at least", + "one card with {V:1}#2#{} suit", + "{C:red}Destroys itself after scored{}", + "{C:inactive}(Does not count other Resource Cards){}", + }, + }, + ["m_unstb_slop"] = { + ["name"] = "Slop Card", + ["text"] = { + "{C:chips}+#1#{} chips", + "Always scored", + "Random suit and rank after scored", + "Current: {C:attention}#3#{} of {V:1}#2#{}", + }, + }, + + --Unhancements + + ["m_unstb_radioactive"] = { + ["name"] = "Radioactive Card", + ["text"] = { + "{C:chips}+#1#{} Chips, no rank or suit", + "When played, {C:green}#2# in #3#{} chance to", + "convert other played cards to {C:attention}Radioactive Cards{}", + "", + "When held in hand, {C:green}#2# in #4#{} chance to give", + "{X:mult,C:white}X#5#{} Mult, otherwise gives {X:mult,C:white}X#6#{} Mult", + }, + }, + ["m_unstb_biohazard"] = { + ["name"] = "Biohazard Card", + ["text"] = { + "{X:mult,C:white}X#1#{} Mult, no rank or suit", + "Lose {C:red}-$#2#{} while this card stays in hand", + "When discarded, {C:green}#3# in #4#{} chance to turn one random", + "card in the deck into {C:attention}Biohazard Card{}", + }, + }, + ["m_unstb_poison"] = { + ["name"] = "Poison Card", + ["text"] = { + "{C:red}-#1#{} Chips", + "Goes back into deck", + "when discarded", + }, + }, + }, + + ["Auxiliary"] = { + + ["c_unstb_aux_spades"] = { + ["name"] = "Essence of Spades", + ["text"] = { + "Add {V:1}#2#{} to", + "{C:attention}#1#{} selected cards", + "in your hand", + }, + }, + ["c_unstb_aux_hearts"] = { + ["name"] = "Essence of Hearts", + ["text"] = { + "Add {V:1}#2#{} to", + "{C:attention}#1#{} selected cards", + "in your hand", + }, + }, + ["c_unstb_aux_clubs"] = { + ["name"] = "Essence of Clubs", + ["text"] = { + "Add {V:1}#2#{} to", + "{C:attention}#1#{} selected cards", + "in your hand", + }, + }, + ["c_unstb_aux_diamonds"] = { + ["name"] = "Essence of Diamonds", + ["text"] = { + "Add {V:1}#2#{} to", + "{C:attention}#1#{} selected cards", + "in your hand", + }, + }, + + ["c_unstb_aux_face"] = { + ["name"] = "Possessions", + ["text"] = { + "Add a {C:attention}Face Seal{}", + "to {C:attention}#1#{} selected cards", + "in your hand", + }, + }, + + ["c_unstb_aux_plus_two"] = { + ["name"] = "+2", + ["text"] = { + "Creates {C:attention}#1#{} cards in", + "your hand with a random", + "{C:attention}rank{} and the same {C:attention}suit{}", + "as the selected card", + }, + }, + ["c_unstb_aux_plus_four_wild"] = { + ["name"] = "Wild +4", + ["text"] = { + "Creates {C:attention}#1#{} cards in", + "your hand with a random", + "{C:attention}suit{} and the same {C:attention}rank{}", + "as the selected card", + }, + }, + + ["c_unstb_aux_inst_disc"] = { + ["name"] = "The Past", + ["text"] = { + "{C:red}+#1#{} discards this round", + }, + }, + ["c_unstb_aux_inst_hsize"] = { + ["name"] = "The Present", + ["text"] = { + "{C:attention}+#1#{} hand size this round", + }, + }, + ["c_unstb_aux_inst_hand"] = { + ["name"] = "The Future", + ["text"] = { + "{C:blue}+#1#{} hands this round", + }, + }, + + ["c_unstb_aux_seal_move"] = { + ["name"] = "Seal Exchange", + ["text"] = { + "Swap {C:attention}seals{} between", + "{C:attention}2{} selected cards", + }, + }, + + ["c_unstb_aux_conv_1"] = { + ["name"] = "All for One", + ["text"] = { + "Converts rank of", + "up to {C:attention}#1#{} selected", + "cards to {C:attention}1{}", + }, + }, + + ["c_unstb_aux_21"] = { + ["name"] = "The Twenty-One", + ["text"] = { + "Select up to {C:attention}#1#{} cards", + "with total rank of {C:attention}21 or more{}.", + "Destroys all selected cards and creates one", + "{C:attention}rank 21{} card of a random suit.", + }, + }, + + ["c_unstb_aux_upgrade"] = { + ["name"] = "Monkey Paw", + ["text"] = { + "{C:purple}Upgrades {C:attention}1{} selected card's {C:attention}edition{}", + "Turns adjacent cards into {C:attention}Poison Cards{}", + "{C:inactive}(Loops around hand edges){}", + }, + }, + + ["c_unstb_aux_heal"] = { + ["name"] = "Heal Guardian", + ["text"] = { + "Add a {C:attention}Heal Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + ["c_unstb_aux_heal_hand"] = { + ["name"] = "Heal Aura", + ["text"] = { + "Heal all", + "{C:red}Unhanced Cards{}", + "in your hand", + }, + }, + + ["c_unstb_aux_lottery"] = { + ["name"] = "Lottery", + ["text"] = { + "{C:green}#1# in #2#{} chance", + "to win {C:gold}$#3#{}", + }, + }, + + ["c_unstb_aux_blank"] = { + ["name"] = "Blank Card", + ["text"] = { + "Does nothing..?", + }, + }, + ["c_unstb_aux_dark_matter"] = { + ["name"] = "Dark Matter", + ["text"] = { + "Permanently gain", + "{C:dark_edition}+#1#{} Joker slot", + "{C:red}Cannot be Negative", + }, + }, + ["c_unstb_aux_dark_matter_n"] = { + ["name"] = "Dark Matter..?", + ["text"] = { + "Permanently gain", + "{C:red}-#1#{} Joker slot", + }, + }, + + ["c_unstb_aux_random"] = { + ["name"] = "The Question", + ["text"] = { + "Creates up to {C:attention}#1#{}", + "random {C:auxiliary}Auxiliary{} cards", + "{C:inactive}(Must have room)", + }, + }, + }, + + ["Voucher"] = { + + ["v_unstb_aux1"] = { + ["name"] = "Hardware Store", + ["text"] = { + "{C:auxiliary}Auxiliary{} cards", + "can be purchased", + "from the {C:attention}shop{}", + }, + }, + + ["v_unstb_aux2"] = { + ["name"] = "Heavy Duty Tools", + ["text"] = { + "{C:auxiliary}Auxiliary{} cards in", + "{C:attention}Expansion Packs{} can", + "sometimes be {C:dark_edition}Negative{}", + }, + }, + + }, + + ["Joker"] = { + --Basic Common Jokers + + ["j_unstb_lunar_calendar"] = { + ["name"] = "Lunar Calendar", + ["text"] = { + "Played cards with", + "{V:1}#1#{} suit have", + "{C:green}#2# in #3#{} chance to create a", + "random {C:planet}Planet{} card when scored", + "{C:inactive}(Must have room)", + "{s:0.8}Suit changes at the end of the round", + }, + }, + + ["j_unstb_dragon_hoard"] = { + ["name"] = "Dragon Hoard", + ["text"] = { + "Gives {C:mult}+#1#{} Mult for", + "every {C:attention}#2#{} {C:attention}consumables{}", + "in your {C:attention}consumable{} area", + "{C:inactive}(Currently {C:mult}+#3#{C:inactive} Mult)", + }, + }, + + ["j_unstb_card_dealer"] = { + ["name"] = "Card Dealer", + ["text"] = { + "{C:chips}+#1#{} Chips per {C:attention}played{}", + "cards this round", + "{C:attention}Resets{} at the end", + "of the round", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + }, + }, + + ["j_unstb_match_three"] = { + ["name"] = "Match Three", + ["text"] = { + "{C:mult}+#1#{} Mult if played hand contains", + "{C:attention}#2#{} or more adjacent cards", + "with the {C:attention}same base suit{}", + }, + }, + + --Extra Uncommons + + ["j_unstb_furry_joker"] = { + ["name"] = "Furry Joker", + ["text"] = { + "Played {C:attention}Wild Cards{} have", + "{C:green}#1# in #2#{} chance to", + "turn {C:dark_edition}Polychrome{} when scored", + }, + }, + + ["j_unstb_luxurious_handbag"] = { + ["name"] = "Luxurious Handbag", + ["text"] = { + "{C:attention}+#1#{} consumable slot", + "{C:red}Raises prices{} of everything in the", + "{C:attention}shop{} (except reroll) by {C:gold}$#2#{}", + }, + }, + + ["j_unstb_portal"] = { + ["name"] = "Portal", + ["text"] = { + "{C:attention}Aces{} can be used in the", + "middle of a {C:purple}Straight{} hand", + "{C:inactive}(Ex: 3, 2, Ace, King, Queen)", + }, + }, + + --Suit Seal Support + + ["j_unstb_vainglorious_joker"] = { + ["name"] = "Vainglorious Joker", + ["text"] = { + "Played cards with", + "{C:attention}Suit Seal{} give", + "{C:attention}+#1#{} Mult when scored", + }, + }, + + ["j_unstb_acedia_joker"] = { + ["name"] = "Acedia Joker", + ["text"] = { + "Played cards with", + "{C:attention}Suit Seal{} in the same", + "{C:purple}suit category{} as the card's suit", + "give {C:attention}+#1#{} Mult when scored", + }, + }, + + ["j_unstb_cinnabar"] = { + ["name"] = "Cinnabar", + ["text"] = { + "Played cards with {C:attention}Suit Seal{} have", + "{C:green}#1# in #2#{} chance to create respective", + "{C:auxiliary}Seal Auxiliary{} Card when scored", + "{C:inactive}(Must have room)", + }, + }, + + --Auxiliary Support + + ["j_unstb_free_trial"] = { + ["name"] = "Free Trial", + ["text"] = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Negative{} random {C:auxiliary}Auxiliary{} Card", + "when an {C:auxiliary}Auxiliary{} Card is used", + }, + }, + + ["j_unstb_extended_warranty"] = { + ["name"] = "Extended Warranty", + ["text"] = { + "{C:auxiliary}Auxiliary{} Cards in", + "your {C:attention}consumable{} area", + "give {X:mult,C:white}X#1#{} Mult", + }, + }, + + ["j_unstb_technician"] = { + ["name"] = "Technician", + ["text"] = { + "{C:chips}+#1#{} Chips per", + "{C:auxiliary}Auxiliary{} Card", + "used this run", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips){}", + }, + }, + + ["j_unstb_season_pass"] = { + ["name"] = "Season Pass", + ["text"] = { + "{C:green}#1# in #2#{} chance to create", + "a random {C:auxiliary}Auxiliary{} Card", + "when discarding a {C:attention}face{} card", + "{C:inactive}(Must have room)", + }, + }, + + --Rank 21 + + ["j_unstb_black_jack"] = { + ["name"] = "Black Jack", + ["text"] = { + "This Joker gains {C:chips}Chips{} equal to {C:attention}total rank{}", + "of the scored hand if it is under {C:attention}#1#{}.", + "{C:red}Doubles all chips{} if the {C:attention}total rank{} is exactly {C:attention}#1#{}.", + "Resets if the total rank is more than {C:attention}#1#{}.", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + }, + }, + + ["j_unstb_what"] = { + ["name"] = "What", + ["text"] = { + "Played {C:attention}???{}", + "give {C:chips}+#1#{} Chips and", + "{C:mult}+#2#{} Mult when scored", + }, + }, + + --Decimal Rank + + ["j_unstb_floating_point_error"] = { + ["name"] = "Floating-Point Error", + ["text"] = { + "Every played {C:attention}Decimal Rank{} card", + "permanently gains {C:chips}extra chips{}", + "equal to its own {C:attention}rank{}", + }, + }, + + ["j_unstb_academic_journal"] = { + ["name"] = "Academic Journal", + ["text"] = { + "Adds one random {C:attention}Enhanced Card{}", + "with {C:attention}decimal rank{} to deck if", + "scored hand does not have a {C:attention}face{} card", + "{C:inactive}({C:red}#1#{C:inactive} times per round, {C:red}#2# left{C:inactive})", + }, + }, + + ["j_unstb_engineer"] = { + ["name"] = "Engineer", + ["text"] = { + "Cards with {C:attention}decimal ranks{} count as", + "the nearest {C:attention}integer rank{} rounded up when", + "played in non-{C:purple}Straight{} hands", + "{C:inactive}(EX: 3.14 -> 4)", + }, + }, + + ["j_unstb_thesis_proposal"] = { + ["name"] = "Thesis Proposal", + ["text"] = { + "Retrigger all", + "{C:attention}decimal rank{} cards", + "{C:attention}#1#{} additional times", + }, + }, + + ["j_unstb_rainbow_flag"] = { + ["name"] = "Rainbow Flag", + ["text"] = { + "If a hand containing {C:purple}Straight{} has", + "at least 1 {C:attention}decimal rank{} card,", + "{C:green}#1# in #2#{} chance to turn", + "one of the cards {C:dark_edition}Polychrome{}", + }, + }, + + --Binary Ranks + + ["j_unstb_dummy_data"] = { + ["name"] = "Dummy Data", + ["text"] = { + "For each {C:attention}unscored card{}", + "{C:green}#1# in #2#{} chance to", + "convert its rank to {C:attention}0{}", + "(Retains amount of Chips)", + }, + }, + + ["j_unstb_micro_sd_card"] = { + ["name"] = "Micro SD Card", + ["text"] = { + "If this Joker is in the {C:attention}leftmost position{},", + "take all Chips from discarded base {C:attention}numbered cards{}", + "and strip those cards' ranks to {C:attention}0{}", + "{C:green}#1# in #2#{} chance to destroy itself at the end of round", + "Chance increases by the amount of Chips stored", + "{C:inactive}(Currently {C:chips}+#4#{C:inactive} Chips)", + "{C:inactive}Status: {V:1}#5#{}", + }, + }, + + ["j_unstb_social_experiment"] = { + ["name"] = "Social Experiment", + ["text"] = { + "For each scored {C:attention}numbered card{},", + "double its Chips, give it to the next card", + "and strip its {C:attention}rank{} to {C:attention}0{}", + }, + }, + + ["j_unstb_power_of_one"] = { + ["name"] = "Power of One", + ["text"] = { + "Gives {C:mult}+#1#{} Mult for each", + "{C:attention}1{} in your {C:attention}full deck{}", + "{C:inactive}(Currently {C:mult}+#2#{C:inactive})", + }, + }, + + ["j_unstb_binary_number"] = { + ["name"] = "Binary Number", + ["text"] = { + "If played hand contains {C:attention}4{} or fewer cards", + "and only consists of {C:attention}rank 0{} and {C:attention}1{} cards,", + "create a card with {C:attention}rank{} equal", + "to the number represented in binary", + "({C:red}Ex:{} {C:attention}1001{} -> {C:attention}9{})", + "{C:inactive}[Jack, Queen, King, Ace is 11-14, respectively]{}", + }, + }, + + --Basegame Enhancement + + ["j_unstb_quintuplets"] = { + ["name"] = "Quintuplets", + ["text"] = { + "If played hand", + "contains {C:attention}Five of a Kind{},", + "create a {C:dark_edition}Negative Tag{}", + }, + }, + + --Edition Line + + ["j_unstb_graphic_card"] = { + ["name"] = "Graphic Card", + ["text"] = { + "Create a {C:attention}Double Tag{}", + "at the end of the round", + "if {C:attention}#1#{} or more cards with", + "{C:dark_edition}editions{} were scored this round", + "{C:inactive}(Currently {C:attention}#2# {C:inactive}cards)", + }, + }, + + ["j_unstb_connoiseur"] = { + ["name"] = "Connoiseur", + ["text"] = { + "Retrigger all played cards", + "with {C:dark_edition}editions{}", + }, + }, + + ["j_unstb_jeweler"] = { + ["name"] = "Jeweler", + ["text"] = { + "{C:green}#1# in #2#{} chance to downgrade", + "level of played {C:attention}poker hand{}", + "and {C:purple}upgrade{} edition", + "of all scored cards", + }, + }, + + --Slop + + ["j_unstb_joker_diffusion"] = { + ["name"] = "Joker Diffusion", + ["text"] = { + "Converts {C:attention}#1#{} random {C:attention}non-enhanced{} card", + "in hand to {C:attention}Slop Card{} every time", + "a hand is played with {C:attention}unscored card{}", + }, + }, + + ["j_unstb_nonfungible_joker"] = { + ["name"] = "Non-Fungible Joker", + ["text"] = { + "If a discarded hand contains", + "only {C:attention}#1# Slop Card{}, destroy it and", + "earn random amount of up to {C:gold}$#2#", + "{s:0.8,C:red}Maximum Payout and Sell Value drops at the end of the round", + }, + }, + + ["j_unstb_prompt"] = { + ["name"] = "Prompt", + ["text"] = { + "Scored {C:attention}Slop Cards{} take", + "the {C:attention}rank{} and {C:attention}suit{} from", + "other scored {C:attention}non-Slop Cards{}", + "instead of random (if possible)", + }, + }, + + ["j_unstb_uninterested_primate"] = { + ["name"] = "Uninterested Primate", + ["text"] = { + "Gains {C:chips}+#1#{} Chips every {C:attention}#2# {C:inactive}[#6#]", + "{C:attention}Slop Cards{} scored.", + "{C:green}#3# in #4#{} chance this card is", + "destroyed at end of round", + "{C:inactive}(Currently {C:chips}+#5# {C:inactive}Chips)", + }, + }, + + ["j_unstb_lethargic_lion"] = { + ["name"] = "Lethargic Lion", + ["text"] = { + "Gains {X:mult,C:white}X#1#{} Mult every {C:attention}#2# {C:inactive}[#6#]", + "{C:attention}Slop Cards{} scored.", + "{C:green}#3# in #4#{} chance this card is", + "destroyed at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#5#{C:inactive} Mult)", + }, + }, + + --New Enhancement Support + + ["j_unstb_vintage_joker"] = { + ["name"] = "Vintage Joker", + ["text"] = { + "Scored {C:attention}Vintage Cards{} have", + "{C:green}#1# in #2#{} chance to reset", + "their destroy chance", + }, + }, + + ["j_unstb_rules_errata"] = { + ["name"] = "Rules Errata", + ["text"] = { + "{C:attention}Acorn Mark Cards{} give {C:attention}half{} of their", + "{C:attention}rank{}'s value as Mult when scored", + }, + }, + + ["j_unstb_auction_winner"] = { + ["name"] = "Auction Winner", + ["text"] = { + "If a {C:attention}Promo Card{} is", + "destroyed from scoring, this Joker", + "gains {C:gold}money{} equal to", + "the {C:gold}money{} that card gave", + "before its destruction", + }, + }, + + ["j_unstb_joker_island"] = { + ["name"] = "Joker Island", + ["text"] = { + "For every scored card", + "with {C:attention}rank{} {C:attention}#1#{},", + "{C:green}#2# in #3#{} chance to add a", + "{C:attention}Resource Card{} of the same suit to deck", + "{s:0.8}rank changes at end of round", + }, + }, + + --Unhancement Jokers + + ["j_unstb_kaiju"] = { + ["name"] = "Kaiju", + ["text"] = { + "{C:dark_edition}+#1#{} Joker slot", + "Converts one random card", + "into a {C:attention}Radioactive Card{} when", + "first hand is drawn", + }, + }, + + ["j_unstb_poison_the_well"] = { + ["name"] = "Poison the Well", + ["text"] = { + "{C:red}+#1#{} Discards", + "Converts one random card", + "in each discarded hand", + "into a {C:attention}Poison Card{}", + }, + }, + + ["j_unstb_petri_dish"] = { + ["name"] = "Petri Dish", + ["text"] = { + "{C:blue}+#1#{} Hands", + "{C:green}#2# in #3#{} chance of converting", + "one random played card", + "into a {C:attention}Biohazard Card{}", + }, + }, + + --Unhancement Support + + ["j_unstb_geiger_counter"] = { + ["name"] = "Geiger Counter", + ["text"] = { + "Gives {C:mult}+#1#{} Mult for", + "each {C:attention}Radioactive Card{}", + "in your {C:attention}full deck{}", + "{C:inactive}(Currently {C:mult}+#2#{C:inactive} Mult)", + }, + }, + + ["j_unstb_strych_nine"] = { + ["name"] = "Strych-Nine", + ["text"] = { + "Gives {C:chips}+#1#{} Chips", + "for each {C:attention}Poison Card{}", + "in your {C:attention}full deck{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + }, + }, + + ["j_unstb_vaccination_card"] = { + ["name"] = "Vaccination Card", + ["text"] = { + "Neutralize all {C:attention}Biohazard Card{} passive effects", + "Gives additional {X:mult,C:white}X#1#{} Mult for each", + "{C:attention}Biohazard Card{} held in hand", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + }, + }, + + --Shitposts + + ["j_unstb_joker2"] = { + ["name"] = "Joker2", + ["text"] = { + "{C:mult}+#1#{} Mult", + "{X:mult,C:white}X#2#{} Mult", + "{C:green}#3# in #4#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + + ["j_unstb_joker_stairs"] = { + ["name"] = "Joker Stairs", + ["text"] = { + "Gain {C:mult}+#1#{} Mult when buying a Joker", + "with {C:attention}\"Joker\"{} in its name", + "directly from the shop", + "{C:inactive}(Currently {C:mult}+#2#{C:inactive} Mult){}", + }, + }, + + ["j_unstb_plagiarism"] = { + ["name"] = "Plagiarism", + ["text"] = { + "Copies ability of", + "{C:attention}Joker{} to the", + "left or right at random", + "{s:0.8}Direction changes each hand played", + }, + }, + + ["j_unstb_joker_throwing_card"] = { + ["name"] = "Joker Throwing Card", + ["text"] = { + "Decrease {C:attention}Blind{} size by {C:attention}#1#%{}", + "{C:green}#2# in #3#{} chance to destroy", + "one of the scored cards", + "Increases percentage by {C:attention}#4#%{}", + "if a card is destroyed this way", + }, + }, + + --Face Card Jokers + + ["j_unstb_jackhammer"] = { + ["name"] = "Jackhammer", + ["text"] = { + "If the {C:attention}played hand{} has only one {C:attention}Jack{},", + "retrigger it {C:attention}#1#{} more times", + "and {C:red}destroy it{} afterward", + }, + }, + + ["j_unstb_jack_of_all_trades"] = { + ["name"] = "Jack of All Trades", + ["text"] = { + "Played {C:attention}Jacks{} give", + "{C:chips}+#1#{} Chips, {C:mult}+#2#{} Mult, ", + "{X:mult,C:white}X#3#{} Mult, and {C:gold}$#4#", + }, + }, + + ["j_unstb_magic_trick_card"] = { + ["name"] = "Magic Trick Card", + ["text"] = { + "Scored {V:1}#1#{} cards", + "turn into {V:2}#2#{} cards", + "{s:0.8}On discard, flips over the Joker", + }, + }, + + ["j_unstb_queensland"] = { + ["name"] = "Queensland", + ["text"] = { + "Scored {C:attention}Queens{} add", + "{C:attention}Resource Cards{} of", + "the corresponding suits to deck", + "{C:inactive}({C:red}#1#{C:inactive} times per round, {C:red}#2# left{C:inactive})", + }, + }, + + ["j_unstb_king_of_pop"] = { + ["name"] = "King of Pop", + ["text"] = { + "Played {C:attention}Enhanced Kings{} have", + "{C:green}#1# in #2#{} chance to be", + "destroyed and create a {C:dark_edition}Double Tag", + }, + }, + + ["j_unstb_prssj"] = { + ["name"] = "Polychrome Red Seal Steel Joker", + ["text"] = { + "{C:green}#1# in #2#{} chance for scored {C:attention}Kings{} to {C:purple}upgrade{} edition", + "{C:green}#1# in #3#{} chance for scored {C:attention}Kings{} to {C:attention}retrigger", + "{C:green}#1# in #4#{} chance for {C:attention}Kings{} held in hand to give {X:mult,C:white}X#5#{} Mult", + }, + }, + + ["j_unstb_master_of_one"] = { + ["name"] = "Master of One", + ["text"] = { + "Adds one {C:attention}Enhanced Rank 1{} card", + "to deck when {C:attention}Blind{} is selected", + }, + }, + + --Intermission + + ["j_unstb_spectre"] = { + ["name"] = "Spectre", + ["text"] = { + "{X:mult,C:white}X#1#{} Mult per {C:spectral}Spectral{}", + "card used this run", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + + ["j_unstb_library_card"] = { + ["name"] = "Library Card", + ["text"] = { + "For each scored card's {C:attention}singular", + "{C:attention}suit name{} in English, give:", + "{C:chips}+#1#{} Chips per {C:attention}Consonant", + "{C:mult}+#2#{} Mult per {C:attention}Vowel", + "{C:inactive}(Example: Spade = {C:chips}+15{C:inactive} Chips, {C:mult}+4{C:inactive} Mult)", + }, + }, + + ["j_unstb_collector_album"] = { + ["name"] = "Collector's Album", + ["text"] = { + "Jokers with {C:attention}\"Card\"{} in", + "their name each give", + "{C:chips}+#1#{} Chips", + }, + }, + + ["j_unstb_throwing_hands"] = { + ["name"] = "Throwing Hands", + ["text"] = { + "{X:mult,C:white}X#1#{} Mult", + "{C:green}#2# in #3#{} chance to {C:red}self destruct{}", + "if the poker hand is not {C:attention}#4#{},", + "{s:0.8}hand changes at end of round", + }, + }, + + --Historic Cards + + ["j_unstb_imperial_bower"] = { + ["name"] = "Imperial Bower", + ["text"] = { + "{X:mult,C:white}X#1#{} Mult if played hand", + "contains a {C:attention}Straight{} and", + "has at least one {C:attention}face{} card", + }, + }, + + ["j_unstb_the_jolly_joker"] = { + ["name"] = "The \"Jolly Joker\"", + ["text"] = { + "Gains {C:mult}+#1#{} Mult if", + "played hand contains a {C:attention}Pair{}", + "{C:red}Resets{} if played hand", + "does not contain a {C:attention}Pair{}", + "{C:inactive}(Currently {C:mult}+#2#{C:inactive} Mult)", + }, + }, + + ["j_unstb_get_out_of_jail_free_card"] = { + ["name"] = "Get Out of Jail Free Card", + ["text"] = { + "Sell this Joker to", + "win the blind instantly", + "{C:inactive}Status: {V:1}#1#{}", + }, + }, + + ["j_unstb_tanzaku"] = { + ["name"] = "Tanzaku", + ["text"] = { + "Retrigger played cards with", + "{C:attention}seals{} {C:attention}#1#{} additional times", + "Amount increases by {C:attention}#2#{} for", + "every discarded card with a {C:attention}seal{}", + "{s:0.8}Resets after each round", + }, + }, + + --Misc Jokers + + ["j_unstb_glass_cannon"] = { + ["name"] = "Glass Cannon", + ["text"] = { + "Retrigger each", + "played {C:attention}Glass Card{}", + "{C:red}Glass Cards are guaranteed", + "{C:red}to break when scored", + }, + }, + + ["j_unstb_pity_rate_drop"] = { + ["name"] = "Pity Rate Drop", + ["text"] = { + "When {C:attention}Blind{} is selected,", + "{C:green}#1# in #2#{} chance to create a {C:red}Rare {C:attention}Joker{}", + "Chance increase by {C:green}#3#{} if no {C:attention}Joker{} created,", + "resets to {C:green}#4#{} otherwise", + "{C:inactive}(Must have room)", + }, + }, + + ["j_unstb_salmon_run"] = { + ["name"] = "Salmon Run", + ["text"] = { + "Each played {C:attention}7{} has a", + "{C:green}#1# in #2#{} chance of adding a", + "permanent copy to deck when scored", + }, + }, + + ["j_unstb_cool_s"] = { + ["name"] = "Cool S", + ["text"] = { + "Scored {C:attention}8s{} become", + "a random {C:attention}Enhanced card{}", + }, + }, + + ["j_unstb_memoriam_photo"] = { + ["name"] = "Memoriam Photo", + ["text"] = { + "When a playing card is {C:attention}destroyed{},", + "this Joker gains Chips equal to {C:attention}double{}", + "the rank of the {C:attention}destroyed card{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + + ["j_unstb_schrodinger_cat"] = { + ["name"] = "Schrödinger's Cat", + ["text"] = { + "When a playing card is {C:attention}destroyed{},", + "{C:green}#1# in #2#{} chance to add", + "a copy of the card to deck", + }, + }, + + ["j_unstb_cashback_card"] = { + ["name"] = "Cashback Card", + ["text"] = { + "At the end of a {C:attention}Boss Blind{},", + "earn {C:gold}money{} equals to the amount", + "of {C:attention}consumables{} used this {C:attention}Ante", + "{C:inactive}(Currently {C:gold}$#1#{C:inactive})", + }, + }, + + ["j_unstb_raffle"] = { + ["name"] = "Raffle", + ["text"] = { + "At the end of each {C:attention}shop{},", + "{C:green}#1# in #2#{} chance to win {C:gold}$#3#", + "Chance increases by {C:green}#4#{}", + "for each purchase in the {C:attention}shop", + "{s:0.8, C:red}Resets after exiting the shop", + }, + }, + + ["j_unstb_ic_card"] = { + ["name"] = "IC Card", + ["text"] = { + "Gain {C:gold}$#1#{} to store in this Joker", + "every time a hand is played", + "When spending money, the {C:attention}balance", + "in this Joker is spent first", + "{C:red}Only usable for #2# rounds", + "{C:inactive}(Currently {C:gold}$#3#{C:inactive}, {C:red}#4# Rounds left{C:inactive}){}", + }, + }, + + ["j_unstb_j2048"] = { + ["name"] = "2048", + ["text"] = { + "If played hand contains a {C:attention}Pair{}", + "destroy the left one and", + "give all chips to the other", + "{C:inactive}(Drag to rearrange){}", + }, + }, + + ["j_unstb_inductor"] = { + ["name"] = "Inductor", + ["text"] = { + "For every played card with the same", + "{C:attention}rank{} and suit{C:attention}{} as another played card:", + "{C:green}#1# in #2#{} chance to copy {C:attention}Enhancement{}", + "{C:green}#1# in #3#{} chance to copy {C:attention}Edition{}", + "{C:green}#1# in #4#{} chance to copy {C:attention}Seal{}", + }, + }, + + + --Vanilla Joker Override + + ["j_fibonacci_ex"] = { + ["name"] = "Fibonacci", + ["text"] = { + "Each played {C:attention}0{}, {C:attention}1{}, {C:attention}Ace{},", + "{C:attention}2{}, {C:attention}3{}, {C:attention}5{}, {C:attention}8{}, {C:attention}13{}, or {C:attention}21{} gives", + "{C:mult}+#1#{} Mult when scored", + }, + }, + + ["j_odd_todd_ex"] = { + ["name"] = "Odd Todd", + ["text"] = { + "Played cards with", + "{C:attention}odd{} rank give", + "{C:chips}+#1#{} Chips when scored", + "{C:inactive}(161, 21, 13, 11, A, 9, 7, 5, 3, 1){}", + }, + }, + + ["j_even_steven_ex"] = { + ["name"] = "Even Steven", + ["text"] = { + "Played cards with", + "{C:attention}even{} rank give", + "{C:mult}+#1#{} Mult when scored", + "{C:inactive}(12, 10, 8, 6, 4, 2, 0){}", + }, + }, + + ["j_hack_ex"] = { + ["name"] = "Hack", + ["text"] = { + "Retrigger", + "each played", + "{C:attention}0{}, {C:attention}1{}, {C:attention}2{},", + "{C:attention}3{}, {C:attention}4{}, or {C:attention}5{}", + }, + }, + }, + + ["Mod"] = { + ["UnStable"] = { + ["name"] = "UnStable", + ["text"] = { + "Mod with a lot of new but vanilla-style mechanics.", + "Contains new Jokers, consumables, and other stuff!", + " ", + "{C:blue}Brazilian Portuguese{} localization by {C:attention}PinkMaggit{}" + } + } + } + + }, +} diff --git a/UnStable-main/localization/pt_BR.lua b/UnStable-main/localization/pt_BR.lua new file mode 100644 index 0000000..93733a6 --- /dev/null +++ b/UnStable-main/localization/pt_BR.lua @@ -0,0 +1,1631 @@ +return { + ["misc"] = { + ["dictionary"] = { + + --Config Stuff + + ["unstb_config_requires_restart"] = "Necessário reiniciar o jogo para aplicar os efeitos", + + ["unstb_config_header_mech_setting"] = "Mecânicas", + + ["unstb_config_header_rank"] = "Classe", + ["unstb_config_rank21"] = "Classe 21", + ["unstb_config_rank_bi"] = "Classes 0 e 1", + ["unstb_config_rank_decimal"] = "Classes Decimais", + + ["unstb_config_header_enh"] = "Aprimoramento", + ["unstb_config_enh_custom"] = "Novo aprimoramento", + ["unstb_config_enh_disenh"] = "Desaprimoramento", + + ["unstb_config_header_mechanics"] = "Novas Mecânicas", + ["unstb_config_mech_upgrade"] = "Aprimoramento de Edição", + ["unstb_config_mech_suitseal"] = "Selos de Naipe e Classe", + ["unstb_config_mech_aux"] = "Carta Auxiliar", + ["unstb_config_mech_music"] = "Música Personalizada", + ["unstb_config_mech_fallback"] = "Espectrais Alternativos", + ["unstb_config_mech_fallback_desc"] = { + "Contém as mesmas propriedades da Carta Auxiliar, ", + "é recomendado ligar se Carta Auxiliar estiver desligado", + }, + ["unstb_config_mech_new_spectral"] = "Novas Cartas Espectrais", + + ["unstb_config_header_joker_settings"] = "Curingas", + + --Extra Flavour Badge Text + ["k_tarot_exclaim"] = "Tarô!?", + + ["k_blueprint_l_compatible"] = "Esquerda: compatível", + ["k_blueprint_l_incompatible"] = "Esquerda: incompatível", + ["k_blueprint_r_compatible"] = "Direita: compatível", + ["k_blueprint_r_incompatible"] = "Direita: incompatível", + + --Auxiliary Card stuff + --Category Name + ["k_auxiliary"] = "Auxiliar", + + --Gallery Button + ["b_auxiliary_cards"] = "Cartas Auxiliares", + + --Booster Packs + ["k_booster_group_p_unstb_aux_1"] = "Pacote de Expansão", + ["k_booster_group_p_unstb_aux_2"] = "Pacote de Expansão", + ["k_booster_group_p_unstb_aux_mega"] = "Pacote de Expansão", + ["k_booster_group_p_unstb_aux_jumbo"] = "Pacote de Expansão", + + ["k_booster_group_p_unstb_prem_1"] = "Pacote Padrão Premium", + ["k_booster_group_p_unstb_prem_2"] = "Pacote Padrão Premium", + ["k_booster_group_p_unstb_prem_mega"] = "Pacote Padrão Premium", + ["k_booster_group_p_unstb_prem_jumbo"] = "Pacote Padrão Premium", + }, + ["ranks"] = { + + ["unstb_21"] = "21", + ["unstb_???"] = "???", + + ["unstb_0"] = "0", + ["unstb_1"] = "1", + + ["unstb_0.5"] = "Meio", + ["unstb_r2"] = "Raiz de 2", + ["unstb_e"] = "e", + ["unstb_Pi"] = "Pi", + + ["unstb_11"] = "11", + ["unstb_12"] = "12", + ["unstb_13"] = "13", + ["unstb_25"] = "25", + ["unstb_161"] = "161", + + }, + ["labels"] = { + + ["unstb_spades_seal"] = "Selo de Espadas", + ["unstb_hearts_seal"] = "Selo de Copas", + ["unstb_clubs_seal"] = "Selo de Paus", + ["unstb_diamonds_seal"] = "Selo de Ouros", + + ["unstb_face_seal"] = "Selo de Realeza", + ["unstb_heal_seal"] = "Selo de Cura", + + }, + }, + ["descriptions"] = { + + ["Back"] = { + + ["b_unstb_utility"] = { + ["name"] = "Baralho Utilitário", + ["text"] = { + "Comece a tentativa com o", + "cupom {C:auxiliary}Loja de Ferragens{}", + "e {C:attention}1{} cópia de {C:auxiliary}A Interrogação{},", + "{C:auxiliary}Cartas Auxiliares{} podem aparecer", + "na {C:attention}loja{} mais frequentemente", + }, + }, + + ["b_unstb_lowkey"] = { + ["name"] = "Baralho Discreto", + ["text"] = { + "Comece a tentativa com apenas", + "cartas de {C:attention}classes 0-5{}", + "e todas as {C:attention}classes", + "{C:attention}decimais{} entre elas", + }, + }, + + }, + + ["Sleeve"] = { + + ["sleeve_unstb_utility"] = { + ["name"] = "Capa Utilitária", + ["text"] = { + "Comece a tentativa com o", + "cupom {C:auxiliary}Loja de Ferragens{}", + "e {C:attention}1{} cópia de {C:auxiliary}A Interrogação{},", + "{C:auxiliary}Cartas Auxiliares{} podem aparecer", + "na {C:attention}loja{} mais frequentemente", + }, + }, + + ["sleeve_unstb_utility_alt"] = { + ["name"] = "Capa Utilitária", + ["text"] = { + "Comece a tentativa com o", + "cupom {C:auxiliary}Ferramentas Robustas{}", + "e um {C:attenttion}Amostra Grátis{} {C:dark_edition}Negativo{}", + }, + }, + + ["sleeve_unstb_lowkey"] = { + ["name"] = "Capa Discreta", + ["text"] = { + "Comece a tentativa com apenas", + "cartas de {C:attention}classes 0-5{}", + "e todas as {C:attention}classes", + "{C:attention}decimais{} entre elas", + }, + }, + + ["sleeve_unstb_lowkey_alt"] = { + ["name"] = "Capa Discreta", + ["text"] = { + "Cartas com classe maior que ", + "{C:attention}5{} não aparecem na tentativa", + }, + }, + + }, + + ["Other"] = { + + --Playing Card Ability Text + + ["no_chip"] = { + ["text"] = { + "Sem Fichas", + }, + }, + ["decimal_rank_ability"] = { + ["text"] = { + "Pode fucionar como uma {C:attention}classe diferente{}", + "quando a mão for uma {C:purple}Sequência{}", + "{C:inactive}[#1#, #2#, #3#]{}", + }, + }, + ["decimal_rank_ability_2"] = { + ["text"] = { + "Pode fucionar como uma {C:attention}classe diferente{}", + "quando a mão for uma {C:purple}Sequência{}", + "{C:inactive}[#1#, #2#]{}", + }, + }, + + -- Other Mechanics + + ["upgrade_edition"] = { + ["name"] = "Aprimorar Edição", + ["text"] = { + "Mude a {C:attention}edição{} da carta", + "em {C:attention}1{} passo na ordem", + "{C:inactive}(Nenhuma){} -> {C:dark_edition}Foil{} ->", + "{C:dark_edition}Holograma{} -> {C:dark_edition}Policromática{}", + }, + }, + + ["disenhancement"] = { + ["name"] = "Desaprimoramento", + ["text"] = { + "Aprimoramento que possui", + "efeitos negativos", + "{C:attention}(Radiação, Contágio, Veneno){}", + }, + }, + + --Extra Tooltip for description that does not lend itself well in tooltip form + + ["acorn_tooltip"] = { + ["name"] = "Carta Bolota", + ["text"] = { + "Ganhe {C:chips}o dobro{} de fichas", + "quando esta carta estiver na mão,", + "não dá fichas quando contabilizda", + }, + }, + ["resource_tooltip"] = { + ["name"] = "Carta de Recurso", + ["text"] = { + "Não tem classe, sempre é contabilizada.", + "Ganhe {X:mult,C:white}X5{} Multi", + "se a mão jogada tiver pelo menos", + "uma carta com naipe de {C:attention}(Corresponding Suit){}", + "{C:red}Destruída depois de contabilizada{}", + "{C:inactive}(Não conta com outras Cartas de Recurso){}", + }, + }, + ["poison_tooltip"] = { + ["name"] = "Carta Venenosa", + ["text"] = { + "Dá um {C:red}valor negativo{} de fichas,", + "{C:attention}Cartas Venenosas{} descartadas", + "voltam para o baralho", + }, + }, + + --Seal + + ["suit_seal"] = { + ["name"] = "Selo de Naipe", + ["label"] = "Selo de Naipe", + ["text"] = { + "Faz esta carta também", + "contar como {C:attention}(Naipe respectivo){}", + }, + }, + ["unstb_spades_seal"] = { + ["name"] = "Selo de Espadas", + ["label"] = "Selo de Espadas", + ["text"] = { + "Esta carta também", + "conta como {V:1}#1#{}", + }, + }, + ["unstb_hearts_seal"] = { + ["name"] = "Selo de Copas", + ["label"] = "Selo de Copas", + ["text"] = { + "Esta carta também", + "conta como {V:1}#1#{}", + }, + }, + ["unstb_clubs_seal"] = { + ["name"] = "Selo de Paus", + ["label"] = "Selo de Paus", + ["text"] = { + "Esta carta também", + "conta como {V:1}#1#{}", + }, + }, + ["unstb_diamonds_seal"] = { + ["name"] = "Selo de Ouros", + ["label"] = "Selo de Ouros", + ["text"] = { + "Esta carta também", + "conta como {V:1}#1#{}", + }, + }, + ["unstb_face_seal"] = { + ["name"] = "Selo Real", + ["label"] = "Selo Real", + ["text"] = { + "Esta carta também conta", + "como uma carta de {C:attention}realeza{}", + }, + }, + ["unstb_heal_seal"] = { + ["name"] = "Selo de Cura", + ["label"] = "Selo de Cura", + ["text"] = { + "Remove o {C:red}Desaprimoramento{} de uma", + "carta aleatória na mão quando pontuada.", + "Esta carta não pode ser {C:red}Desaprimorada{}.", + }, + }, + + --Auxiliary Card-related stuff + + ["undiscovered_auxiliary"] = { + ["name"] = "Não Descoberto", + ["text"] = { + "Compre ou use", + "esta carta em uma", + "tentativa sem código", + "para descobrir o que ela faz", + }, + }, + + ["p_unstb_aux_1"] = { + ["group_name"] = "Pacote de Expansão", + ["name"] = "Pacote de Expansão", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2#{} cartas {C:auxiliary}Auxiliares{} para", + "adicionar à sua área de consumíveis", + }, + }, + ["p_unstb_aux_2"] = { + ["group_name"] = "Pacote de Expansão", + ["name"] = "Pacote de Expansão", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2#{} cartas {C:auxiliary}Auxiliares{} para", + "adicionar à sua área de consumíveis", + }, + }, + ["p_unstb_aux_mega"] = { + ["group_name"] = "Pacote de Expansão", + ["name"] = "Pacote de Expansão Mega", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2#{} cartas {C:auxiliary}Auxiliares{} para", + "adicionar à sua área de consumíveis", + }, + }, + ["p_unstb_aux_jumbo"] = { + ["group_name"] = "Pacote de Expansão", + ["name"] = "Pacote de Expansão Jumbo", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2#{} cartas {C:auxiliary}Auxiliares{} para", + "adicionar à sua área de consumíveis", + }, + }, + + ["p_unstb_prem_1"] = { + ["group_name"] = "Pacote Padrão Premium", + ["name"] = "Pacote Padrão Premium", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2# cartas de Jogo Aprimoradas{} com", + "{C:attention}classe especial{} para adicionar ao seu baralho" + }, + }, + ["p_unstb_prem_2"] = { + ["group_name"] = "Pacote Padrão Premium", + ["name"] = "Pacote Padrão Premium", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2# cartas de Jogo Aprimoradas{} com", + "{C:attention}classe especial{} para adicionar ao seu baralho" + }, + }, + ["p_unstb_prem_mega"] = { + ["group_name"] = "Pacote Padrão Premium", + ["name"] = "Pacote Padrão Premium Mega", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2# cartas de Jogo Aprimoradas{} com", + "{C:attention}classe especial{} para adicionar ao seu baralho" + }, + }, + ["p_unstb_prem_jumbo"] = { + ["group_name"] = "Pacote Padrão Premium", + ["name"] = "Pacote Padrão Premium Jumbo", + ["text"] = { + "Escolha {C:attention}#1#{} de até", + "{C:attention}#2# cartas de Jogo Aprimoradas{} com", + "{C:attention}classe especial{} para adicionar ao seu baralho" + }, + }, + }, + + ["Spectral"] = { + + --Fallback Spectral Cards + + ["c_unstb_spc_elixir"] = { + ["name"] = "Elixir da Vida", + ["text"] = { + "Cure todas as {C:red}Cartas Desaprimoradas{} no baralho", + "Reduza o dinheiro pela metade {C:inactive}(Arrendondando para cima){}", + }, + }, + ["c_unstb_spc_vessel"] = { + ["name"] = "Receptáulo", + ["text"] = { + "Selecione até {C:attention}#1#{} cartas,", + "destrua a carta mais à {C:attention}esquerda{}", + "e dê o {C:red}Selo de Naipe{} correspondente", + "para todas as cartas {C:attention}restantes{}", + "{C:inactive}(Arraste para reorganizar){}", + }, + }, + ["c_unstb_spc_conferment"] = { + ["name"] = "Concessão", + ["text"] = { + "Adicione um {C:attention}Selo Real{}", + "a {C:attention}#1#{} cartas aleatórias", + "na sua mão,", + "{C:red}-$#2#{} {C:inactive}(Sem limite de débito){}", + }, + }, + ["c_unstb_spc_amnesia"] = { + ["name"] = "Amnésia", + ["text"] = { + "Converte {C:attention}#1#{} cartas", + "aleatórias na sua mão", + "para {C:attention}Classe 0{}", + }, + }, + ["c_unstb_spc_altar"] = { + ["name"] = "Altar", + ["text"] = { + "Destrua {C:attention}#1#{} cartas", + "aleatórias na sua mão,", + "adicione {C:attention}#2#{} {C:attention}21s{} {C:attention}Aprimorados{}", + "para a sua mão", + }, + }, + ["c_unstb_spc_contract"] = { + ["name"] = "Contrato Diabólico", + ["text"] = { + "{C:purple}Aprimora {C:attention}#1#{} carta", + "aleatória na sua mão", + "{C:red}Desaprimora {C:attention}#2#{} cartas", + "aleatórias na sua mão", + }, + }, + + --Other New Spectral Cards + + ["c_unstb_spc_poltergeist"] = { + ["name"] = "Poltergeist", + ["text"] = { + "Embaralhe todas as", + "{C:dark_edition}Edições{} dos seus Curingas", + }, + }, + ["c_unstb_spc_projection"] = { + ["name"] = "Projeção Astral", + ["text"] = { + "Troque {C:dark_edition}edições{} entre", + "o {C:attention}Curinga Selecionado{}", + "e o que está {C:attention}à sua direita{}", + "{C:green}#1# de #2#{} chance de {C:red}destruir{}", + "um dos Curingas", + }, + }, + ["c_unstb_spc_siphon"] = { + ["name"] = "Sifão", + ["text"] = { + "Destrua {C:attention}1 Curinga{} selecionado", + "com uma {C:dark_edition}edição{}", + "Dá essa mesma {C:dark_edition}edição{} para", + "{C:attention}#1#{} cartas aleatórias na sua mão", + "{C:inactive}(Exceto{} {C:dark_edition}Negativo{}{C:inactive}){}", + }, + }, + }, + + ["Tarot"] = { + + --Enhancement Conversion Tarot + + ["c_unstb_trt_time"] = { + ["name"] = "Tempo", + ["text"] = { + "Aprimora {C:attention}#1#{} ", + "cartas selecionadas para", + "{C:attention}Cartas Retrô{}", + }, + }, + ["c_unstb_trt_acorn"] = { + ["name"] = "A Bolota", + ["text"] = { + "Aprimora {C:attention}#1#{} ", + "carta selecionada para", + "{C:attention}Carta de Bolota{}", + }, + }, + ["c_unstb_trt_greed"] = { + ["name"] = "Ganância", + ["text"] = { + "Aprimora {C:attention}#1#{} ", + "cartas selecionadas para", + "{C:attention}Cartas Promocionais{}", + }, + }, + + --Rank-addition Tarot + + ["c_unstb_trt_half"] = { + ["name"] = "A Metade", + ["text"] = { + "Divide {C:attention}1{} carta de {C:attention}classe inteira{}", + "em {C:attention}2{} cartas com {C:attention}metade da classe{}.", + "Se a Classe inicial for {C:attention}ímpar{}, também adicione {C:attention}1{}", + "carta {C:attention}classe 0.5{} com o mesmo {C:attention}Aprimoramento{}.", + }, + }, + ["c_unstb_trt_knowledge"] = { + ["name"] = "Sabedoria", + ["text"] = { + "Adiciona uma carta de {C:attention}classe decimal{}", + "aleatória do naipe de {C:attention}1{} carta selecionada pra mão", + }, + }, + + }, + + ["Enhanced"] = { + + --Standard Enhancement + + ["m_unstb_vintage"] = { + ["name"] = "Carta Retrô", + ["text"] = { + "Ganha {C:chips}+#1#{} fichas extra quando pontuada", + "{C:green}#2# de #3#{} chance de se autodestruir", + "{C:inactive}Chance aumenta em {C:green}+#4#{C:inactive} quando pontuada{}", + }, + }, + ["m_unstb_acorn"] = { + ["name"] = "Carta de Bolota", + ["text"] = { + "Dá {C:chips}+#1#{} fichas quando estiver na mão,", + "Não dá fichas quando pontuada", + }, + }, + ["m_unstb_promo"] = { + ["name"] = "Carta Promocional", + ["text"] = { + "Dá {C:gold}$#1#{} enquanto esta carta é contida na mão", + "Ganha {C:gold}+$#2#{} e tem {C:green}#3# de #4#{}", + "chance de se autodestruir quando pontuada", + }, + }, + + --Special Joker-centric Enhancement + + ["m_unstb_resource"] = { + ["name"] = "Carta de Recurso", + ["text"] = { + "Não tem classe, sempre pontua.", + "Dá {X:mult,C:white}X#1#{} Multi se a", + "mão jogada tiver ao menos uma", + "carta com o naipe de {V:1}#2#{}", + "{C:red}Se autodestrói após pontuada{}", + "{C:inactive}(Outras Cartas de Recursos não contam){}", + }, + }, + ["m_unstb_slop"] = { + ["name"] = "Carta Malfeita", + ["text"] = { + "{C:chips}+#1#{} chips", + "Always scored", + "Random suit and rank after scored", + "Current: {C:attention}#3#{} of {V:1}#2#{}", + }, + }, + + --Unhancements + + ["m_unstb_radioactive"] = { + ["name"] = "Carta Radioativa", + ["text"] = { + "{C:chips}+#1#{} Fichas, sem classe ou naipe", + "Quando jogada, {C:green}#2# de #3#{} chance de", + "converter outras cartas jogadas para {C:attention}Cartas Radioativas{}", + "", + "Quando contida na mão, {C:green}#2# de #4#{} chance de dar", + "{X:mult,C:white}X#5#{} Multi, caso contrário {X:mult,C:white}X#6#{} Multi", + }, + }, + ["m_unstb_biohazard"] = { + ["name"] = "Carta Contagiosa", + ["text"] = { + "{X:mult,C:white}X#1#{} Multi, sem classe ou naipe", + "Perca {C:red}-$#2#{} enquanto esta carta é contida na mão", + "Quando descartada, {C:green}#3# de #4#{} chance de converter", + "uma carta no baralho para {C:attention}Carta Contagiosa{}", + }, + }, + ["m_unstb_poison"] = { + ["name"] = "Carta Venenosa", + ["text"] = { + "{C:red}-#1#{} Fichas", + "Volta para o baralho", + "quando descartada", + }, + }, + }, + + ["Auxiliary"] = { + + ["c_unstb_aux_spades"] = { + ["name"] = "Essência de Espadas", + ["text"] = { + "Adicione um {V:1}#2#{} para", + "{C:attention}#1#{} cartas selecionadas", + "na sua mão", + }, + }, + ["c_unstb_aux_hearts"] = { + ["name"] = "Essência de Copas", + ["text"] = { + "Adicione um {V:1}#2#{} para", + "{C:attention}#1#{} cartas selecionadas", + "na sua mão", + }, + }, + ["c_unstb_aux_clubs"] = { + ["name"] = "Essência de Paus", + ["text"] = { + "Adicione um {V:1}#2#{} para", + "{C:attention}#1#{} cartas selecionadas", + "na sua mão", + }, + }, + ["c_unstb_aux_diamonds"] = { + ["name"] = "Essência de Ouros", + ["text"] = { + "Adicione um {V:1}#2#{} para", + "{C:attention}#1#{} cartas selecionadas", + "na sua mão", + }, + }, + + ["c_unstb_aux_face"] = { + ["name"] = "Possessão", + ["text"] = { + "Adicione um {C:attention}Selo Real{} para", + "{C:attention}#1#{} cartas selecionadas", + "na sua mão", + }, + }, + + ["c_unstb_aux_plus_two"] = { + ["name"] = "+2", + ["text"] = { + "Cria {C:attention}#1#{} cartas na", + "na sua mão com uma {C:attention}classe{}", + "aleatória e o mesmo {C:attention}naipe{}", + "da carta selecionada", + }, + }, + ["c_unstb_aux_plus_four_wild"] = { + ["name"] = "+4", + ["text"] = { + "Cria {C:attention}#1#{} cartas na", + "na sua mão com um {C:attention}naipe{}", + "aleatório e a mesma {C:attention}classe{}", + "da carta selecionada", + }, + }, + + ["c_unstb_aux_inst_disc"] = { + ["name"] = "O Passado", + ["text"] = { + "{C:red}+#1#{} descartes nesta rodada", + }, + }, + ["c_unstb_aux_inst_hsize"] = { + ["name"] = "O Presente", + ["text"] = { + "{C:attention}+#1#{} tamanho de mão nesta rodada", + }, + }, + ["c_unstb_aux_inst_hand"] = { + ["name"] = "O Futuro", + ["text"] = { + "{C:blue}+#1#{} mãos nesta rodada", + }, + }, + + ["c_unstb_aux_seal_move"] = { + ["name"] = "Troca de Selos", + ["text"] = { + "Troque {C:attention}selos{} entre", + "{C:attention}2{} cartas selecionadas", + }, + }, + + ["c_unstb_aux_conv_1"] = { + ["name"] = "Todos-Por-Um", + ["text"] = { + "Converte a classe de", + "até {C:attention}#1#{} scartas", + "selecionadas para {C:attention}1{}", + }, + }, + + ["c_unstb_aux_21"] = { + ["name"] = "O Vinte-e-Um", + ["text"] = { + "Selecione até {C:attention}#1#{} cartas", + "cujas classes somam {C:attention}21 ou mais{}.", + "Destrói todas as cartas selecionadas e cria uma", + "carta {C:attention}classe 21{} de um naipe aleatório", + }, + }, + + ["c_unstb_aux_upgrade"] = { + ["name"] = "Pata do Macaco", + ["text"] = { + "{C:purple}Aprimora{} a {C:attention}edição{} de {C:attention}1{} carta selecionada ", + "Converte cartas adjacentes para {C:attention}Cartas Venenosas{}", + "{C:inactive}(Dá a volta nas bordas da mão){}", + }, + }, + + ["c_unstb_aux_heal"] = { + ["name"] = "Guardião da Cura", + ["text"] = { + "Addicione um {C:attention}Selo de Cura{}", + "para {C:attention}#1#{} carta", + "selecionada na sua mão", + }, + }, + ["c_unstb_aux_heal_hand"] = { + ["name"] = "Aura de Cura", + ["text"] = { + "Cure todas as", + "{C:red}Cartas Desaprimoradas{}", + "na sua mão", + }, + }, + + ["c_unstb_aux_lottery"] = { + ["name"] = "Loteria", + ["text"] = { + "{C:green}#1# de #2#{} chance", + "de ganhar {C:gold}$#3#{}", + }, + }, + + ["c_unstb_aux_blank"] = { + ["name"] = "Carta em Branco", + ["text"] = { + "Não faz nada..?", + }, + }, + ["c_unstb_aux_dark_matter"] = { + ["name"] = "Matéria Escura", + ["text"] = { + "Permanentemente ganhe", + "{C:dark_edition}+#1#{} espaço de Curinga", + "{C:red}Não pode ser negativa", + }, + }, + ["c_unstb_aux_dark_matter_n"] = { + ["name"] = "Matéria Escura..?", + ["text"] = { + "Permanentemente ganhe", + "{C:red}-#1#{} espaço de Curinga", + }, + }, + + ["c_unstb_aux_random"] = { + ["name"] = "A Interrogação", + ["text"] = { + "Cria até {C:attention}#1#{} cartas", + "{C:auxiliary}Auxiliares{} aleatórias", + "{C:inactive}(Deve ter espaço)", + }, + }, + }, + + ["Voucher"] = { + + ["v_unstb_aux1"] = { + ["name"] = "Loja de Ferragens", + ["text"] = { + "Cartas {C:auxiliary}Auxiliares{}", + "podem ser compradas", + "na {C:attention}loja{}", + }, + }, + + ["v_unstb_aux2"] = { + ["name"] = "Ferramentas Robustas", + ["text"] = { + "Cartas {C:auxiliary}Auxiliares{} nos", + "{C:attention}Pacotes de Expansão{} podem", + "ser {C:dark_edition}Negativas{}, às vezes", + }, + }, + + }, + + ["Joker"] = { + --Basic Common Jokers + + ["j_unstb_lunar_calendar"] = { + ["name"] = "Calendário Lunar", + ["text"] = { + "Cartas jogadas com", + "o naipe de {V:1}#1#{} têm", + "{C:green}#2# de #3#{} chance de criar uma", + "carta de {C:planet}Planeta{} aleatória quando pontuadas", + "{C:inactive}(Deve ter espaço)", + "{s:0.8}O naipe muda no fim da rodada", + }, + }, + + ["j_unstb_dragon_hoard"] = { + ["name"] = "Tesouro do Dragão", + ["text"] = { + "Dá {C:mult}+#1#{} Multi para", + "cada {C:attention}#2#{} {C:attention}consumíveis{}", + "na sua {C:attention}área de consumíveis{}", + "{C:inactive}(No momento, {C:mult}+#3#{C:inactive} Multi)", + }, + }, + + ["j_unstb_card_dealer"] = { + ["name"] = "Crupiê", + ["text"] = { + "{C:chips}+#1#{} Fichas por carta", + "{C:attention}jogada{} nesta tentativa", + "{C:attention}Reseta{} no fim da rodada", + "{C:inactive}(No momento, {C:chips}+#2#{C:inactive} Fichas)", + }, + }, + + ["j_unstb_match_three"] = { + ["name"] = "Formar 3", + ["text"] = { + "{C:mult}+#1#{} Multi se a mão jogada contém", + "{C:attention}#2#{} ou mais cartas adjacentes", + "com o {C:attention}mesmo naipe base{}", + }, + }, + + --Extra Uncommons + + ["j_unstb_furry_joker"] = { + ["name"] = "Curinga Furry", + ["text"] = { + "Cartas {C:attention}Naipe Curinga{} jogadas têm", + "{C:green}#1# de #2#{} chance de", + "virarem {C:dark_edition}Policromáticas{} quando pontuadas", + }, + }, + + ["j_unstb_luxurious_handbag"] = { + ["name"] = "Bolsa Luxuosa", + ["text"] = { + "{C:attention}+#1#{} espaço de consumível", + "{C:red}Aumenta o preço{} de tudo na", + "{C:attention}loja{} (exceto atualizações) em {C:gold}$#2#{}", + }, + }, + + ["j_unstb_portal"] = { + ["name"] = "Portal", + ["text"] = { + "{C:attention}Ázes{} podem ser usados", + "no meio de uma {C:purple}Sequência{}", + "{C:inactive}(Ex: 3, 2, Ás, Rei, Rainha)", + }, + }, + + --Selo de Naipe Support + + ["j_unstb_vainglorious_joker"] = { + ["name"] = "Curinga Vanglorioso", + ["text"] = { + "Cartas jogadas com um", + "{C:attention}Selo de Naipe{} dão", + "{C:attention}+#1#{} Multi quando pontuadas", + }, + }, + + ["j_unstb_acedia_joker"] = { + ["name"] = "Curinga Apático", + ["text"] = { + "Cartas jogadas com um", + "{C:attention}Selo de Naipe{} da mesma", + "{C:purple}categoria{} do naipe da carta", + "dão {C:attention}+#1#{} Multi quando pontuadas", + }, + }, + + ["j_unstb_cinnabar"] = { + ["name"] = "Cinábrio", + ["text"] = { + "Cartas jogadas com um {C:attention}Selo de Naipe{} têm", + "{C:green}#1# de #2#{} chance de criar a carta", + "{C:auxiliary}Auxiliar de Selo{} respectiva quando pontuada", + "{C:inactive}(Deve ter espaço)", + }, + }, + + --Auxiliary Support + + ["j_unstb_free_trial"] = { + ["name"] = "Amostra Grátis", + ["text"] = { + "{C:green}#1# de #2#{} chance de criar", + "uma carta {C:auxiliary}Auxiliar{} {C:dark_edition}Negativa{} aleatória", + "quando uma carta {C:auxiliary}Auxiliar{} for usada", + }, + }, + + ["j_unstb_extended_warranty"] = { + ["name"] = "Garantia Extendida", + ["text"] = { + "Cartas {C:auxiliary}Auxiliares{} na", + "sua área de {C:attention}consumíveis{}", + "dão {X:mult,C:white}X#1#{} Multi", + }, + }, + + ["j_unstb_technician"] = { + ["name"] = "Técnico", + ["text"] = { + "{C:chips}+#1#{} Fichas por", + "Carta {C:auxiliary}Auxiliar{}", + "usada nesta tentativa", + "{C:inactive}(No momento, {C:chips}+#2#{C:inactive} Fichas){}", + }, + }, + + ["j_unstb_season_pass"] = { + ["name"] = "Passe de Temporada", + ["text"] = { + "{C:green}#1# de #2#{} chance de criar", + "uma carta {C:auxiliary}Auxiliar{}", + "ao descartar uma carta de {C:attention}realeza{}", + "{C:inactive}(Deve ter espaço)", + }, + }, + + --Rank 21 + + ["j_unstb_black_jack"] = { + ["name"] = "Vinte e Um", + ["text"] = { + "Este Curinga ganha {C:chips}Fichas{} igual ao {C:attention}valor total{}", + "das classes da mão pontuada se for menor que {C:attention}#1#{}.", + "{C:red}Dobra as Fichas acumuladas{} se o {C:attention}valor total{} for exatamente {C:attention}#1#{}.", + "Reseta se o valor total for maior que {C:attention}#1#{}.", + "{C:inactive}(No momento, {C:chips}+#2#{C:inactive} Fichas)", + }, + }, + + ["j_unstb_what"] = { + ["name"] = "É o quê?", + ["text"] = { + "{C:attention}???{} jogados", + "dão {C:chips}+#1#{} Fichas e", + "{C:mult}+#2#{} Multi quando pontuadas", + }, + }, + + --Decimal Rank + + ["j_unstb_floating_point_error"] = { + ["name"] = "Erro de Ponto Flutuante", + ["text"] = { + "Toda carta de {C:attention}Classe Decimal{} jogada", + "permanentemente ganha {C:chips}fichas extra{}", + "iguais à sua {C:attention}classe{}", + }, + }, + + ["j_unstb_academic_journal"] = { + ["name"] = "Revista Acadêmica", + ["text"] = { + "Adiciona uma carta {C:attention}Aprimorada{} aleatória", + "com uma {C:attention}classe decimal{} ao baralho se", + "a mão pontuada não tem uma carta de {C:attention}realeza{}", + "{C:inactive}(Uma vez por rodada. No momento, {C:red}#1#/#2#{C:inactive})", + }, + }, + + ["j_unstb_engineer"] = { + ["name"] = "Engenheiro", + ["text"] = { + "cartas com {C:attention}classes decimais{} contam como", + "a maior {C:attention}classe inteira{} mais próxima quando", + "jogadas, exceto quando a mão for {C:purple}SeqUência{}", + "{C:inactive}(EX: 3.14 -> 4)", + }, + }, + + ["j_unstb_thesis_proposal"] = { + ["name"] = "Proposta de Tese", + ["text"] = { + "Reativa {C:attention}#1#{} vezes adicionais", + "todas as cartas de", + "{C:attention}classe decimal{} jogadas" + }, + }, + + ["j_unstb_rainbow_flag"] = { + ["name"] = "Bandeira Arco-Íris", + ["text"] = { + "Se uma mão que contém {C:purple}Sequência{} tem", + "ao menos 1 carta de {C:attention}classe decimal{},", + "{C:green}#1# de #2#{} chance de converter", + "uma das cartas para {C:dark_edition}Policromático{}", + }, + }, + + --Binary Ranks + + ["j_unstb_dummy_data"] = { + ["name"] = "Dados Fictícios", + ["text"] = { + "Para cada carta {C:attention}não-pontuada{}", + "{C:green}#1# de #2#{} chance de", + "converter a sua classe para {C:attention}0{}", + "(Mantém a quantidade de Fichas)", + }, + }, + + ["j_unstb_micro_sd_card"] = { + ["name"] = "Cartão de Memória", + ["text"] = { + "Se este Curinga estiver na ponta da {C:attention}esquerda{},", + "pega todas as Fichas de {C:attention}cartas numeradas{} base", + "{C:attention}descartadas{} e converte a classe dessas cartas para {C:attention}0{}", + "{C:green}#1# de #2#{} chance de se autodestruir ao fim da rodada", + "A chance aumenta na quantidade de Fichas salvas", + "{C:inactive}(No momento, {C:chips}+#4#{C:inactive} Fichas)", + "{C:inactive}Status: {V:1}#5#{}", + }, + }, + + ["j_unstb_social_experiment"] = { + ["name"] = "Experimento Social", + ["text"] = { + "Para cada {C:attention}carta numerada{} pontuada,", + "dobre suas Fichas, dê para a próxima carta", + "e converta sua {C:attention}classe{} para {C:attention}0{}", + }, + }, + + ["j_unstb_power_of_one"] = { + ["name"] = "Um-Por-Todos", + ["text"] = { + "Dá {C:mult}+#1#{} Multi por cada", + "{C:attention}1{} no seu {C:attention}baralho completo{}", + "{C:inactive}(No momento, {C:mult}+#2#{C:inactive})", + }, + }, + + ["j_unstb_binary_number"] = { + ["name"] = "Número Binário", + ["text"] = { + "Se a mão jogada contém {C:attention}4{} cartas ou menos", + "e só tem cartas de classe {C:attention}0{} e {C:attention}1{},", + "crie uma carta de {C:attention}classe{} igual ao", + "ao número binário representado pela mão", + "({C:red}Ex:{} {C:attention}1001{} -> {C:attention}9{})", + "{C:inactive}[Jack, Queen, King, Ace são 11-14, respectivamente]{}", + }, + }, + + --Basegame Enhancement + + ["j_unstb_quintuplets"] = { + ["name"] = "Quintigêmeos", + ["text"] = { + "Se a mão jogada contém um", + "{C:attention}Five of a Kind{}", + "cria uma {C:dark_edition}Marca Negativa{}", + }, + }, + + --Edition Line + + ["j_unstb_graphic_card"] = { + ["name"] = "Placa de Vídeo", + ["text"] = { + "Cria uma {C:attention}Marca Dupla{}", + "no fim da rodada se", + "{C:attention}#1#{} ou mais cartas com", + "{C:dark_edition}edições{} pontuaram neste round", + "{C:inactive}(No momento, {C:attention}#2# {C:inactive}cartas)", + }, + }, + + ["j_unstb_connoiseur"] = { + ["name"] = "Conhecedor", + ["text"] = { + "Reative todas as cartas jogadas", + "com {C:dark_edition}edições{}", + }, + }, + + ["j_unstb_jeweler"] = { + ["name"] = "Joalheiro", + ["text"] = { + "{C:green}#1# de #2#{} chance de reduzir", + "o nível da {C:attention}mão de pôquer{}", + "e {C:purple}aprimorar{} a edição", + "de todas as cartas pontuadas", + }, + }, + + --Slop + + ["j_unstb_joker_diffusion"] = { + ["name"] = "Difusão Instável", + ["text"] = { + "Converte {C:attention}#1#{} carta {C:attention}não-aprimorada{} aleatória", + "na mão para {C:attention}Carta Malfeita{} toda vez que", + "uma mão é jogada com uma carta {C:attention}não-pontuada{}", + }, + }, + + ["j_unstb_nonfungible_joker"] = { + ["name"] = "Curinga Não Fungível", + ["text"] = { + "Se uma mão descartada contém", + "apenas {C:attention}#1# Carta Malfeita{}, destrua-a e", + "e receba um valor monetário aleatório {C:inactive}(Máx. {C:gold}$#2# {C:inactive})", + "{s:0.7,C:red}Valor máximo e preço de venda caem no fim da rodada", + }, + }, + + ["j_unstb_prompt"] = { + ["name"] = "Prompt", + ["text"] = { + "{C:attention}Cartas Malfeitas{} pontuadas têm", + "{C:attention}classe{} e {C:attention}naipe{} de", + "outras cartas {C:attention}Não-Malfeitas{}", + "ao invés de aleatórios (se possível)", + }, + }, + + ["j_unstb_uninterested_primate"] = { + ["name"] = "Primata Desinteressado", + ["text"] = { + "Ganha {C:chips}+#1#{} Fichas a cada {C:attention}#2# {C:inactive}[#6#]", + "{C:attention}Cartas Malfeitas{} pontuadas.", + "{C:green}#3# de #4#{} chance desta carta ser", + "destruída no fim da rodada", + "{C:inactive}(No momento, {C:chips}+#5# {C:inactive}Fichas)", + }, + }, + + ["j_unstb_lethargic_lion"] = { + ["name"] = "Leão Letárgico", + ["text"] = { + "Ganha {X:mult,C:white}X#1#{} Multi a cada {C:attention}#2# {C:inactive}[#6#]", + "{C:attention}Cartas Malfeitas{} pontuadas.", + "{C:green}#3# de #4#{} chance desta carta ser", + "destruída no fim da rodada", + "{C:inactive}(No momento, {X:mult,C:white}+#5# {C:inactive}Multi)", + }, + }, + + --New Enhancement Support + + ["j_unstb_vintage_joker"] = { + ["name"] = "Curinga Retrô", + ["text"] = { + "{C:attention}Cartas Retrô{} pontuadas têm", + "{C:green}#1# de #2#{} chance de resetar", + "sua chance de destruição", + }, + }, + + ["j_unstb_rules_errata"] = { + ["name"] = "Lista de Regras", + ["text"] = { + "{C:attention}Cartas de Bolota{} dão {C:attention}metade{} do valor", + "de sua {C:attention}classe{} em Multi ao pontuar", + }, + }, + + ["j_unstb_auction_winner"] = { + ["name"] = "Vencedor do Leilão", + ["text"] = { + "Se uma {C:attention}Carta Promocional{} for", + "destruída ao pontuar, este Curinga", + "ganha {C:gold}dinheiro{} igual ao", + "{C:gold}dinheiro{} que qquela carta dava", + "antes de sua destruição", + }, + }, + + ["j_unstb_joker_island"] = { + ["name"] = "Sobrevivência na Ilha", + ["text"] = { + "Para cada carta pontuada", + "com {C:attention}classe{} {C:attention}#1#{}, {C:green}#2# de #3#{} chance", + "de adicionar uma {C:attention}Carta de Recurso{}", + "do mesmo naipe ao baralho", + "{s:0.8}Classe muda no dim da rodada", + }, + }, + + --Unhancement Jokers + + ["j_unstb_kaiju"] = { + ["name"] = "Kaiju", + ["text"] = { + "{C:dark_edition}+#1#{} espaço de Curinga", + "Converte uma carta aleatória", + "para {C:attention}Carta Radioativa{} quando", + "a primeira mão é retirada", + }, + }, + + ["j_unstb_poison_the_well"] = { + ["name"] = "Envenenar o Poço", + ["text"] = { + "{C:red}+#1#{} Descartes", + "Converte uma carta aleatória", + "em cada mão descartada", + "para {C:attention}Carta Venenosa{}", + }, + }, + + ["j_unstb_petri_dish"] = { + ["name"] = "Placa de Petri", + ["text"] = { + "{C:blue}+#1#{} Mãos", + "{C:green}#2# de #3#{} chance de converter", + "uma carta jogada aleatória", + "para {C:attention}Carta Contagiosa{}", + }, + }, + + --Unhancement Support + + ["j_unstb_geiger_counter"] = { + ["name"] = "Contador Geiger", + ["text"] = { + "Dá {C:mult}+#1#{} Multi por", + "cada {C:attention}Carta Radioativa{}", + "no seu {C:attention}baralho completo{}", + "{C:inactive}(No momento, {C:mult}+#2#{C:inactive} Multi)", + }, + }, + + ["j_unstb_strych_nine"] = { + ["name"] = "Estricnina", + ["text"] = { + "Dá {C:chips}+#1#{} Fichas por", + "cada {C:attention}Carta Venenosa{}", + "no seu {C:attention}baralho completo{}", + "{C:inactive}(No momento, {C:chips}+#2#{C:inactive} Fichas)", + }, + }, + + ["j_unstb_vaccination_card"] = { + ["name"] = "Cartão de Vacinação", + ["text"] = { + "Neutralize todos os efeitos passivos das ", + "{C:attention}Cartas Contagiosas{} e ganha {X:mult,C:white}X#1#{} Multi por cada", + "{C:attention}Carta Contagiosa{} contida na mão", + "{C:inactive}(No momento, {X:mult,C:white}X#2#{C:inactive} Multi)", + }, + }, + + --Shitposts + + ["j_unstb_joker2"] = { + ["name"] = "Joker2", + ["text"] = { + "{C:mult}+#1#{} Multi", + "{X:mult,C:white}X#2#{} Multi", + "{C:green}#3# in #4#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + + ["j_unstb_joker_stairs"] = { + ["name"] = "Escadaria do Curinga", + ["text"] = { + "Ganha {C:mult}+#1#{} Multi quando um Curinga", + "com {C:attention}\"Curinga\"{} no nome é", + "comprado diretamente da loja", + "{C:inactive}(No momento, {C:mult}+#2#{C:inactive} Multi){}", + }, + }, + + ["j_unstb_plagiarism"] = { + ["name"] = "Plágio", + ["text"] = { + "Copia a habilidade do", + "{C:attention}Curinga{} à direita", + "ou esquerda, aleatoriamente", + "{s:0.8}A direção muda a cada mão jogada", + }, + }, + + ["j_unstb_joker_throwing_card"] = { + ["name"] = "Carta Curinga de Arremesso", + ["text"] = { + "Reduza o tamanho do {C:attention}Blind{} em {C:attention}#1#%{}", + "{C:green}#2# de #3#{} chance de destruir", + "uma das cartas pontuadas", + "Aumenta a porcentagem em {C:attention}#4#%{}", + "se uma carta for destruída dessa maneira", + }, + }, + + --Face Card Jokers + + ["j_unstb_jackhammer"] = { + ["name"] = "Vale-te 5 Vezes", + ["text"] = { + "Se a {C:attention}mão jogada{} tem", + "apenas um {C:attention}Valete{},", + "reative-o {C:attention}#1#{} vezes adicionais", + "e em seguida {C:red}destrua-o{}", + }, + }, + + ["j_unstb_jack_of_all_trades"] = { + ["name"] = "Valete Trabalhador", + ["text"] = { + "{C:attention}Valetes{} jogados dão", + "{C:chips}+#1#{} Fichas, {C:mult}+#2#{} Multi, ", + "{X:mult,C:white}X#3#{} Multi, e {C:gold}$#4#", + }, + }, + + ["j_unstb_magic_trick_card"] = { + ["name"] = "Carta de Mágico", + ["text"] = { + "As {V:1}#1#{} pontuadas", + "viram {V:2}#2#{}", + "{s:0.8}Após Descartar, gira o curinga", + }, + }, + + ["j_unstb_queensland"] = { + ["name"] = "Queensland", + ["text"] = { + "{C:attention}Rainhas{} pontuadas adicionam", + "{C:attention}Cartas de Recurso{} do", + "naipe correspondente ao baralho", + "{C:inactive}({C:red}#1#{C:inactive} vezes por round, {C:red}restam #2#{C:inactive})", + }, + }, + + ["j_unstb_king_of_pop"] = { + ["name"] = "Rei do Pop", + ["text"] = { + "{C:attention}Reis Aprimorados{} têm", + "{C:green}#1# de #2#{} chance de serem", + "destruídos e criarem uma {C:dark_edition}Marca Dupla", + }, + }, + + ["j_unstb_prssj"] = { + ["name"] = "Curinga de Aço Policromático com Selo Vermelho", + ["text"] = { + "{C:green}#1# de #2#{} chance de {C:purple}aprimorar{} a edição de {C:attention}Reis{} pontuados", + "{C:green}#1# de #3#{} chance de {C:attention}reativar Reis{} pontuados", + "{C:green}#1# de #4#{} chance de {C:attention}Reis{} contidos na mão darem {X:mult,C:white}X#5#{} Multi", + }, + }, + + ["j_unstb_master_of_one"] = { + ["name"] = "Mestre de Um", + ["text"] = { + "Adicione uma carta {C:attention}classe 1 Aprimorada{}", + "ao baralho quando o {C:attention}Blind{} é selecionado", + }, + }, + + --Intermission + + ["j_unstb_spectre"] = { + ["name"] = "Espectro", + ["text"] = { + "{X:mult,C:white}X#1#{} Multi por carta {C:spectral}Espectral{}", + "usada nesta tentativa", + "{C:inactive}(No momento, {X:mult,C:white}X#2#{C:inactive} Multi){}", + }, + }, + + ["j_unstb_library_card"] = { + ["name"] = "Cartão de Biblioteca", + ["text"] = { + "Para cada {C:attention}nome de naipe{} no {C:attention}singular", + "em Inglês, este Curinga dá:", + "{C:chips}+#1#{} Fichas por {C:attention}Consoante", + "{C:mult}+#2#{} Multi por {C:attention}Vogal", + "{C:inactive}(Exemplo: Spade = {C:chips}+15{C:inactive} Fichas, {C:mult}+4{C:inactive} Multi)", + }, + }, + + ["j_unstb_collector_album"] = { + ["name"] = "Álbum de Coleção", + ["text"] = { + "Curingas com {C:attention}\"Carta\", \"Card\"{}", + "ou {C:attention}\"Cartão\"{} no seu nome dão", + "{C:chips}+#1#{} Fichas", + }, + }, + + ["j_unstb_throwing_hands"] = { + ["name"] = "Mãozada", + ["text"] = { + "{X:mult,C:white}X#1#{} Multi", + "{C:green}#2# de #3#{} chance de se {E:1,C:red}autodestruir{}", + "se a mão de pôquer pontuada", + "não for {C:attention}#4#{}", + "{s:0.7}A mão muda no fim da rodada", + }, + }, + + --Historic Cards + + ["j_unstb_imperial_bower"] = { + ["name"] = "Imperial Bower", + ["text"] = { + "{X:mult,C:white}X#1#{} Multi se a mão jogada", + "contém uma {C:attention}Sequência{} e", + "tem ao menos uma carta de {C:attention}realeza{}", + }, + }, + + ["j_unstb_the_jolly_joker"] = { + ["name"] = "O \"Curinga Alegre\"", + ["text"] = { + "Ganha {C:mult}+#1#{} Multi se a", + "mão jogada contém um {C:attention}Par{}", + "{C:red}Reseta{} se a mão jogada", + "não contém um {C:attention}Par{}", + "{C:inactive}(No momento, {C:mult}+#2#{C:inactive} Multi)", + }, + }, + + ["j_unstb_get_out_of_jail_free_card"] = { + ["name"] = "Carta Saída Livre da Prisão", + ["text"] = { + "Venda este Curinga para", + "vencer o Blind instantaneamente", + "{C:inactive}Status: {V:1}#1#{}", + }, + }, + + ["j_unstb_tanzaku"] = { + ["name"] = "Tanzaku", + ["text"] = { + "Reativa cartas jogadas com", + "{C:attention}selos{} {C:attention}#1#{} vezes adicionais", + "A quantidade aumenta em {C:attention}#2#{} por", + "cada carta descartada com um {C:attention}selo{}", + "{s:0.8}Reseta após cada rodada", + }, + }, + + --Misc Jokers + + ["j_unstb_glass_cannon"] = { + ["name"] = "Canhão de Vidro", + ["text"] = { + "Reativa cada", + "{C:attention}Carta de Vidro{} jogada", + "{C:red}Cartas de Vidro sempre", + "{C:red}quebram quando pontuadas", + }, + }, + + ["j_unstb_pity_rate_drop"] = { + ["name"] = "Oitavo Drop Garantido", + ["text"] = { + "Quando o {C:attention}Blind{} é selecionado,", + "{C:green}#1# de #2#{} chance de criar um {C:attention}Curinga{} {C:red}Raro{}", + "Chance aumenta em {C:green}#3#{} se nenhum {C:attention}Curinga{} criado,", + "Caso contrário, reseta para {C:green}#4#{}", + "{C:inactive}(Deve ter espaço)", + }, + }, + + ["j_unstb_salmon_run"] = { + ["name"] = "Corrida de Salmão", + ["text"] = { + "Cada {C:attention}7{} pontuado tem", + "{C:green}#1# em #2#{} chances de adicionar uma", + "cópia permanente ao baralho", + }, + }, + + ["j_unstb_cool_s"] = { + ["name"] = "S Daora", + ["text"] = { + "{C:attention}8s{} pontuados tornam-se", + "uma carta {C:attention}Aprimorada{} aleatória", + }, + }, + + ["j_unstb_memoriam_photo"] = { + ["name"] = "Foto Póstuma", + ["text"] = { + "quando uma carta de jogo é {C:attention}destruída{},", + "este Curinga ganha Fichas igual ao {C:attention}double{}", + "da classe da {C:attention}carta destruída{}", + "{C:inactive}(No momento, {C:chips}+#1#{C:inactive} Fichas)", + }, + }, + + ["j_unstb_schrodinger_cat"] = { + ["name"] = "Gato de Schrödinger", + ["text"] = { + "Quando uma carta de jogo é {C:attention}destruída{},", + "{C:green}#1# de #2#{} chance de adicionar", + "uma cópia da carta ao baralho", + }, + }, + + ["j_unstb_cashback_card"] = { + ["name"] = "Cartão de Cashback", + ["text"] = { + "No fim de um {C:attention}Blind de Chefe{},", + "ganhe {C:gold}dinheiro{} igual à quantidade", + "de {C:attention}consumíveis{} usados nesta {C:attention}Aposta", + "{C:inactive}(No momento, {C:gold}$#1#{C:inactive})", + }, + }, + + ["j_unstb_raffle"] = { + ["name"] = "Rifa", + ["text"] = { + "No final de cada {C:attention}loja{},", + "{C:green}#1# de #2#{} chance de ganhar {C:gold}$#3#", + "Chance aumenta em {C:green}#4#{}", + "por cada compra na {C:attention}loja", + "{s:0.8, C:red}Reseta após sair da loja", + }, + }, + + ["j_unstb_ic_card"] = { + ["name"] = "Cartão IC", + ["text"] = { + "Guarde {C:gold}$#1#{} neste Curinga toda", + "vez que uma mão é jogada", + "Ao gastar dinheiro, o {C:attention}saldo", + "neste Curinga é gasto primeiro", + "{C:red}Usável apenas por #2# rodadas", + "{s:0.8,C:inactive}(No momento, {C:gold}$#3#{C:inactive}, {C:red}Restam #4# rodadas{C:inactive}){}", + }, + }, + + ["j_unstb_j2048"] = { + ["name"] = "2048", + ["text"] = { + "Se a mão jogada contém um {C:attention}Par{},", + "destrói a carta da esquerda e dá", + "suas fichas para a da direita", + "{C:inactive}(Arraste para reorganizar){}", + }, + }, + ["j_unstb_j2048_ex"] = { + ["name"] = "2048", + ["text"] = { + "Se a mão jogada contém um {C:attention}Par{} com", + "classes potências de 2 {C:attention}(0, 1, 2, 4, 8){}, destrói", + "a da esquerda e dá as Fichas para a outra", + "{C:inactive}(Arraste para reorganizar){}", + }, + }, + + ["j_unstb_inductor"] = { + ["name"] = "Indutor", + ["text"] = { + "Para cada carta jogada com os mesmos", + "{C:attention}naipe{} e {C:attention}classe{} de outra carta jogada:", + "{C:green}#1# de #2#{} chance de copiar o {C:attention}Aprimoramento{}", + "{C:green}#1# de #3#{} chance de copiar a {C:attention}Edição{}", + "{C:green}#1# de #4#{} chance de copiar o {C:attention}Selo{}", + }, + }, + + + --Vanilla Joker Override + + ["j_fibonacci_ex"] = { + ["name"] = "Fibonacci", + ["text"] = { + "Cada {C:attention}0{}, {C:attention}1{}, {C:attention}Ás{},", + "{C:attention}2{}, {C:attention}3{}, {C:attention}5{}, {C:attention}8{}, {C:attention}13{}, ou {C:attention}21{} jogados", + "dão {C:mult}+#1#{} Multi quando pontuados", + }, + }, + + ["j_odd_todd_ex"] = { + ["name"] = "Crispimpar", + ["text"] = { + "Cartas jogadas com", + "classe {C:attention}ímpar{} dão", + "{C:mult}+#1#{} Multi quando pontuadas", + "{C:inactive}(161, 21, 13, 11, A, 9, 7, 5, 3, 1){}", + }, + }, + + ["j_even_steven_ex"] = { + ["name"] = "Gaspar", + ["text"] = { + "Cartas jogadas com", + "classe {C:attention}par{} dão", + "{C:mult}+#1#{} Multi quando pontuadas", + "{C:inactive}(12, 10, 8, 6, 4, 2, 0){}", + }, + }, + + ["j_hack_ex"] = { + ["name"] = "Impostor", + ["text"] = { + "Reativa cada", + "{C:attention}0{}, {C:attention}1{}, {C:attention}2{},", + "{C:attention}3{}, {C:attention}4{}, ou {C:attention}5{}", + }, + }, + }, + + }, +} diff --git a/UnStable-main/manifest.json b/UnStable-main/manifest.json new file mode 100644 index 0000000..69b0a20 --- /dev/null +++ b/UnStable-main/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "UnStable", + "id": "UnStable", + "version": "1.1.2c", + "website_url": "https://github.com/kirbio/UnStable", + "description": "Mod with a lot of new but vanilla-style mechanics. Contains new Jokers, consumables, and other stuff!", + "prefix": "unstb", + "author": ["Kirbio, RamChops Games"], + "main_file": "Unstable.lua", + "priority": 777, + "badge_colour": "41c300", + "dependencies": [ + "Steamodded (>=1.0.0~ALPHA-1318b)" + ] +} diff --git a/UnStable-main/override/jokerdisplay.lua b/UnStable-main/override/jokerdisplay.lua new file mode 100644 index 0000000..8de10a4 --- /dev/null +++ b/UnStable-main/override/jokerdisplay.lua @@ -0,0 +1,183 @@ +local jd = JokerDisplay.Definitions + +--UnStable's Joker + +--[[jd["j_unstb_lunar_calendar"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "count", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.SECONDARY_SET.Planet }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "lunar_suit"}, + { text = ")" } + }, + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = ")" }, + } + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + local count = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:get_id() and scoring_card:is_suit(card.ability.extra.suit) then + count = count + + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.count = count + card.joker_display_values.odds = localize { type = 'variable', key = "jdis_odds", vars = { (G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odds_spawn } } + card.joker_display_values.lunar_suit = localize(card.ability.extra.suit, 'suits_plural') + end, + style_function = function(card, text, reminder_text, extra) + if reminder_text and reminder_text.children[2] then + reminder_text.children[2].config.colour = lighten(G.C.SUITS[card.ability.extra.suit], 0.35) + end + return false + end +} + +jd["j_unstb_dragon_hoard"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.MULT }, + calc_function = function(card) + card.joker_display_values.mult = (G.consumeables and card.ability.extra.mult_rate * math.floor(#G.consumeables.cards/card.ability.extra.held_amount))or 0 + end +} + +jd["j_unstb_card_dealer"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "chips", retrigger_type = "mult" } + }, + text_config = { colour = G.C.CHIPS }, +} + +jd["j_unstb_match_three"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.MULT }, + calc_function = function(card) + local count = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + --TODO: Check for suits in the adjacent card + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:get_id() and scoring_card:is_suit(card.ability.extra.suit) then + count = count + + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + + card.joker_display_values.mult = count + end +}]] + + +--Vanilla Override Jokers +if unstb_global.config.joker.vanilla then + +jd["j_fibonacci"].calc_function = function(card) + local mult = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if not scoring_card.config.center.no_rank and (unstb_global.fibo[scoring_card.base.nominal] or scoring_card.base.value == 'Ace') then + mult = mult + + card.ability.extra.mult * + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.mult = mult + + if getPoolRankFlagEnable('unstb_0') or getPoolRankFlagEnable('unstb_1') or getPoolRankFlagEnable('unstb_13') or getPoolRankFlagEnable('unstb_21') then + card.joker_display_values.localized_text = "(0,1," .. localize("Ace", "ranks") .. ",2,3,5,8,13,21)" + else + card.joker_display_values.localized_text = "(" .. localize("Ace", "ranks") .. ",2,3,5,8)" + end + + + end + +jd["j_even_steven"].reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, +} +jd["j_even_steven"].calc_function = function(card) + local mult = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if unstb_global.modulo_check(scoring_card, 2, 0) then + mult = mult + + card.ability.extra.mult * + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.mult = mult + + if getPoolRankFlagEnable('unstb_0') or getPoolRankFlagEnable('unstb_12') then + card.joker_display_values.localized_text = "(12,10,8,6,4,2,0)" + else + card.joker_display_values.localized_text = "(10,8,6,4,2)" + end +end + +jd["j_odd_todd"].reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, +} +jd["j_odd_todd"].calc_function = function(card) + local chips = 0 + local text, _, scoring_hand = JokerDisplay.evaluate_hand() + if text ~= 'Unknown' then + for _, scoring_card in pairs(scoring_hand) do + if unstb_global.modulo_check(scoring_card, 2, 1) then + chips = chips + + card.ability.extra.chips * + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.chips = chips + + if getPoolRankFlagEnable('unstb_1') or getPoolRankFlagEnable('unstb_11') or getPoolRankFlagEnable('unstb_13') or getPoolRankFlagEnable('unstb_21') or getPoolRankFlagEnable('unstb_25') or getPoolRankFlagEnable('unstb_161')then + card.joker_display_values.localized_text = "(161,25,21,13,11," .. localize("Ace", "ranks") .. ",9,7,5,3,1)" + else + card.joker_display_values.localized_text = "(" .. localize("Ace", "ranks") .. ",9,7,5,3)" + end +end + +--Completely redefine Hack +jd["j_hack"] = { + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "eligible_ranks" }, + }, + calc_function = function(card) + if getPoolRankFlagEnable('unstb_0') or getPoolRankFlagEnable('unstb_1') then + card.joker_display_values.eligible_ranks = "(0,1,2,3,4,5)" + else + card.joker_display_values.eligible_ranks = "(2,3,4,5)" + end + end, + retrigger_function = function(playing_card, scoring_hand, held_in_hand, joker_card) + if held_in_hand then return 0 end + return (not playing_card.config.center.no_rank and unstb_global.hack[playing_card.base.value]) and + joker_card.ability.extra * JokerDisplay.calculate_joker_triggers(joker_card) or 0 + end +} + +end \ No newline at end of file diff --git a/UnStable-main/override/suits.lua b/UnStable-main/override/suits.lua new file mode 100644 index 0000000..8d389e6 --- /dev/null +++ b/UnStable-main/override/suits.lua @@ -0,0 +1,131 @@ +local function print(message) + sendDebugMessage('[Unstable_Suit] - '..(tostring(message) or '???')) +end + +local suit_group = {} --Store each suit group, and store a map of suits inside +local suit_group_map = {} -- A map pointing from suit name (key) directly to suit group name + +suit_group.suit_black = {} +suit_group.suit_red = {} +suit_group.no_smear = {} + +function register_suit_group(type, suit) + + print('Registering suit: '..suit..' with the type '..type) + + local suit_table = suit_group[type] or {} + + suit_table[suit] = true + + suit_group_map[suit] = type +end + +function get_suit_group(suit) + return suit_group_map[suit] or "no_smear" +end + +register_suit_group("suit_black", "Spades") +register_suit_group("suit_black", "Clubs") +register_suit_group("suit_red", "Hearts") +register_suit_group("suit_red", "Diamonds") + +--print(inspectDepth(suit_group)) + +--Modded Suits Code in UnStableEX +--register_suit_group("no_smear", "Inks_Inks") +--register_suit_group("no_smear", "Inks_Color") + +local card_issuit_ref = Card.is_suit +function Card:is_suit(suit, bypass_debuff, flush_calc, bypass_seal) --Adds one more argument: bypass_seal. true if the calculation bypass the seal on the card + --Modified from SMODS-patched version of is_suit, completely re-implemented to supports the new smear system + if flush_calc then + + --Has seal, is modded seal, and has suit_seal property + if not bypass_seal and self.seal and SMODS.Seals[self.seal] and SMODS.Seals[self.seal].suit_seal and not self.debuff then + --returns true immediately if the suit is equal + if SMODS.Seals[self.seal].suit_seal == suit then + return true + end + end + + if self.ability.effect == 'Stone Card' or self.config.center.no_suit then + return false + end + if (self.ability.name == 'Wild Card' or self.config.center.any_suit) and not self.debuff then + return true + end + + --If the suit is equal, returns immediately + if self.base.suit == suit then + return true + end + + --If smeared joker exists, and is not in no_smear group + if next(SMODS.find_card('j_smeared')) and not suit_group.no_smear[suit] then + --Has seal, is modded seal, and has suit_seal property + if not bypass_seal and self.seal and SMODS.Seals[self.seal] and SMODS.Seals[self.seal].suit_seal and not self.debuff then + local targetGroup = get_suit_group(SMODS.Seals[self.seal].suit_seal) + + --returns true immediately if the suit is in the same suit group + if suit_group[targetGroup][suit] then + return true + end + end + + local targetGroup = get_suit_group(self.base.suit) + + --returns true if the suit is in the same suit group + if suit_group[targetGroup][suit] then + return true + end + end + + --fallback - go back to the main reference, in case other mood hook it as well + local fallback_res = card_issuit_ref(self, suit, bypass_debuff, flush_calc) + return fallback_res + else + if self.debuff and not bypass_debuff then return end + + --Has seal, is modded seal, and has suit_seal property + if not bypass_seal and self.seal and SMODS.Seals[self.seal] and SMODS.Seals[self.seal].suit_seal then + --returns true immediately if the suit is equal + if SMODS.Seals[self.seal].suit_seal == suit then + return true + end + end + + if self.ability.effect == 'Stone Card' or self.config.center.no_suit then + return false + end + if self.ability.name == 'Wild Card' or self.config.center.any_suit then + return true + end + + --If the suit is equal, returns immediately + if self.base.suit == suit then + return true + end + + --If smeared joker exists, and is not in no_smear group + if next(SMODS.find_card('j_smeared')) and not suit_group.no_smear[suit] then + --Has seal, is modded seal, and has suit_seal property + if not bypass_seal and self.seal and SMODS.Seals[self.seal] and SMODS.Seals[self.seal].suit_seal then + local targetGroup = get_suit_group(SMODS.Seals[self.seal].suit_seal) + --returns true immediately if the suit is in the same suit group + if suit_group[targetGroup][suit] then + return true + end + end + + local targetGroup = get_suit_group(self.base.suit) + + --returns true if the suit is in the same suit group + if suit_group[targetGroup][suit] then + return true + end + end + + --fallback - go back to the main reference, in case other mood hook it as well + return card_issuit_ref(self, suit, bypass_debuff, flush_calc) + end +end \ No newline at end of file diff --git a/UnStable-main/override/vanilla_joker.lua b/UnStable-main/override/vanilla_joker.lua new file mode 100644 index 0000000..4ce3d46 --- /dev/null +++ b/UnStable-main/override/vanilla_joker.lua @@ -0,0 +1,183 @@ + +--Rank + +--Fibonacci +--Operates from a table, other mods can register more number in this table if needed +--Currently supports up to 144 +unstb_global.fibo = {} + +--Register more Fibonacci numbers, accepts a list of number +function unstb_global.register_fibonacci(number_list) + for i = 1, #number_list do + unstb_global.fibo[number_list[i]] = true + end +end + +unstb_global.register_fibonacci({0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144}) + +--Fibonacci looks for a *nominal* chip of the rank (with special hardcoded case accounted for Ace) +SMODS.Joker:take_ownership('fibonacci', { + + config = { extra = { mult = 8} }, + loc_vars = function(self, info_queue, card) + + local key = self.key + if getPoolRankFlagEnable('unstb_0') or getPoolRankFlagEnable('unstb_1') or getPoolRankFlagEnable('unstb_21') then + key = self.key..'_ex' + end + + return { key = key, vars = {card and card.ability.extra.mult or self.config.extra.mult} } + end, + + calculate = function(self, card, context) + + if context.individual and context.cardarea == G.play then + local nominal = context.other_card.base.nominal + + if not context.other_card.config.center.no_rank and (unstb_global.fibo[nominal] or context.other_card.base.value == 'Ace') then + return { + mult = card.ability.extra.mult, + card = card + } + + else + --Failsafe for mod compat + return { + mult = 0 + } + end + end + + end +}, true) + +--Odd Todd and Even Steven + +--A special function written to specifically looking for rank's *nominal* + special case for ??? (Triggers everything) + +--Table of a true 'face' rank (TODO: Might open it up to allow other mods to register more? Not sure yet) +unstb_global.face_rank = {Jack = true, Queen = true, King = true} + +--Check for a modulo and remainder of the card's rank - used for both odd and even check +function unstb_global.modulo_check(card, mod, remainder) + + if card.config.center.no_rank then + return false + end + + local rank = card.base.value + + if rank == 'unstb_???' then -- ??? Card + return true + elseif unstb_global.face_rank[rank] then -- "True" Face Card (aka bypass Pareidolia) + return false + end + + return (card.base.nominal%mod == remainder) +end + +SMODS.Joker:take_ownership('odd_todd', { + + config = { extra = { chips = 31} }, + loc_vars = function(self, info_queue, card) + + local key = self.key + if getPoolRankFlagEnable('unstb_1') or getPoolRankFlagEnable('unstb_21') then + key = self.key..'_ex' + end + + return { key = key, vars = {card and card.ability.extra.chips or self.config.extra.chips} } + end, + + calculate = function(self, card, context) + + if context.individual and context.cardarea == G.play then + if unstb_global.modulo_check(context.other_card, 2, 1) then + return { + chips = card.ability.extra.chips, + card = card + } + + else + --Failsafe for mod compat + return { + chips = 0 + } + end + end + + end +}, true) + +SMODS.Joker:take_ownership('even_steven', { + + config = { extra = { mult = 4} }, + loc_vars = function(self, info_queue, card) + + local key = self.key + if getPoolRankFlagEnable('unstb_0') then + key = self.key..'_ex' + end + + return { key = key, vars = {card and card.ability.extra.mult or self.config.extra.mult} } + end, + + calculate = function(self, card, context) + + if context.individual and context.cardarea == G.play then + if unstb_global.modulo_check(context.other_card, 2, 0) then + return { + mult = card.ability.extra.mult, + card = card + } + + else + --Failsafe for mod compat + return { + mult = 0 + } + end + end + + end +}, true) + +--Hack +--Operates from a table, other mods can register more number in this table if needed +unstb_global.hack = {} + +--Register ranks that triggers Hack +function unstb_global.register_hack(rank_list) + for i = 1, #rank_list do + unstb_global.hack[rank_list[i]] = true + end +end + +unstb_global.register_hack({'unstb_0', 'unstb_1', '2', '3', '4', '5'}) + +SMODS.Joker:take_ownership('hack', { + + config = { extra = 1 }, + loc_vars = function(self, info_queue, card) + + local key = self.key + if getPoolRankFlagEnable('unstb_0') or getPoolRankFlagEnable('unstb_1') then + key = self.key..'_ex' + end + + return { key = key, vars = {card and card.ability.extra or self.config.extra} } + end, + + calculate = function(self, card, context) + if context.cardarea == G.play and context.repetition and not context.repetition_only then + if not context.other_card.config.center.no_rank and unstb_global.hack[context.other_card.base.value] then + return { + message = 'Again!', + repetitions = card.ability.extra, + card = context.blueprint_card or card + } + end + end + end, + +}, true) \ No newline at end of file diff --git a/UnStableEX-main/LICENSE b/UnStableEX-main/LICENSE new file mode 100644 index 0000000..a75caa7 --- /dev/null +++ b/UnStableEX-main/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Kirbio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/UnStableEX-main/README.md b/UnStableEX-main/README.md new file mode 100644 index 0000000..1539366 --- /dev/null +++ b/UnStableEX-main/README.md @@ -0,0 +1,20 @@ +![img](https://kirbio.github.io/UnStableModSite/img/logo_ex.png) + +Expansion and Cross-mod Compatibility for UnStable. +Mostly focused on Vanilla-like content mod. + +It currently only contains basic cross-mod support to make new features work properly. +This mod is very early in development and will continue to add more support and other content in the future. + +Limitation: + +Currently, it only supports the following mods: Bunco, Six Suits, Ink and Colors, Familiar, and Ortalab. +Any other mods that have Joker or Consumable effects that change the rank of the card will most likely break on all new ranks added by UnStable. +Any Enhancement that changes the appearance with the card's base suit will use a generic placeholder graphic for any non-basegame additional suit. + +Please note that this is not a standalone mod, you need the base [UnStable](https://github.com/kirbio/UnStable/tree/main) as well. + +Other cross-mod contents will show up only if you have the supported mod installed. + +For more information, please visit the mod's website here: +https://kirbio.github.io/UnStableModSite/ex.html diff --git a/UnStableEX-main/UnstableEX.lua b/UnStableEX-main/UnstableEX.lua new file mode 100644 index 0000000..69d8f47 --- /dev/null +++ b/UnStableEX-main/UnstableEX.lua @@ -0,0 +1,1961 @@ +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 \ No newline at end of file diff --git a/UnStableEX-main/assets/1x/enh_res.png b/UnStableEX-main/assets/1x/enh_res.png new file mode 100644 index 0000000..303a7bc Binary files /dev/null and b/UnStableEX-main/assets/1x/enh_res.png differ diff --git a/UnStableEX-main/assets/1x/enh_slop.png b/UnStableEX-main/assets/1x/enh_slop.png new file mode 100644 index 0000000..a8e8d58 Binary files /dev/null and b/UnStableEX-main/assets/1x/enh_slop.png differ diff --git a/UnStableEX-main/assets/1x/enh_slop_hc.png b/UnStableEX-main/assets/1x/enh_slop_hc.png new file mode 100644 index 0000000..97938a9 Binary files /dev/null and b/UnStableEX-main/assets/1x/enh_slop_hc.png differ diff --git a/UnStableEX-main/assets/1x/enh_slop_hc_b.png b/UnStableEX-main/assets/1x/enh_slop_hc_b.png new file mode 100644 index 0000000..c51fbd6 Binary files /dev/null and b/UnStableEX-main/assets/1x/enh_slop_hc_b.png differ diff --git a/UnStableEX-main/assets/1x/jokers_wip.png b/UnStableEX-main/assets/1x/jokers_wip.png new file mode 100644 index 0000000..a82ab67 Binary files /dev/null and b/UnStableEX-main/assets/1x/jokers_wip.png differ diff --git a/UnStableEX-main/assets/1x/modicon.png b/UnStableEX-main/assets/1x/modicon.png new file mode 100644 index 0000000..0743dec Binary files /dev/null and b/UnStableEX-main/assets/1x/modicon.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex.png b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex.png new file mode 100644 index 0000000..7a5ce52 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2.png b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2.png new file mode 100644 index 0000000..859a011 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2_hc.png b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2_hc.png new file mode 100644 index 0000000..8bac876 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2_hc.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2_hc_b.png b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2_hc_b.png new file mode 100644 index 0000000..927185a Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex2_hc_b.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex_hc.png b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex_hc.png new file mode 100644 index 0000000..a3dcb11 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex_hc.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex_hc_b.png b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex_hc_b.png new file mode 100644 index 0000000..e6ba5a2 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/bunco/rank_ex_hc_b.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/cardsauce/rank_ex2_cs.png b/UnStableEX-main/assets/1x/rank_ex/cardsauce/rank_ex2_cs.png new file mode 100644 index 0000000..b76be6d Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/cardsauce/rank_ex2_cs.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/cardsauce/rank_ex_cs.png b/UnStableEX-main/assets/1x/rank_ex/cardsauce/rank_ex_cs.png new file mode 100644 index 0000000..b8dd22d Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/cardsauce/rank_ex_cs.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/default/rank_ex.png b/UnStableEX-main/assets/1x/rank_ex/default/rank_ex.png new file mode 100644 index 0000000..0777102 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/default/rank_ex.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/default/rank_ex2.png b/UnStableEX-main/assets/1x/rank_ex/default/rank_ex2.png new file mode 100644 index 0000000..158d283 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/default/rank_ex2.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/inkscolor/rank_ex.png b/UnStableEX-main/assets/1x/rank_ex/inkscolor/rank_ex.png new file mode 100644 index 0000000..c829430 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/inkscolor/rank_ex.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/inkscolor/rank_ex2.png b/UnStableEX-main/assets/1x/rank_ex/inkscolor/rank_ex2.png new file mode 100644 index 0000000..05da697 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/inkscolor/rank_ex2.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/minty/rank_ex.png b/UnStableEX-main/assets/1x/rank_ex/minty/rank_ex.png new file mode 100644 index 0000000..dd88976 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/minty/rank_ex.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/minty/rank_ex2.png b/UnStableEX-main/assets/1x/rank_ex/minty/rank_ex2.png new file mode 100644 index 0000000..13fab90 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/minty/rank_ex2.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex.png b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex.png new file mode 100644 index 0000000..0bff823 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex2.png b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex2.png new file mode 100644 index 0000000..2d50861 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex2.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex2_hc.png b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex2_hc.png new file mode 100644 index 0000000..3a86673 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex2_hc.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex_hc.png b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex_hc.png new file mode 100644 index 0000000..da89c1c Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/mtj/rank_ex_hc.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/sixsuits/rank_ex.png b/UnStableEX-main/assets/1x/rank_ex/sixsuits/rank_ex.png new file mode 100644 index 0000000..e8f0215 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/sixsuits/rank_ex.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex/sixsuits/rank_ex2.png b/UnStableEX-main/assets/1x/rank_ex/sixsuits/rank_ex2.png new file mode 100644 index 0000000..3ff5ea6 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex/sixsuits/rank_ex2.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex2_multi.png b/UnStableEX-main/assets/1x/rank_ex2_multi.png new file mode 100644 index 0000000..158d283 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex2_multi.png differ diff --git a/UnStableEX-main/assets/1x/rank_ex_multi.png b/UnStableEX-main/assets/1x/rank_ex_multi.png new file mode 100644 index 0000000..0777102 Binary files /dev/null and b/UnStableEX-main/assets/1x/rank_ex_multi.png differ diff --git a/UnStableEX-main/assets/2x/enh_res.png b/UnStableEX-main/assets/2x/enh_res.png new file mode 100644 index 0000000..5fbbdb7 Binary files /dev/null and b/UnStableEX-main/assets/2x/enh_res.png differ diff --git a/UnStableEX-main/assets/2x/enh_slop.png b/UnStableEX-main/assets/2x/enh_slop.png new file mode 100644 index 0000000..c6edf92 Binary files /dev/null and b/UnStableEX-main/assets/2x/enh_slop.png differ diff --git a/UnStableEX-main/assets/2x/enh_slop_hc.png b/UnStableEX-main/assets/2x/enh_slop_hc.png new file mode 100644 index 0000000..22eac3f Binary files /dev/null and b/UnStableEX-main/assets/2x/enh_slop_hc.png differ diff --git a/UnStableEX-main/assets/2x/enh_slop_hc_b.png b/UnStableEX-main/assets/2x/enh_slop_hc_b.png new file mode 100644 index 0000000..b951f5d Binary files /dev/null and b/UnStableEX-main/assets/2x/enh_slop_hc_b.png differ diff --git a/UnStableEX-main/assets/2x/jokers_wip.png b/UnStableEX-main/assets/2x/jokers_wip.png new file mode 100644 index 0000000..00268ce Binary files /dev/null and b/UnStableEX-main/assets/2x/jokers_wip.png differ diff --git a/UnStableEX-main/assets/2x/modicon.png b/UnStableEX-main/assets/2x/modicon.png new file mode 100644 index 0000000..b2c56b5 Binary files /dev/null and b/UnStableEX-main/assets/2x/modicon.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex.png b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex.png new file mode 100644 index 0000000..f6658b2 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2.png b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2.png new file mode 100644 index 0000000..5c57aee Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2_hc.png b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2_hc.png new file mode 100644 index 0000000..fb30270 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2_hc.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2_hc_b.png b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2_hc_b.png new file mode 100644 index 0000000..1c1db07 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex2_hc_b.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex_hc.png b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex_hc.png new file mode 100644 index 0000000..f5280f3 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex_hc.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex_hc_b.png b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex_hc_b.png new file mode 100644 index 0000000..0e35f42 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/bunco/rank_ex_hc_b.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/cardsauce/rank_ex2_cs.png b/UnStableEX-main/assets/2x/rank_ex/cardsauce/rank_ex2_cs.png new file mode 100644 index 0000000..326d6ca Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/cardsauce/rank_ex2_cs.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/cardsauce/rank_ex_cs.png b/UnStableEX-main/assets/2x/rank_ex/cardsauce/rank_ex_cs.png new file mode 100644 index 0000000..b99f56c Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/cardsauce/rank_ex_cs.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/default/rank_ex.png b/UnStableEX-main/assets/2x/rank_ex/default/rank_ex.png new file mode 100644 index 0000000..353c52d Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/default/rank_ex.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/default/rank_ex2.png b/UnStableEX-main/assets/2x/rank_ex/default/rank_ex2.png new file mode 100644 index 0000000..7dee867 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/default/rank_ex2.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/inkscolor/rank_ex.png b/UnStableEX-main/assets/2x/rank_ex/inkscolor/rank_ex.png new file mode 100644 index 0000000..cec5573 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/inkscolor/rank_ex.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/inkscolor/rank_ex2.png b/UnStableEX-main/assets/2x/rank_ex/inkscolor/rank_ex2.png new file mode 100644 index 0000000..0c39f0a Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/inkscolor/rank_ex2.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/minty/rank_ex.png b/UnStableEX-main/assets/2x/rank_ex/minty/rank_ex.png new file mode 100644 index 0000000..790c40e Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/minty/rank_ex.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/minty/rank_ex2.png b/UnStableEX-main/assets/2x/rank_ex/minty/rank_ex2.png new file mode 100644 index 0000000..2116add Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/minty/rank_ex2.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex.png b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex.png new file mode 100644 index 0000000..1f900c3 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex2.png b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex2.png new file mode 100644 index 0000000..532d4d5 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex2.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex2_hc.png b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex2_hc.png new file mode 100644 index 0000000..5a1e25e Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex2_hc.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex_hc.png b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex_hc.png new file mode 100644 index 0000000..7425748 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/mtj/rank_ex_hc.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/sixsuits/rank_ex.png b/UnStableEX-main/assets/2x/rank_ex/sixsuits/rank_ex.png new file mode 100644 index 0000000..cc113c7 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/sixsuits/rank_ex.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex/sixsuits/rank_ex2.png b/UnStableEX-main/assets/2x/rank_ex/sixsuits/rank_ex2.png new file mode 100644 index 0000000..02639b7 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex/sixsuits/rank_ex2.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex2_multi.png b/UnStableEX-main/assets/2x/rank_ex2_multi.png new file mode 100644 index 0000000..7dee867 Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex2_multi.png differ diff --git a/UnStableEX-main/assets/2x/rank_ex_multi.png b/UnStableEX-main/assets/2x/rank_ex_multi.png new file mode 100644 index 0000000..848758d Binary files /dev/null and b/UnStableEX-main/assets/2x/rank_ex_multi.png differ diff --git a/UnStableEX-main/config.lua b/UnStableEX-main/config.lua new file mode 100644 index 0000000..b079fb3 --- /dev/null +++ b/UnStableEX-main/config.lua @@ -0,0 +1,9 @@ +return { + ["dndj"] = { + ["keep_sprite"] = true, + }, + ["showdown"] = { + ["use_decimal"] = true, + ["replace_zero"] = false, + }, +} \ No newline at end of file diff --git a/UnStableEX-main/lib/suit_compat.lua b/UnStableEX-main/lib/suit_compat.lua new file mode 100644 index 0000000..0bb06f5 --- /dev/null +++ b/UnStableEX-main/lib/suit_compat.lua @@ -0,0 +1,45 @@ +--Utility to auto-generate atlas and metadata +--mod_suffix: the folder name inside "rank_ex" folder, also used to append the rank atlas key +--lc_only: if true, high contrast atlas is set to be the same as lc atlas +function unstbex_lib.init_suit_compat(suit, mod_suffix, lc_only) + +--Only create atlas if the atlas for this mod has not been created before +if not SMODS.Atlases['unstbex_rank_ex_'..mod_suffix] then + +SMODS.Atlas { + key = "rank_ex_"..mod_suffix, + path = "rank_ex/"..mod_suffix.."/rank_ex.png", + px = 71, + py = 95 +} + +SMODS.Atlas { + key = "rank_ex2_"..mod_suffix, + path = "rank_ex/"..mod_suffix.."/rank_ex2.png", + px = 71, + py = 95 +} + +if not lc_only then + SMODS.Atlas { + key = "rank_ex_hc_"..mod_suffix, + path = "rank_ex/"..mod_suffix.."/rank_ex_hc.png", + px = 71, + py = 95 + } + + SMODS.Atlas { + key = "rank_ex2_hc_"..mod_suffix, + path = "rank_ex/"..mod_suffix.."/rank_ex2_hc.png", + px = 71, + py = 95 + } +end + +end + +unstbex_lib.extra_suits[suit] = {modname = mod_suffix, + lc_atlas = {"unstbex_rank_ex_"..mod_suffix, "unstbex_rank_ex2_"..mod_suffix}, + hc_atlas = {(lc_only and "unstbex_rank_ex_" or "unstbex_rank_ex_hc_")..mod_suffix, (lc_only and "unstbex_rank_ex2_" or "unstbex_rank_ex2_hc_")..mod_suffix}} + +end \ No newline at end of file diff --git a/UnStableEX-main/manifest.json b/UnStableEX-main/manifest.json new file mode 100644 index 0000000..dd13827 --- /dev/null +++ b/UnStableEX-main/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "UnStableEX", + "id": "UnStableEX", + "version": "0.2.2b", + "website_url": "https://github.com/kirbio/UnStableEX", + "description": "Add-on for the UnStable mod to extend the support for other mods", + "prefix": "unstbex", + "author": ["Kirbio"], + "main_file": "UnstableEX.lua", + "priority": 99999, + "badge_colour": "41c300", + "dependencies": [ + "Steamodded (>=1.0.0~ALPHA-1307d)", + "UnStable" + ] +}