diff --git a/assets/1x/martingale.png b/assets/1x/martingale.png new file mode 100644 index 0000000..601c006 Binary files /dev/null and b/assets/1x/martingale.png differ diff --git a/assets/2x/afterimage.png b/assets/2x/afterimage.png index 7dade4b..7fdce19 100644 Binary files a/assets/2x/afterimage.png and b/assets/2x/afterimage.png differ diff --git a/assets/2x/blind.png b/assets/2x/blind.png index 4d9e630..c064748 100644 Binary files a/assets/2x/blind.png and b/assets/2x/blind.png differ diff --git a/assets/2x/escapey.png b/assets/2x/escapey.png index bf6ffee..a3dfde0 100644 Binary files a/assets/2x/escapey.png and b/assets/2x/escapey.png differ diff --git a/assets/2x/icon.png b/assets/2x/icon.png index 77ca6d1..11ace88 100644 Binary files a/assets/2x/icon.png and b/assets/2x/icon.png differ diff --git a/assets/2x/martingale.png b/assets/2x/martingale.png new file mode 100644 index 0000000..1ce9b94 Binary files /dev/null and b/assets/2x/martingale.png differ diff --git a/assets/2x/void.png b/assets/2x/void.png index c560bf6..8fb66fe 100644 Binary files a/assets/2x/void.png and b/assets/2x/void.png differ diff --git a/localization/en-us.lua b/localization/en-us.lua index 955f8cd..9ec3197 100644 --- a/localization/en-us.lua +++ b/localization/en-us.lua @@ -42,6 +42,19 @@ return { }, }, }, + j_Roland_martingale = { + name = "Martingale", + text = { + "{C:green}#1# in #2#{} chance to give {X:mult,C:white}X#2#{} Mult", + "{s:0.9}Otherwise {C:green,s:0.9}#1# in #2#{s:0.9} chance to give {X:mult,C:white,s:0.9}X#3#{s:0.9} Mult", + "{s:0.81}Otherwise {C:green,s:0.81}#1# in #2#{s:0.81} chance to give {X:mult,C:white,s:0.81}X#4#{s:0.81} Mult", + "{s:0.6561}Otherwise {C:green,s:0.6561}#1# in #2#{s:0.6561} chance to give {X:mult,C:white,s:0.6561}X#5#{s:0.6561} Mult", + "{s:0.43046721}Otherwise {C:green,s:0.43046721}#1# in #2#{s:0.43046721} chance to give {X:mult,C:white,s:0.43046721}X#6#{s:0.43046721} Mult", + "{s:0.1853020188851841}Otherwise {C:green,s:0.1853020188851841}#1# in #2#{s:0.1853020188851841} chance to give {X:mult,C:white,s:0.1853020188851841}X#7#{s:0.1853020188851841} Mult", + "{s:0.0343368382029250877861747139}Otherwise {C:green,s:0.0343368382029250877861747139}#1# in #2#{s:0.0343368382029250877861747139} chance to give {X:mult,C:white,s:0.0343368382029250877861747139}X#8#{s:0.0343368382029250877861747139} Mult", + "{s:0}Otherwise {C:green,s:0}#1# in #2#{s:0} chance to give {X:mult,C:white,s:0}X#9#{s:0} Mult", + }, + }, }, Spectral = { c_Roland_afterimage = { @@ -65,9 +78,11 @@ return { misc = { challenge_names = { c_Roland_Jokerful = "Jokerful", + c_Roland_Pastries = "Sweet Pastries", }, v_text = { ch_c_Roland_Jokerful = {"Only the {C:common}default Joker{} can appear in shops"}, + ch_c_Roland_Pastries = {"All blinds, cards, and tags are of {C:gold}Bakery{} or {C:blue}Roland"}, }, }, } diff --git a/manifest.json b/manifest.json index e5a5045..3a2fb30 100644 --- a/manifest.json +++ b/manifest.json @@ -6,11 +6,11 @@ "author": [ "Emik" ], - "version": "1.4.7", + "version": "1.5.0", "badge_colour": "8BE9FD", "main_file": "src/main.lua", "badge_text_colour": "44475A", - "display_name": "Roland (Emik)", + "display_name": "Roland", "description": "Adds several disconnected funny ideas I had in my head that I couldn't resist implementing in the game.", "provides": [], "conflicts": [], diff --git a/refs/Balatest b/refs/Balatest new file mode 120000 index 0000000..c89a2b4 --- /dev/null +++ b/refs/Balatest @@ -0,0 +1 @@ +../../Balatest/src/ \ No newline at end of file diff --git a/src/challenge.lua b/src/challenge.lua index 7abf50e..cf47676 100644 --- a/src/challenge.lua +++ b/src/challenge.lua @@ -1,4 +1,24 @@ local jokerful = {banned_cards = {}} +local pastries = {banned_cards = {}, banned_tags = {}, banned_other = {}} + +local function adder(tbl) + return function(v) + table.insert(tbl, {id = v.key}) + end +end + +local function is_banned_from_pastry(_, k) + return not k:find("_Bakery_") and not k:find("_Roland_") +end + +local function is_center_banned_from_pastry(v, k) + local pastries_targets = {Enhanced = true, Joker = true, Tarot = true, Spectral = true} + return pastries_targets[v.set] and is_banned_from_pastry(v, k) +end + +local function is_joker(v) + return v.set == "Joker" +end SMODS.Challenge { key = "Jokerful", @@ -7,17 +27,20 @@ SMODS.Challenge { pronouns = "he_him", } +SMODS.Challenge { + key = "Pastries", + rules = {custom = {{id = "Roland_Pastries"}}}, + restrictions = pastries, + pronouns = "she_them", +} + G.E_MANAGER:add_event(Event { trigger = "immediate", func = function() - F.foreach( - F.filter( - G.P_CENTERS, - function(v) return v.set == "Joker" end - ), - function(v) table.insert(jokerful.banned_cards, {id = v.key}) end - ) - + F.foreach(F.filter(G.P_CENTERS, is_joker), adder(jokerful.banned_cards)) + F.foreach(F.filter(G.P_TAGS, is_banned_from_pastry), adder(pastries.banned_tags)) + F.foreach(F.filter(G.P_BLINDS, is_banned_from_pastry), adder(pastries.banned_other)) + F.foreach(F.filter(G.P_CENTERS, is_center_banned_from_pastry), adder(pastries.banned_cards)) return true end, }) diff --git a/src/joker.lua b/src/joker.lua index fdec2fe..2fb7e3c 100644 --- a/src/joker.lua +++ b/src/joker.lua @@ -23,6 +23,10 @@ local function destructible(card) return not card.highlighted and not (card.ability or {}).eternal end +local function is_carbon(card) + return card.edition and card.edition.key == "e_Bakery_Carbon" +end + local function is_mergeable_with(it) return function(card) return it.rank ~= card.rank and @@ -38,6 +42,13 @@ SMODS.Atlas { path = "escapey.png", } +SMODS.Atlas { + px = 71, + py = 95, + key = "martingale", + path = "martingale.png", +} + SMODS.Joker { key = "escapey", atlas = "escapey", @@ -59,7 +70,7 @@ SMODS.Joker { local quotes = loc_self.quotes local merge = G.jokers - and F.count(F.filter(G.jokers.cards, is_mergeable_with(card))) > 0 + and #F.filter(G.jokers.cards, is_mergeable_with(card)) > 0 and loc_self.merge or {} local normal = (merge[1] or sinister) and {} or @@ -102,7 +113,7 @@ SMODS.Joker { return "DEBUFFED" end - return #G.GAME.tags == 0 and F.count(G.consumeables.cards, destructible) == 0 and + return #G.GAME.tags == 0 and #F.filter(G.consumeables.cards, destructible) == 0 and F.any(F.filter(G.jokers.cards, is_mergeable_with(card))) and "FUSE" or "ESCAPE" end, ---@param card Card @@ -112,22 +123,27 @@ SMODS.Joker { end local consumables = F.filter(G.consumeables.cards, destructible) - local consumable_count = F.count(consumables) - local object_count = consumable_count + #G.GAME.tags + local consumable_count = #consumables + local tag_count = #G.GAME.tags - if object_count == 0 then + if consumable_count == 0 and tag_count == 0 then local level_sum, sell_sum = 0, 0 + local any_carbon = is_carbon(card) F.foreach( F.filter(G.jokers.cards, is_mergeable_with(card)), function(v) + any_carbon = any_carbon or is_carbon(v) level_sum = level_sum + v.ability.extra.level_up_by * (v.getEvalQty and v:getEvalQty() or 1) sell_sum = sell_sum + v.sell_cost * (v.getEvalQty and v:getEvalQty() or 1) v:start_dissolve({HEX("57ecab")}, nil, 1.6) end ) - card.ability.extra.level_up_by = card.ability.extra.level_up_by + level_sum + if not any_carbon then + card.ability.extra.level_up_by = card.ability.extra.level_up_by + level_sum + end + card.sell_cost = card.sell_cost + sell_sum return end @@ -179,6 +195,8 @@ SMODS.Joker { ) end + local destroyed = 0 + if consumable_count == 0 then local trigger = #G.GAME.tags >= 30 and "immediate" or "before" local delay = #G.GAME.tags >= 30 and 0 or 1 / #G.GAME.tags @@ -186,19 +204,27 @@ SMODS.Joker { F.foreach( G.GAME.tags, function(v) - G.E_MANAGER:add_event(Event {trigger = trigger, blocking = #G.GAME.tags < 30, delay = delay, func = fast_delete(v)}) + destroyed = destroyed + 1 + + G.E_MANAGER:add_event(Event { + trigger = trigger, + blocking = #G.GAME.tags < 30, + delay = delay, + func = fast_delete(v), + }) end ) else F.foreach( consumables, function(v) + destroyed = destroyed + 1 v:start_dissolve({HEX("57ecab")}, nil, 1.6) end ) end - level_up_hand(card, hand, nil, object_count * card.ability.extra.level_up_by) + level_up_hand(card, hand, nil, destroyed * card.ability.extra.level_up_by) update_hand_text( {sound = "button", volume = 0.7, pitch = 1.1, delay = 0}, @@ -206,3 +232,46 @@ SMODS.Joker { ) end, } + +SMODS.Joker { + key = "martingale", + atlas = "martingale", + pronouns = "he_him", + pos = {x = 0, y = 0}, + config = {extra = {odds = 2, times = 0, martingale = true}}, + cost = 7, + rarity = 3, + eternal_compat = true, + blueprint_compat = true, + perishable_compat = true, + loc_vars = function(_, _, card) + local normal = G.GAME.probabilities.normal + local odds = card.ability.extra.odds + local vars = {normal} + + for i = #vars, 9 do + vars[i + 1] = math.pow(odds, i) + end + + return {vars = vars} + end, + calculate = function(_, card, context) + if not context.joker_main then + return + end + + local extra = card.ability.extra + local xmult = extra.odds + + for _ = 1, 31 do + if SMODS.pseudorandom_probability(card, "j_Roland_martingale", 1, extra.odds, "Martingale") then + break + end + + SMODS.calculate_effect({card = card, repetitions = 1, message = "1/" .. number_format(xmult)}, card) + xmult = xmult * extra.odds + end + + SMODS.calculate_effect({card = card, xmult = xmult}, card) + end, +} diff --git a/src/main.lua b/src/main.lua index 6a733a3..7eda99a 100644 --- a/src/main.lua +++ b/src/main.lua @@ -4,11 +4,17 @@ assert(SMODS.load_file("src/spectral.lua"))() assert(SMODS.load_file("src/blind.lua"))() assert(SMODS.load_file("src/joker.lua"))() +if Balatest then + assert(SMODS.load_file("src/tests/joker.tests.lua"))() + assert(SMODS.load_file("src/tests/blind.tests.lua"))() + assert(SMODS.load_file("src/tests/spectral.tests.lua"))() +end + +SMODS.Joker:take_ownership("joker", {cost = 1}, true) + SMODS.Atlas { px = 256, py = 256, key = "modicon", path = "icon.png", } - -SMODS.Joker:take_ownership("joker", {cost = 1}, true) diff --git a/src/tests/blind.tests.lua b/src/tests/blind.tests.lua new file mode 100644 index 0000000..b4e1275 --- /dev/null +++ b/src/tests/blind.tests.lua @@ -0,0 +1,60 @@ +if not Balatest then + return +end + +Balatest.TestPlay { + category = {"blind", "improbable"}, + name = "improbable", + blind = "bl_Roland_improbable", + jokers = {"j_oops", "j_oops", "j_oops"}, + deck = {cards = {{s = "S", r = "2", e = "m_lucky"}, {s = "S", r = "3"}}}, + execute = function() + Balatest.play_hand {"2S"} + end, + assert = function() + Balatest.assert_chips(7) + end, +} + +Balatest.TestPlay { + category = {"blind", "improbable"}, + name = "improbable_not_leaky", + blind = "bl_Roland_improbable", + jokers = {"j_oops", "j_oops", "j_oops"}, + deck = {cards = {{s = "S", r = "2", e = "m_lucky"}, {s = "S", r = "3"}}}, + execute = function() + Balatest.play_hand {"2S"} + Balatest.next_round("bl_small") + Balatest.play_hand {"2S"} + end, + assert = function() + Balatest.assert_chips(7 * 21) + end, +} + +Balatest.TestPlay { + category = {"blind", "nimble"}, + name = "nimble", + blind = "bl_Roland_nimble", + deck = {cards = { + {s = "S", r = "2"}, + {s = "S", r = "2"}, + {s = "S", r = "2"}, + {s = "S", r = "2"}, + {s = "S", r = "2"}, + }}, + no_auto_start = true, + execute = function() + Balatest.q(function() + G.FUNCS.select_blind { + config = {ref_table = G.P_BLINDS[Balatest.current_test_object.blind]}, + UIBox = {get_UIE_by_ID = function() end}, + } + end) + + Balatest.wait_for_input() + end, + assert = function() + Balatest.assert_chips(2720) + end, +} diff --git a/src/tests/joker.tests.lua b/src/tests/joker.tests.lua new file mode 100644 index 0000000..b9dd613 --- /dev/null +++ b/src/tests/joker.tests.lua @@ -0,0 +1,352 @@ +if not Balatest then + return +end + +Balatest.TestPlay { + category = {"joker", "martingale"}, + name = "martingale_oops", + jokers = {"j_Roland_martingale", "j_oops"}, + execute = function() + Balatest.play_hand {"2S"} + end, + assert = function() + Balatest.assert_chips(7 * 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "martingale"}, + name = "martingale_improbable", + blind = "bl_Roland_improbable", + jokers = {"j_Roland_martingale"}, + execute = function() + Balatest.play_hand {"2S"} + end, + assert = function() + Balatest.assert_chips(7 * math.pow(2, 32)) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_none", + jokers = {"j_Roland_escapey"}, + execute = function() end, + assert = function() + Balatest.assert(not G.jokers.cards[1].config.center:Bakery_can_use(G.jokers.cards[1])) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_one_consumable", + jokers = {"j_Roland_escapey"}, + consumeables = {"c_strength"}, + execute = function() + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(G.GAME.hands["High Card"].level, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_two_consumables", + jokers = {"j_Roland_escapey"}, + consumeables = {"c_strength", "c_strength"}, + execute = function() + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(G.GAME.hands["High Card"].level, 3) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_one_tag", + jokers = {"j_Roland_escapey"}, + no_auto_start = true, + execute = function() + Balatest.skip_blind("tag_investment") + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(G.GAME.hands["High Card"].level, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_two_tags", + jokers = {"j_Roland_escapey"}, + no_auto_start = true, + execute = function() + Balatest.skip_blind("tag_investment") + Balatest.skip_blind("tag_investment") + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(G.GAME.hands["High Card"].level, 3) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_consumables_and_tags", + jokers = {"j_Roland_escapey"}, + consumeables = {"c_strength"}, + no_auto_start = true, + execute = function() + Balatest.skip_blind("tag_investment") + Balatest.skip_blind("tag_investment") + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(G.GAME.hands["High Card"].level, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_partial_selected_consumables_and_tags", + jokers = {"j_Roland_escapey"}, + consumeables = {"c_strength", "c_strength"}, + no_auto_start = true, + execute = function() + Balatest.skip_blind("tag_investment") + Balatest.skip_blind("tag_investment") + + Balatest.q(function() + G.consumeables:add_to_highlighted(G.consumeables.cards[1]) + end) + + Balatest.wait() + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(G.GAME.hands["High Card"].level, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "escape"}, + name = "escapey_full_selected_consumables_and_tags", + jokers = {"j_Roland_escapey"}, + consumeables = {"c_strength"}, + no_auto_start = true, + execute = function() + Balatest.skip_blind("tag_investment") + Balatest.skip_blind("tag_investment") + + Balatest.q(function() + G.consumeables:add_to_highlighted(G.consumeables.cards[1]) + end) + + Balatest.wait() + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(G.GAME.hands["High Card"].level, 3) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "fuse"}, + name = "escapey_fusion", + jokers = {"j_Roland_escapey", "j_Roland_escapey"}, + execute = function() + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(#G.jokers.cards, 1) + Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8) + Balatest.assert_eq(G.jokers.cards[1].ability.extra.level_up_by, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "fuse"}, + name = "escapey_consumable_takes_precedence", + jokers = {"j_Roland_escapey", "j_Roland_escapey"}, + consumeables = {"c_strength"}, + execute = function() + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(#G.jokers.cards, 2) + Balatest.assert_eq(G.GAME.hands["High Card"].level, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "fuse"}, + name = "escapey_tag_takes_precedence", + jokers = {"j_Roland_escapey", "j_Roland_escapey"}, + no_auto_start = true, + execute = function() + Balatest.skip_blind("tag_investment") + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(#G.jokers.cards, 2) + Balatest.assert_eq(G.GAME.hands["High Card"].level, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "fuse"}, + name = "escapey_fusion_takes_precedence", + jokers = {"j_Roland_escapey", "j_Roland_escapey"}, + consumeables = {"c_strength"}, + execute = function() + Balatest.q(function() + G.consumeables:add_to_highlighted(G.consumeables.cards[1]) + end) + + Balatest.wait() + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + Balatest.assert_eq(#G.jokers.cards, 1) + Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8) + Balatest.assert_eq(G.jokers.cards[1].ability.extra.level_up_by, 2) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "fuse"}, + name = "escapey_scribe_fusion", + jokers = {"j_Roland_escapey"}, + execute = function() + if not G.P_CENTERS.c_Bakery_Scribe then + sendWarnMessage("escapey_scribe_fusion cannot run without c_Bakery_Scribe, skipping test.") + return + end + + Balatest.q(function() + local scribe = create_card(nil, G.consumeables, nil, nil, nil, nil, "c_Bakery_Scribe", "balatest") + scribe:add_to_deck() + G.consumeables:emplace(scribe) + end) + + Balatest.wait() + + Balatest.q(function() + G.jokers:add_to_highlighted(G.jokers.cards[1]) + end) + + Balatest.wait() + Balatest.use(function() return G.consumeables.cards[1] end) + + Balatest.q(function() + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}} + end) + + Balatest.wait() + end, + assert = function() + if not G.P_CENTERS.c_Bakery_Scribe then + return + end + + Balatest.assert_eq(#G.jokers.cards, 1) + Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8) + Balatest.assert_eq(G.jokers.cards[1].ability.extra.level_up_by, 1) + end, +} + +Balatest.TestPlay { + category = {"joker", "escapey", "fuse"}, + name = "escapey_scribe_fusion_alt", + jokers = {"j_Roland_escapey"}, + execute = function() + if not G.P_CENTERS.c_Bakery_Scribe then + sendWarnMessage("escapey_scribe_fusion_alt cannot run without c_Bakery_Scribe, skipping test.") + return + end + + Balatest.q(function() + local scribe = create_card(nil, G.consumeables, nil, nil, nil, nil, "c_Bakery_Scribe", "balatest") + scribe:add_to_deck() + G.consumeables:emplace(scribe) + end) + + Balatest.wait() + + Balatest.q(function() + G.jokers:add_to_highlighted(G.jokers.cards[1]) + end) + + Balatest.wait() + Balatest.use(function() return G.consumeables.cards[1] end) + + Balatest.q(function() + G.jokers:add_to_highlighted(G.jokers.cards[2]) + G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[2]}} + end) + + Balatest.wait() + end, + assert = function() + if not G.P_CENTERS.c_Bakery_Scribe then + return + end + + Balatest.assert_eq(#G.jokers.cards, 1) + Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8) + Balatest.assert_eq(G.jokers.cards[1].ability.extra.level_up_by, 1) + end, +} diff --git a/src/tests/spectral.tests.lua b/src/tests/spectral.tests.lua new file mode 100644 index 0000000..3cce18c --- /dev/null +++ b/src/tests/spectral.tests.lua @@ -0,0 +1,33 @@ +if not Balatest then + return +end + +Balatest.TestPlay { + category = {"spectral", "afterimage"}, + name = "afterimage", + consumeables = {"c_Roland_afterimage"}, + deck = {cards = {{s = "S", r = "2"}}}, + execute = function() + Balatest.highlight({"2S"}) + Balatest.use(G.consumeables.cards[1]) + Balatest.end_round() + end, + assert = function() + Balatest.assert_eq(G.hand.config.card_limit, 51) + Balatest.assert(G.deck.cards[1].edition.negative) + end, +} + +Balatest.TestPlay { + category = {"spectral", "void"}, + name = "spectral", + consumeables = {"c_Roland_void"}, + execute = function() + Balatest.end_round() + Balatest.use(G.consumeables.cards[1]) + end, + assert = function() + Balatest.assert_eq(#G.deck.cards, 0) + Balatest.assert_eq(#G.consumeables.cards, 2) + end, +}