Compare commits

..

No commits in common. "main" and "2.9.14" have entirely different histories.
main ... 2.9.14

32 changed files with 491 additions and 339 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -495,22 +495,6 @@ return {
},
},
},
Voucher = {
v_Roland_ceres = {
name = "Ceres",
text = {
"Level up {C:attention}Flush House",
"by {C:attention}#1# {}when it is played",
},
},
v_Roland_neptune = {
name = "Neptune",
text = {
"Level up {C:attention}Straight Flush",
"by {C:attention}#1# {}when it is played",
},
},
},
},
misc = {
challenge_names = {

View file

@ -3,7 +3,7 @@
"id": "Roland",
"name": "Roland",
"prefix": "Roland",
"version": "2.9.28",
"version": "2.9.14",
"badge_colour": "8BE9FD",
"display_name": "Roland",
"main_file": "src/main.lua",

View file

@ -23,7 +23,10 @@ SMODS.Atlas {
atlas_table = "ANIMATION_ATLAS",
}
SMODS.Sound {key = "kick", path = "kick.ogg"}
SMODS.Sound {
key = "kick",
path = "kick.ogg",
}
local function common_rank()
local tally, to_name = {}, {}
@ -52,7 +55,7 @@ end
local function has_enhancement(card)
local e = SMODS.get_enhancements(card)
return e and next(e)
return not not (e and next(e))
end
local function set_freeze(state)
@ -239,16 +242,16 @@ blind {
disable = function(self)
self:defeat()
end,
calculate = function(self, b, context)
return not b.disabled and
context.hand_drawn and
self.cards()
calculate = function(self, b)
return not b.disabled and self.cards()
:where("Roland_blizzard", false)
:where("facing", "front")
:each(set_freeze(true)) or nil
end,
cards = function()
return f(G):where(getmetatable, CardArea):flatmap("cards", ipairs)
return f(G):where(function(v)
return type(v) == "table" and type(v.cards) == "table"
end):flatmap("cards", ipairs)
end,
}
@ -350,16 +353,6 @@ function SMODS.current_mod:calculate(context)
local _ = not str and type(G.calc[1]) == "function" and G.calc[1](f(context):keys():string())
end
local _ = context.before and
f {"v_Roland_ceres", "v_Roland_neptune"}
:where(f.index_into(G.GAME.used_vouchers))
:map(f.index_into(G.P_CENTERS))
:where("config.extra.hand_type", context.scoring_name)
:pun "SMODS.Voucher"
:each(function(v, _)
SMODS.smart_level_up_hand(nil, v.config.extra.hand_type, nil, v.config.extra.amount)
end)
local improbable, orig = G.GAME.modifiers.Roland_improbable, G.GAME.probabilities
local _ = context.end_of_round and

View file

@ -188,7 +188,7 @@ SMODS.Challenge {
end)
local mod = G.GAME.modifiers
mod.Roland_Eternally_Crimson = context.setting_blind and true or mod.Roland_Eternally_Crimson
mod.Roland_Eternally_Crimson = not context.setting_blind and mod.Roland_Eternally_Crimson or nil
local debuff = context.debuff_card
local cards = G.jokers.cards
@ -262,7 +262,7 @@ SMODS.Challenge {
SMODS.Challenge {
key = "Eternally_Violet",
rules = {custom = {{id = "Roland_Eternally_Violet"}, {id = "Roland_Showdown_Violet"}}},
jokers = {{id = "j_joker"}, {id = "j_Roland_violet"}},
jokers = {{id = "j_Roland_violet"}},
restrictions = violet,
pronouns = "she_they",
calculate = function(_, context)

View file

@ -261,7 +261,7 @@ local orig_apply_to_run = Tag.apply_to_run
function Tag:apply_to_run(...)
if G.GAME.Bakery_charm == "BakeryCharm_Roland_wii" and
G.Bakery_charm_area.cards[1].ability.extra.active then
G.Bakery_charm_area.cards[1].config.center.ability.extra.active then
return
end

View file

@ -13,7 +13,7 @@ end
frozen_sound "_click"
local frozen_blocklist = {CardSleeve = true}
local frozen_sounds = f(4):map(frozen_sound):map "key":table()
local frozen_sounds = f(4):map(frozen_sound):map("key"):table()
local needs_chip_mult_override = {
Bull = true,
@ -144,7 +144,7 @@ local function hook_proxy()
local orig_calculate = proxy.calculate
function proxy:calculate(card, context, ...)
if not (card or {}).Roland_frozen or not (card.edition or {}).Roland_frozen then
if not card or not card.edition or not card.edition.Roland_frozen then
return orig_calculate(self, card, context, ...)
else
return SMODS.blueprint_effect(card, get_proxied_joker(card), context)
@ -154,7 +154,7 @@ local function hook_proxy()
local orig_loc_vars = proxy.loc_vars
function proxy:loc_vars(info_queue, card, ...)
if not (card or {}).Roland_frozen or not (card.edition or {}).Roland_frozen then
if not card or not card.edition or not card.edition.Roland_frozen then
return orig_loc_vars(self, info_queue, card, ...)
end

View file

@ -216,9 +216,9 @@ joker {
SMODS.calculate_context {drawing_cards = true, draw = {G.deck.cards}}
SMODS.calculate_context {
hand_drawn = facing_blind and {G.deck.cards[1]} and true,
other_drawn = not facing_blind and {G.deck.cards[1]} and true,
first_hand_drawn = not current_round.any_hand_drawn and facing_blind or nil,
hand_drawn = facing_blind and {G.deck.cards[1]} --[[@as true]],
other_drawn = not facing_blind and {G.deck.cards[1]},
}
if type(facing_blind) == "table" then
@ -488,9 +488,11 @@ joker {
end
end
return SMODS.merge_effects(
local key = card.config.center.key
local merged = SMODS.merge_effects(
f(G.jokers.cards):where(is_frozen):where(function(v)
return v.config.center.key ~= card.config.center.key
return v.config.center.key ~= key
end):map(function(v)
return SMODS.blueprint_effect(card, v, context)
end):where(type, "table"):map(function(v)
@ -498,6 +500,8 @@ joker {
return v
end):values():table()
)
return merged
end,
}
@ -648,7 +652,7 @@ joker {
local _ = card.area == G.jokers and self:cerulean(false)
end,
cerulean = function(_, value)
local _ = G.jokers and f(G.jokers.cards):each(function(v)
f(G.jokers.cards):each(function(v)
f {"click", "drag", "focus", "hover"}:map(f.index_into(v.states)):each(function(s)
s.can = value or v.config.center.key == "j_Roland_cerulean"
end)
@ -675,7 +679,7 @@ joker {
{card = card, xmult = card.ability.extra.xmult} or nil
end,
crimson = function()
local _ = G.jokers and f(G.jokers.cards, ipairs_reversed):where(is_frozen, false):each(function(v)
f(G.jokers.cards, ipairs_reversed):where(is_frozen, false):each(function(v)
local right = G.jokers.cards[v.rank + 1]
local debuffed_by_crimson = right and
@ -684,13 +688,11 @@ joker {
right.config.center.key == "j_Roland_crimson"
if debuffed_by_crimson and v.ability.Roland_crimson == nil then
local debuff = not not v.debuff
v.ability.Roland_crimson = not not v.debuff
v:set_debuff(true)
v.ability.Roland_crimson = debuff
elseif not debuffed_by_crimson and v.ability.Roland_crimson ~= nil then
local debuff = v.ability.Roland_crimson
v:set_debuff(v.ability.Roland_crimson)
v.ability.Roland_crimson = nil
v:set_debuff(debuff)
end
end)
end,
@ -757,7 +759,7 @@ joker {
key = "violet",
pronouns = "she_they",
idea = "hamester",
config = {extra = {before = 0.1, xmult = 6}},
config = {extra = {before = 0.1, xmult = 9}},
pixel_size = {w = 68, h = 68},
attributes = {"xmult"},
cost = 6,
@ -936,14 +938,10 @@ joker {
local extra, numerator, xmult = card.ability.extra, 1, 1
if G.GAME.blind.name == "bl_mp_nemesis" then
local n, d = SMODS.get_probability_vars(card, 1, extra.odds, self.key)
return n < d and {card = card, xmult = extra.odds} or nil
return {card = card, xmult = extra.odds}
end
-- 2^38 is the first iteration displayed as scientific notation.
local max, overflown = 38, false
for i = 1, max do
for _ = 1, 256 do
local key = "RolandMartingale" .. tostring(G.GAME.modifiers.Roland_martingale_seed or "")
if SMODS.pseudorandom_probability(card, self.key, 1, extra.odds, key) then
@ -955,18 +953,9 @@ joker {
xmult = xmult * extra.odds
local message = number_format(numerator) .. "/" .. number_format(xmult)
SMODS.calculate_effect({card = card, repetitions = 1, message = message, message_card = card}, card)
overflown = overflown or i == max
end
if overflown then
SMODS.calculate_effect({card = card, score = 1 / 0}, card)
q(function()
card:shatter()
end)
else
SMODS.calculate_effect({card = card, xmult = xmult}, card)
end
end,
}
@ -1258,7 +1247,7 @@ joker {
return {card = card, numerator = extra.probability}
end
if context.end_of_round and not context.repetition and extra.probability ~= extra.reset then
if context.end_of_round and extra.probability ~= extra.reset then
extra.probability = extra.reset
return {message = localize "k_reset", colour = G.C.RED, message_card = card, repetitions = 0}
end

View file

@ -9,91 +9,81 @@ local f = {}
if not f then
---@generic I, O
---@param first string|fun(v: I): O
---@param first fun(v: I): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first)
error {first}
end
---@generic I, T, O
---@param first string|fun(v: I): T
---@param second string|fun(v: T): O
---@param first fun(v: I): T
---@param second fun(v: T): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second)
error {first, second}
end
---@generic I, T1, T2, O
---@param first string|fun(v: I): T1
---@param second string|fun(v: T1): T2
---@param third string|fun(v: T2): O
---@param first fun(v: I): T1
---@param second fun(v: T1): T2
---@param third fun(v: T2): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second, third)
error {first, second, third}
end
---@generic I, T1, T2, T3, O
---@param first string|fun(v: I): T1
---@param second string|fun(v: T1): T2
---@param third string|fun(v: T2): T3
---@param fourth string|fun(v: T3): O
---@param first fun(v: I): T1
---@param second fun(v: T1): T2
---@param third fun(v: T2): T3
---@param fourth fun(v: T3): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second, third, fourth)
error {first, second, third, fourth}
end
---@generic I, T1, T2, T3, T4, O
---@param first string|fun(v: I): T1
---@param second string|fun(v: T1): T2
---@param third string|fun(v: T2): T3
---@param fourth string|fun(v: T3): T4
---@param fifth string|fun(v: T4): O
---@param first fun(v: I): T1
---@param second fun(v: T1): T2
---@param third fun(v: T2): T3
---@param fourth fun(v: T3): T4
---@param fifth fun(v: T4): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second, third, fourth, fifth)
error {first, second, third, fourth, fifth}
end
---@generic I, O
---@param all { [1]: (string|fun(v: I): O) }
---@param all {[1]: (fun(v: I): O)}
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T, O
---@param all { [1]: (string|fun(v: I): T), [2]: (string|fun(v: T): O) }
---@param all {[1]: (fun(v: I): T), [2]: (fun(v: T): O)}
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T1, T2, O
---@param all { [1]: (string|fun(v: I): T1), [2]: (string|fun(v: T1): T2), [3]: (string|fun(v: T2): O) }
---@param all {[1]: (fun(v: I): T1), [2]: (fun(v: T1): T2), [3]: (fun(v: T2): O)}
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T1, T2, T3, O
---@param all { [1]: (string|fun(v: I): T1), [2]: (string|fun(v: T1): T2), [3]: (string|fun(v: T2): T3), [4]: (string|fun(v: T3): O) }
---@param all {[1]: (fun(v: I): T1), [2]: (fun(v: T1): T2), [3]: (fun(v: T2): T3), [4]: (fun(v: T3): O)}
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T1, T2, T3, T4, O
---@param all { [1]: (string|fun(v: I): T1), [2]: (string|fun(v: T1): T2), [3]: (string|fun(v: T2): T3), [4]: (string|fun(v: T3): T4), [4]: (string|fun(v: T4): O) }
---@param all {[1]: (fun(v: I): T1), [2]: (fun(v: T1): T2), [3]: (fun(v: T2): T3), [4]: (fun(v: T3): T4), [4]: (fun(v: T4): O)}
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
@ -114,7 +104,7 @@ local none
---@return T
---@nodiscard
local function autofunc(func)
return type(func) == "string" and f.indices(func) or func or f.id
return type(func) == "string" and f.index(func) or func or f.id
end
---@generic K, V
@ -128,6 +118,13 @@ local function autopairs(tbl, fpairs)
return (fpairs or (tbl[#tbl] and ipairs or pairs))(tbl)
end
---@param any any
---@return boolean
---@nodiscard
local function is_f(any)
return type(any) == "table" and any.from == f.from and any.new == f.new
end
--- Always returns nil.
---@return nil
---@nodiscard
@ -160,7 +157,6 @@ end
---@generic T
---@param value T
---@return fun(T): boolean
---@nodiscard
function f.eq(value)
return function(v)
return value == v
@ -170,7 +166,6 @@ end
---@generic T
---@param value T
---@return fun(T): boolean
---@nodiscard
function f.nq(value)
return function(v)
return value ~= v
@ -195,15 +190,6 @@ function f.const(v)
end
end
---@param i integer
---@return fun(...: any): any
---@nodiscard
function f.arg(i)
return function(...)
return ({...})[i]
end
end
---@generic K, V
---@param v K
---@return fun(x: { [K]: V }): V
@ -242,32 +228,17 @@ function f.indices(v)
end
end
---@param any any
---@return boolean
---@nodiscard
function f.isf(any)
return type(any) == "table" and any.from == f.from and any.new == f.new
end
f[true and "chain"] = function(...)
local ret
for _, v in ipairs {...} do
for _, v in ipairs(...) do
if type(v) == "table" then
for _, vv in ipairs(v) do
local copy = ret
vv = autofunc(vv)
ret = ret and function(...)
return vv(copy(...))
return vv(ret(...))
end or vv
end
else
local copy = ret
v = autofunc(v)
ret = ret and function(...)
return v(copy(...))
return v(ret(...))
end or v
end
end
@ -284,7 +255,6 @@ function f.new(fnext)
return {
all = f.all,
any = f.any,
arg = f.arg,
chain = f.chain,
concat = f.concat,
const = f.const,
@ -299,14 +269,12 @@ function f.new(fnext)
index = f.index,
index_into = f.index_into,
indices = f.indices,
isf = f.isf,
keys = f.keys,
map = f.map,
new = f.new,
next = fnext or f.noop,
noop = f.noop,
nq = f.nq,
peek = f.peek,
pun = f.pun,
skip = f.skip,
slice = f.slice,
@ -386,7 +354,7 @@ function f:concat(...)
local sum, last = 0, 0
for i = 1, #fs do
fs[i] = f.isf(fs[i]) and fs[i] or f.from(fs[i])
fs[i] = is_f(fs[i]) and fs[i] or f.from(fs[i])
end
return f.new(function()
@ -414,30 +382,12 @@ function f:concat(...)
end)
end
---@generic K, V
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any
---@return F|{ [K]: V }
---@nodiscard
function f:peek(func)
func = autofunc(func)
return f.new(function()
local k, v = self:next()
if k ~= nil then
func(v, k)
return k, v
end
end)
end
---@generic K, V, U
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): U
---@param func F|fun(v: V, k: K): U
---@return F|{ [K]: U }
---@overload fun(self: F|{ [K]: V }, func: string): F|{ [K]: U }
---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string): F|{ [K]: U }
function f:map(func)
func = autofunc(func)
@ -452,11 +402,61 @@ end
---@generic K, V, U
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): { [any]: U }
---@param func F|fun(v: V, k: K): { [any]: U }
---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)
---@return F|{ [K]: U }
---@overload fun(self: F|{ [K]: V }, func: string, fpairs?: fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)): F|{ [K]: U }
---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string, fpairs?: fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)): F|{ [K]: U }
function f:flatmap(func, fpairs)
-- local i = 0
local vt, vk, vv, vp
func = autofunc(func)
return f.new(function()
if vk then
vk, vv = vp(vt, vk)
if vk ~= nil then
-- i = i + 1
-- return i, vv
return vk, vv
end
end
while true do
local k, v = self:next()
if k == nil then
return
end
v = func(v, k)
if type(v) ~= "table" then
-- i = i + 1
-- return i, v
return k, v
end
vp, vt, vk = autopairs(v, fpairs)
vk, vv = vp(vt, vk)
if vk ~= nil then
-- i = i + 1
-- return i, vv
return vk, vv
end
end
end)
end
---@generic K, V, U
---@param self F|{ [K]: V }
---@param func F|fun(v: V, k: K): { [any]: U }
---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)
---@return F|{ [K]: U }
---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string, fpairs?: fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)): F|{ [K]: U }
function f:flatmap(func, fpairs)
-- local i = 0
local vt, vk, vv, vp
@ -502,7 +502,7 @@ end
---@generic K, V
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): boolean
---@param func F|fun(v: V, k: K): boolean
---@param is? any
---@return F|{ [K]: V }
---@nodiscard
@ -665,10 +665,10 @@ end
---@generic K, V
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any
---@param func F|fun(v: V, k: K): any
---@return boolean|V
---@overload fun(self: F|{ [K]: V }, func: string?): boolean|V
---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string?): boolean|V
function f:any(func)
func = autofunc(func)
@ -683,10 +683,10 @@ end
---@generic K, V
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any
---@param func F|fun(v: V, k: K): any
---@return boolean|V
---@overload fun(self: F|{ [K]: V }, func: string?): boolean|V
---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string?): boolean|V
function f:all(func)
func = autofunc(func)
@ -701,10 +701,10 @@ end
---@generic K, V
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any
---@param func F|fun(v: V, k: K): any
---@return integer
---@overload fun(self: F|{ [K]: V }, func: string): integer
---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string): integer
function f:count(func)
local ret = 0
func = autofunc(func)

View file

@ -19,21 +19,15 @@ Balatest = Balatest
--- @type { constants?: { TEN: table }, new: (fun(self: self, arr?: number[], sign?: number, noNormalize?: boolean): table), pow: (fun(x: number, y: number): number) }
Big = Big
--- @type table?
--- @type table
CardSleeves = CardSleeves
--- @type table|fun(obj: SMODS.Back): SMODS.Back
--- @type fun(obj: SMODS.Back): SMODS.Back
CardSleeves.Sleeve = CardSleeves.Sleeve
--- @type {aliases: { [string]: [string] }}
Cryptid = Cryptid
--- @type fun(area: CardArea, ...: ...): Card
create_card_for_shop = create_card_for_shop
--- @type boolean|table
G.Bakery_charm_area.cards[1].ability.extra = G.Bakery_charm_area.cards[1].ability.extra
SMODS.Mods.Roland.config = require "config"
---@type userdata|{getWidth: fun(self: self): number}

View file

@ -1,59 +1,5 @@
local f = assert(SMODS.load_file "src/lib/funky.lua")() or require "lib.funky"
---@param v string
---@return Card|Tag
local function add(v)
if not G.P_TAGS[v] then
return SMODS.add_card {no_edition = true, key = v}
end
local tag = Tag(v)
if tag.name == "Orbital Tag" then
local hands = f(G.GAME.hands):where "visible":keys():table()
tag.ability.orbital_hand = pseudorandom_element(hands, pseudoseed "Roland_c_orbital_tag")
end
add_tag(tag)
return tag
end
local function simplify(x)
return type(x) == "string" and x:lower():gsub("%s", ""):gsub("_", ""):gsub("^the", "") or x
end
local function flatten(v)
return type(v) ~= "table" and {v} or (f.isf(v) and v:table() or v)
end
---@param it string
local function find(it)
if (G.P_CENTERS[it] or {}).config then
return it
end
local match = simplify(it)
local pool = f(G.P_CENTER_POOLS):any(f.chain(f.arg(2), simplify, f.eq(match)))
if type(pool) == "table" and next(pool) then
return pseudorandom_element(pool, pseudoseed "Roland_c").key
end
if Cryptid and Cryptid.aliases and Cryptid.aliases[it] then
return Cryptid.aliases[it]
end
return f(G.localization.descriptions)
:flatmap(flatten)
:where(type, "table")
:where(f.chain(f.arg(2), f.index_into(G.P_CENTERS), "config"))
:where(function(v, k)
return match == simplify(k) or match == simplify(v.name)
end)
:keys()
:any()
end
local function protect(fun)
return function()
local res, ret = pcall(fun)
@ -110,35 +56,4 @@ local function u()
G.STATE ~= G.STATES.PLAY_TAROT)
end
--- Creates one or more cards.
---@param ... any
---@return Card|Tag|(Card|Tag[])
local function c(...)
local cards = f {...}
:flatmap(flatten)
:where(type, "string")
:map(string.lower)
:map(find)
:where(f.id)
:map(add)
:values()
:table()
return #cards > 1 and cards or cards[1]
end
return {f, q, u, setmetatable({}, {
__call = function(_, ...)
return c(...)
end,
__index = function(_, k)
return c(k)
end,
__newindex = function(_, k, v)
local n = tonumber(v)
for _ = 1, type(n) == "number" and n or 1 do
c(k)
end
end,
})}
return {f, q, u}

View file

@ -52,30 +52,16 @@ q {
-- G.ARGS.LOC_COLOURS["Bakery_credit_bg_Roland_" .. k] = v.bg
end)
if not SMODS.Mods.DebugPlus or not SMODS.Mods.Roland.config.import_funky then
return
if SMODS.Mods.DebugPlus and SMODS.Mods.Roland.config.import_funky then
_G.f, _G.q, _G.u = unpack(qol)
end
_G.f, _G.q, _G.u, _G.c = unpack(qol)
end,
}
f {
"challenge",
"spectral",
"edition",
"tweaks",
"blind",
"charm",
"joker",
"tarot",
"back",
"seal",
"tag",
"voucher",
}:each(function(v)
f {"challenge", "spectral", "edition", "tweaks", "blind", "charm", "joker", "tarot", "back", "seal", "tag"}
:each(function(v)
assert(SMODS.load_file("src/" .. v .. ".lua"))(qol)
end)
end)
if Balatest then
f {"joker", "blind", "spectral"}:each(function(v)

View file

@ -69,15 +69,15 @@ spectral {
table.insert(info_queue, G.P_CENTERS.m_stone)
end,
can_use = function()
return next(G.hand.cards) and u()
return u() and not not next(G.hand.cards)
end,
use = function(_, card)
q {
trigger = "after",
delay = 0.4,
func = function()
card:juice_up(0.3, 0.5)
play_sound("tarot1")
card:juice_up(0.3, 0.5)
end,
}
@ -89,8 +89,8 @@ spectral {
delay = 0.15,
func = function()
v:flip()
v:juice_up(0.3, 0.3)
play_sound("card1", percent)
v:juice_up(0.3, 0.3)
return true
end,
}
@ -110,8 +110,8 @@ spectral {
delay = 0.15,
func = function()
v:flip()
v:juice_up(0.3, 0.3)
play_sound("tarot2", percent, 0.6)
v:juice_up(0.3, 0.3)
return true
end,
}
@ -155,7 +155,7 @@ spectral {
return {vars = {card.ability.extra.amount}}
end,
can_use = function()
return next(G.playing_cards) and u()
return #G.playing_cards > 1 and not not u()
end,
use = function(_, card)
local function destructible(v)

View file

@ -192,9 +192,9 @@ Balatest.TestPlay {
}
Balatest.TestPlay {
category = {"blind", "mitotic"},
name = "mitotic",
blind = "bl_Roland_mitotic",
category = {"blind", "xerox"},
name = "xerox",
blind = "bl_Roland_xerox",
execute = function()
Balatest.discard {"2S"}
end,
@ -204,9 +204,9 @@ Balatest.TestPlay {
}
Balatest.TestPlay {
category = {"blind", "mitotic"},
name = "mitotic_disabled",
blind = "bl_Roland_mitotic",
category = {"blind", "xerox"},
name = "xerox_disabled",
blind = "bl_Roland_xerox",
jokers = {"j_chicot"},
execute = function()
Balatest.discard {"2S"}

View file

@ -2,6 +2,330 @@ if not Balatest then
return
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.levels, 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.levels, 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.levels, 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.levels, 1)
end,
}
Balatest.TestPlay {
category = {"joker", "martingale"},
name = "martingale_oops",
@ -10,7 +334,7 @@ Balatest.TestPlay {
Balatest.play_hand {"2S"}
end,
assert = function()
Balatest.assert_chips(7)
Balatest.assert_chips(7 * 2)
end,
}
@ -23,6 +347,6 @@ Balatest.TestPlay {
Balatest.play_hand {"2S"}
end,
assert = function()
Balatest.assert_chips(1 / 0)
Balatest.assert_chips(7 * math.pow(2, 32))
end,
}

View file

@ -2,6 +2,22 @@ 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",

View file

@ -2,16 +2,11 @@ local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2]
SMODS.Joker:take_ownership("joker", {cost = 1}, true)
local orig_set_debuff = Card.set_debuff
function Card:set_debuff(should_debuff, ...)
if self.ability.Roland_crimson ~= nil then
self.ability.Roland_crimson = not not should_debuff
return
end
function Card:set_debuff(...)
if SMODS.get_enhancements(self).m_wild and SMODS.Mods.Roland.config.no_wild_debuff then
self.debuff = false
else
orig_set_debuff(self, should_debuff, ...)
orig_set_debuff(self, ...)
end
end

View file

@ -1,44 +0,0 @@
local voucher = (function()
local x = 0
---@param tbl SMODS.Voucher|{attributes: Attributes[]}
---@return SMODS.Voucher
return function(tbl)
tbl.pos = {x = x, y = 0}
tbl.atlas = "voucher"
tbl.cost = 10
x = x + 1
return SMODS.Voucher(tbl)
end
end)()
SMODS.Atlas {
px = 71,
py = 95,
key = "voucher",
path = "voucher.png",
}
voucher {
key = "ceres",
pronouns = "it_its",
config = {extra = {amount = 1, hand_type = "Flush House"}},
attributes = {"planet", "passive", "hand_type", "space"},
loc_vars = function(_, _, card)
return {vars = {card.ability.extra.amount}}
end,
in_pool = function(self)
return G.GAME.hands[self.config.extra.hand_type].visible
end,
}
voucher {
key = "neptune",
pronouns = "it_its",
requires = {"v_Roland_ceres"},
config = {extra = {amount = 2, hand_type = "Straight Flush"}},
attributes = {"planet", "passive", "hand_type", "space"},
loc_vars = function(_, _, card)
return {vars = {card.ability.extra.amount}}
end,
}