Compare commits

..

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

37 changed files with 587 additions and 468 deletions

View file

@ -65,8 +65,4 @@
"unused-local": "Error", "unused-local": "Error",
"unused-vararg": "Error", "unused-vararg": "Error",
}, },
"Lua.runtime.version": "LuaJIT",
"Lua.workspace.library": [
"${3rd}/love2d/library"
],
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 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: 33 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

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

@ -1,6 +1,5 @@
return { return {
animated_icon = true, animated_icon = true,
cool_phones = true,
equinox_assist = false, equinox_assist = false,
faster_planets = false, faster_planets = false,
harsh_ante_scaling = false, harsh_ante_scaling = false,

View file

@ -23,7 +23,7 @@ return {
}, },
BakeryCharm = { BakeryCharm = {
BakeryCharm_Roland_cocacola = { BakeryCharm_Roland_cocacola = {
name = SMODS.Mods.Roland.config.cool_phones and "coca cola phone" or "Pentagram", name = "coca cola phone",
text = { text = {
"{C:attention}Discard 0 cards {}to", "{C:attention}Discard 0 cards {}to",
"gain {C:red}#1# {}discards and", "gain {C:red}#1# {}discards and",
@ -32,7 +32,7 @@ return {
}, },
}, },
BakeryCharm_Roland_fat = { BakeryCharm_Roland_fat = {
name = SMODS.Mods.Roland.config.cool_phones and "fat i phone" or "Product", name = "fat i phone",
text = { text = {
"{C:attention}+#1# Booster Pack {}slots", "{C:attention}+#1# Booster Pack {}slots",
"All {C:attention}Booster Packs {}are {C:attention}Mega", "All {C:attention}Booster Packs {}are {C:attention}Mega",
@ -40,15 +40,15 @@ return {
}, },
}, },
BakeryCharm_Roland_flexible = { BakeryCharm_Roland_flexible = {
name = SMODS.Mods.Roland.config.cool_phones and "flexi ble phone" or "Ring", name = "flexi ble phone",
text = {unpack(G.localization.descriptions.Joker.j_ring_master.text)}, text = {unpack(G.localization.descriptions.Joker.j_ring_master.text)},
}, },
BakeryCharm_Roland_hand = { BakeryCharm_Roland_hand = {
name = SMODS.Mods.Roland.config.cool_phones and "hand phone" or "Shell", name = "hand phone",
text = {"{C:attention}+#1# {}hand size", "{C:blue}#2# {}hands"}, text = {"{C:attention}+#1# {}hand size", "{C:blue}#2# {}hands"},
}, },
BakeryCharm_Roland_wii = { BakeryCharm_Roland_wii = {
name = SMODS.Mods.Roland.config.cool_phones and "wii phone" or "Tire", name = "wii phone",
text = { text = {
"Enter the shop", "Enter the shop",
"when any {C:attention}Blind", "when any {C:attention}Blind",
@ -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 = { misc = {
challenge_names = { challenge_names = {
@ -530,7 +514,6 @@ return {
b_Roland_animated_icon = "Animated Icon (requires restart)", b_Roland_animated_icon = "Animated Icon (requires restart)",
b_Roland_bye = "Bye!", b_Roland_bye = "Bye!",
b_Roland_comma = ", ", b_Roland_comma = ", ",
b_Roland_cool_phones = "cool phones (requires restart)",
b_Roland_debuffed = "DEBUFFED", b_Roland_debuffed = "DEBUFFED",
b_Roland_disabled = "Disabled", b_Roland_disabled = "Disabled",
b_Roland_enabled = "Enabled", b_Roland_enabled = "Enabled",
@ -561,7 +544,7 @@ return {
ch_c_Roland_Eternally_Verdant = {"{C:attention}Verdant Leaf{}'s effect is active every blind"}, ch_c_Roland_Eternally_Verdant = {"{C:attention}Verdant Leaf{}'s effect is active every blind"},
ch_c_Roland_Eternally_Violet = {"{C:attention}Violet Vessel{}'s effect is active every blind"}, ch_c_Roland_Eternally_Violet = {"{C:attention}Violet Vessel{}'s effect is active every blind"},
ch_c_Roland_Glass1 = {"Played cards with no seal have a"}, ch_c_Roland_Glass1 = {"Played cards with no seal have a"},
ch_c_Roland_Glass2 = {"{C:green}1 in 2{} chance to gain {C:dark_edition}Glass Seal"}, ch_c_Roland_Glass2 = {"{C:green}1 in 4{} chance to gain {C:dark_edition}Glass Seal"},
ch_c_Roland_Go = {"Set money to {C:money}$0 {}when entering the shop"}, ch_c_Roland_Go = {"Set money to {C:money}$0 {}when entering the shop"},
ch_c_Roland_Jokerful = {"The only jokers are {C:mult}Joker{} and {C:chips}Ms. Joker"}, ch_c_Roland_Jokerful = {"The only jokers are {C:mult}Joker{} and {C:chips}Ms. Joker"},
ch_c_Roland_Pastries = {"All blinds, cards, and tags are of {C:gold}Bakery{} or {C:blue}Roland"}, ch_c_Roland_Pastries = {"All blinds, cards, and tags are of {C:gold}Bakery{} or {C:blue}Roland"},

View file

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

View file

@ -27,8 +27,9 @@ local back = (function()
tbl.atlas = "back" tbl.atlas = "back"
x = x + 1 x = x + 1
local back = q(SMODS.Back(tbl)) local back = q(SMODS.Back(tbl))
local sleeve = (_G["CardSleeves"] or {}).Sleeve
local _ = CardSleeves and CardSleeves.Sleeve { local _ = sleeve and sleeve {
key = key, key = key,
pos = tbl.pos, pos = tbl.pos,
atlas = "sleeve", atlas = "sleeve",
@ -60,7 +61,7 @@ SMODS.Atlas {
py = 95, py = 95,
} }
local _ = CardSleeves and SMODS.Atlas { local _ = _G["CardSleeves"] and SMODS.Atlas {
key = "sleeve", key = "sleeve",
path = "sleeve.png", path = "sleeve.png",
px = 73, px = 73,

View file

@ -23,7 +23,10 @@ SMODS.Atlas {
atlas_table = "ANIMATION_ATLAS", atlas_table = "ANIMATION_ATLAS",
} }
SMODS.Sound {key = "kick", path = "kick.ogg"} SMODS.Sound {
key = "kick",
path = "kick.ogg",
}
local function common_rank() local function common_rank()
local tally, to_name = {}, {} local tally, to_name = {}, {}
@ -52,7 +55,7 @@ end
local function has_enhancement(card) local function has_enhancement(card)
local e = SMODS.get_enhancements(card) local e = SMODS.get_enhancements(card)
return e and next(e) return not not (e and next(e))
end end
local function set_freeze(state) local function set_freeze(state)
@ -239,16 +242,16 @@ blind {
disable = function(self) disable = function(self)
self:defeat() self:defeat()
end, end,
calculate = function(self, b, context) calculate = function(self, b)
return not b.disabled and return not b.disabled and self.cards()
context.hand_drawn and
self.cards()
:where("Roland_blizzard", false) :where("Roland_blizzard", false)
:where("facing", "front") :where("facing", "front")
:each(set_freeze(true)) or nil :each(set_freeze(true)) or nil
end, end,
cards = function() 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, end,
} }
@ -334,6 +337,7 @@ end
function SMODS.current_mod:calculate(context) function SMODS.current_mod:calculate(context)
local _ = type(G.calccontext) == "function" and G.calccontext(context) local _ = type(G.calccontext) == "function" and G.calccontext(context)
local _ = type(G.calckeys) == "function" and G.calckeys(f(context):keys())
if type(G.calc) == "function" then if type(G.calc) == "function" then
G.calc = {G.calc} G.calc = {G.calc}
@ -350,16 +354,6 @@ function SMODS.current_mod:calculate(context)
local _ = not str and type(G.calc[1]) == "function" and G.calc[1](f(context):keys():string()) local _ = not str and type(G.calc[1]) == "function" and G.calc[1](f(context):keys():string())
end 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 improbable, orig = G.GAME.modifiers.Roland_improbable, G.GAME.probabilities
local _ = context.end_of_round and local _ = context.end_of_round and
@ -483,15 +477,15 @@ local venerable_visage = blind {
vitriol = function(b) vitriol = function(b)
local vitriol = SMODS.Mods.Roland.config.vitriol local vitriol = SMODS.Mods.Roland.config.vitriol
local resize_to_w, resize_to_h = 320, 200 local resize_to_w, resize_to_h = 320, 200
local is_fullscreen = love.window.getFullscreen() local is_fullscreen = _G["love"].window.getFullscreen()
if vitriol then if vitriol then
love.window.setFullscreen(false) _G["love"].window.setFullscreen(false)
delay(1.5) delay(1.5)
end end
local function jitter() local function jitter()
local x, y = love.window.getDesktopDimensions() local x, y = _G["love"].window.getDesktopDimensions()
return pseudorandom(pseudoseed "RolandVenerableVisageX", 0, x) - x / 2, return pseudorandom(pseudoseed "RolandVenerableVisageX", 0, x) - x / 2,
pseudorandom(pseudoseed "RolandVenerableVisageY", 0, y) - y / 2 pseudorandom(pseudoseed "RolandVenerableVisageY", 0, y) - y / 2
@ -519,16 +513,15 @@ local venerable_visage = blind {
play_sound("gong", v) play_sound("gong", v)
end) end)
---@type number, number, table
local w, h, flags = love.window.getMode()
local len = #G.playing_cards local len = #G.playing_cards
local w, h, flags = _G["love"].window.getMode()
if vitriol then if vitriol then
love.window.setMode(resize_to_w, resize_to_h) _G["love"].window.setMode(resize_to_w, resize_to_h)
love.resize(resize_to_w, resize_to_h) _G["love"].resize(resize_to_w, resize_to_h)
end end
local x, y = love.window.getPosition() local x, y = _G["love"].window.getPosition()
f(G.playing_cards):each(function(v, i) f(G.playing_cards):each(function(v, i)
q { q {
@ -537,7 +530,7 @@ local venerable_visage = blind {
func = function() func = function()
if vitriol then if vitriol then
local x_random, y_random = jitter() local x_random, y_random = jitter()
love.window.setPosition(x + x_random * i / len, y + y_random * i / len) _G["love"].window.setPosition(x + x_random * i / len, y + y_random * i / len)
end end
v:start_dissolve() v:start_dissolve()
@ -551,10 +544,10 @@ local venerable_visage = blind {
trigger = "before", trigger = "before",
delay = 1.5, delay = 1.5,
func = function() func = function()
love.window.setPosition(x, y) _G["love"].window.setPosition(x, y)
love.window.setMode(w, h, flags) _G["love"].window.setMode(w, h, flags)
love.resize(w, h) _G["love"].resize(w, h)
love.window.setFullscreen(is_fullscreen) _G["love"].window.setFullscreen(is_fullscreen)
end, end,
} }

View file

@ -46,7 +46,7 @@ SMODS.Challenge {
key = "Glass", key = "Glass",
rules = {custom = {{id = "Roland_Glass1"}, {id = "Roland_Glass2"}}}, rules = {custom = {{id = "Roland_Glass1"}, {id = "Roland_Glass2"}}},
pronouns = "they_them", pronouns = "they_them",
config = {extra = {odds = 2}}, config = {extra = {odds = 4}},
calculate = function(self, context) calculate = function(self, context)
if not context.after or context.blueprint_card then if not context.after or context.blueprint_card then
return return
@ -110,10 +110,12 @@ SMODS.Challenge {
pronouns = "she_them", pronouns = "she_them",
} }
local starting_jokers = f(5):map(f.const {id = "j_joker"}):table()
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Amber", key = "Eternally_Amber",
rules = {custom = {{id = "Roland_Eternally_Amber"}, {id = "Roland_Showdown_Amber"}}}, rules = {custom = {{id = "Roland_Eternally_Amber"}, {id = "Roland_Showdown_Amber"}}},
jokers = {{id = "j_Roland_amber"}}, jokers = starting_jokers,
restrictions = amber, restrictions = amber,
pronouns = "they_them", pronouns = "they_them",
calculate = function(_, context) calculate = function(_, context)
@ -152,7 +154,7 @@ SMODS.Challenge {
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Cerulean", key = "Eternally_Cerulean",
rules = {custom = {{id = "Roland_Eternally_Cerulean"}, {id = "Roland_Showdown_Cerulean"}}}, rules = {custom = {{id = "Roland_Eternally_Cerulean"}, {id = "Roland_Showdown_Cerulean"}}},
jokers = {{id = "j_Roland_cerulean"}}, jokers = starting_jokers,
restrictions = cerulean, restrictions = cerulean,
pronouns = "she_her", pronouns = "she_her",
calculate = function(_, context) calculate = function(_, context)
@ -179,7 +181,7 @@ SMODS.Challenge {
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Crimson", key = "Eternally_Crimson",
rules = {custom = {{id = "Roland_Eternally_Crimson"}, {id = "Roland_Showdown_Crimson"}}}, rules = {custom = {{id = "Roland_Eternally_Crimson"}, {id = "Roland_Showdown_Crimson"}}},
jokers = {{id = "j_Roland_crimson"}}, jokers = starting_jokers,
restrictions = crimson, restrictions = crimson,
pronouns = "she_her", pronouns = "she_her",
calculate = function(_, context) calculate = function(_, context)
@ -188,7 +190,7 @@ SMODS.Challenge {
end) end)
local mod = G.GAME.modifiers 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 debuff = context.debuff_card
local cards = G.jokers.cards local cards = G.jokers.cards
@ -239,7 +241,7 @@ SMODS.Challenge {
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Verdant", key = "Eternally_Verdant",
rules = {custom = {{id = "Roland_Eternally_Verdant"}, {id = "Roland_Showdown_Verdant"}}}, rules = {custom = {{id = "Roland_Eternally_Verdant"}, {id = "Roland_Showdown_Verdant"}}},
jokers = {{id = "j_Roland_verdant"}}, jokers = starting_jokers,
restrictions = verdant, restrictions = verdant,
pronouns = "she_her", pronouns = "she_her",
calculate = function(_, context) calculate = function(_, context)
@ -262,7 +264,7 @@ SMODS.Challenge {
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Violet", key = "Eternally_Violet",
rules = {custom = {{id = "Roland_Eternally_Violet"}, {id = "Roland_Showdown_Violet"}}}, rules = {custom = {{id = "Roland_Eternally_Violet"}, {id = "Roland_Showdown_Violet"}}},
jokers = {{id = "j_joker"}, {id = "j_Roland_violet"}}, jokers = starting_jokers,
restrictions = violet, restrictions = violet,
pronouns = "she_they", pronouns = "she_they",
calculate = function(_, context) calculate = function(_, context)

View file

@ -1,15 +1,14 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2]
local mod = SMODS.current_mod local mod = SMODS.current_mod
local cool_phones = SMODS.Mods.Roland.config.cool_phones
SMODS.Atlas { SMODS.Atlas {
key = "charm", key = "charm",
path = cool_phones and "phorm.png" or "charm.png", path = "charm.png",
px = 68, px = 68,
py = 68, py = 68,
} }
local _ = cool_phones and SMODS.Sound { SMODS.Sound {
key = "phone", key = "phone",
path = "phone.ogg", path = "phone.ogg",
} }
@ -17,7 +16,7 @@ local _ = cool_phones and SMODS.Sound {
local charm = (function() local charm = (function()
local x = 0 local x = 0
---@param tbl SMODS.Joker|{alerted?: boolean, artist?: string, equip?: fun(self: self, card: Card)} ---@param tbl SMODS.Joker|{alerted?: boolean, equip?: fun(self: self, card: Card)}
return function(tbl) return function(tbl)
q { q {
blocking = false, blocking = false,
@ -36,20 +35,19 @@ local charm = (function()
tbl.discovered = true tbl.discovered = true
tbl.pos = {x = x, y = 0} tbl.pos = {x = x, y = 0}
tbl.atlas = "charm" tbl.atlas = "charm"
tbl.artist = not cool_phones and "Roland_bakersdozenbagels" or nil
x = x + 1 x = x + 1
local orig_equip = tbl.equip local orig_equip = tbl.equip
function tbl.equip(...) function tbl.equip(...)
local _ = cool_phones and play_sound("Roland_phone", 1, 0.5) play_sound("Roland_phone", 1, 0.5)
if orig_equip then if orig_equip then
return orig_equip(...) return orig_equip(...)
end end
end end
local charm = Bakery_API.credit(Bakery_API.Charm(tbl)) local charm = Bakery_API.Charm(tbl)
charm:inject() charm:inject()
charm:process_loc_text() charm:process_loc_text()
SMODS.current_mod = current_mod SMODS.current_mod = current_mod
@ -261,7 +259,7 @@ local orig_apply_to_run = Tag.apply_to_run
function Tag:apply_to_run(...) function Tag:apply_to_run(...)
if G.GAME.Bakery_charm == "BakeryCharm_Roland_wii" and 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 return
end end

View file

@ -13,7 +13,7 @@ end
frozen_sound "_click" frozen_sound "_click"
local frozen_blocklist = {CardSleeve = true} 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 = { local needs_chip_mult_override = {
Bull = true, Bull = true,
@ -144,7 +144,7 @@ local function hook_proxy()
local orig_calculate = proxy.calculate local orig_calculate = proxy.calculate
function proxy:calculate(card, context, ...) 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, ...) return orig_calculate(self, card, context, ...)
else else
return SMODS.blueprint_effect(card, get_proxied_joker(card), context) 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 local orig_loc_vars = proxy.loc_vars
function proxy:loc_vars(info_queue, card, ...) 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, ...) return orig_loc_vars(self, info_queue, card, ...)
end end

View file

@ -216,9 +216,9 @@ joker {
SMODS.calculate_context {drawing_cards = true, draw = {G.deck.cards}} SMODS.calculate_context {drawing_cards = true, draw = {G.deck.cards}}
SMODS.calculate_context { 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, 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 if type(facing_blind) == "table" then
@ -488,9 +488,11 @@ joker {
end end
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) 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) end):map(function(v)
return SMODS.blueprint_effect(card, v, context) return SMODS.blueprint_effect(card, v, context)
end):where(type, "table"):map(function(v) end):where(type, "table"):map(function(v)
@ -498,6 +500,8 @@ joker {
return v return v
end):values():table() end):values():table()
) )
return merged
end, end,
} }
@ -648,7 +652,7 @@ joker {
local _ = card.area == G.jokers and self:cerulean(false) local _ = card.area == G.jokers and self:cerulean(false)
end, end,
cerulean = function(_, value) 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) 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" s.can = value or v.config.center.key == "j_Roland_cerulean"
end) end)
@ -675,7 +679,7 @@ joker {
{card = card, xmult = card.ability.extra.xmult} or nil {card = card, xmult = card.ability.extra.xmult} or nil
end, end,
crimson = function() 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 right = G.jokers.cards[v.rank + 1]
local debuffed_by_crimson = right and local debuffed_by_crimson = right and
@ -684,13 +688,11 @@ joker {
right.config.center.key == "j_Roland_crimson" right.config.center.key == "j_Roland_crimson"
if debuffed_by_crimson and v.ability.Roland_crimson == nil then 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:set_debuff(true)
v.ability.Roland_crimson = debuff
elseif not debuffed_by_crimson and v.ability.Roland_crimson ~= nil then 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.ability.Roland_crimson = nil
v:set_debuff(debuff)
end end
end) end)
end, end,
@ -757,7 +759,7 @@ joker {
key = "violet", key = "violet",
pronouns = "she_they", pronouns = "she_they",
idea = "hamester", idea = "hamester",
config = {extra = {before = 0.1, xmult = 6}}, config = {extra = {before = 1 / 3, xmult = 9}},
pixel_size = {w = 68, h = 68}, pixel_size = {w = 68, h = 68},
attributes = {"xmult"}, attributes = {"xmult"},
cost = 6, cost = 6,
@ -936,14 +938,10 @@ joker {
local extra, numerator, xmult = card.ability.extra, 1, 1 local extra, numerator, xmult = card.ability.extra, 1, 1
if G.GAME.blind.name == "bl_mp_nemesis" then if G.GAME.blind.name == "bl_mp_nemesis" then
local n, d = SMODS.get_probability_vars(card, 1, extra.odds, self.key) return {card = card, xmult = extra.odds}
return n < d and {card = card, xmult = extra.odds} or nil
end end
-- 2^38 is the first iteration displayed as scientific notation. for _ = 1, 256 do
local max, overflown = 38, false
for i = 1, max do
local key = "RolandMartingale" .. tostring(G.GAME.modifiers.Roland_martingale_seed or "") local key = "RolandMartingale" .. tostring(G.GAME.modifiers.Roland_martingale_seed or "")
if SMODS.pseudorandom_probability(card, self.key, 1, extra.odds, key) then if SMODS.pseudorandom_probability(card, self.key, 1, extra.odds, key) then
@ -955,18 +953,9 @@ joker {
xmult = xmult * extra.odds xmult = xmult * extra.odds
local message = number_format(numerator) .. "/" .. number_format(xmult) local message = number_format(numerator) .. "/" .. number_format(xmult)
SMODS.calculate_effect({card = card, repetitions = 1, message = message, message_card = card}, card) SMODS.calculate_effect({card = card, repetitions = 1, message = message, message_card = card}, card)
overflown = overflown or i == max
end 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) SMODS.calculate_effect({card = card, xmult = xmult}, card)
end
end, end,
} }
@ -1212,10 +1201,7 @@ joker {
local delta = (context.cardarea == G.play and not card.blueprint) and 1 or 0 local delta = (context.cardarea == G.play and not card.blueprint) and 1 or 0
extra.scored = extra.scored + delta extra.scored = extra.scored + delta
if not extra.getting_flipped and if not extra.flipped and extra.scored == extra.required then
not extra.flipped and
extra.scored == extra.required then
extra.getting_flipped = true
Bakery_API.flip_double_sided(card) Bakery_API.flip_double_sided(card)
q(function() q(function()
@ -1223,7 +1209,7 @@ joker {
end) end)
end end
return extra.scored >= extra.required and {card = card, xmult = card.ability.extra.xmult} or return extra.scored > extra.required and {card = card, xmult = card.ability.extra.xmult} or
(delta > 0 and { (delta > 0 and {
message = extra.scored .. "/" .. extra.required, message = extra.scored .. "/" .. extra.required,
colour = G.C.JOKER_GREY, colour = G.C.JOKER_GREY,
@ -1258,7 +1244,7 @@ joker {
return {card = card, numerator = extra.probability} return {card = card, numerator = extra.probability}
end 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 extra.probability = extra.reset
return {message = localize "k_reset", colour = G.C.RED, message_card = card, repetitions = 0} return {message = localize "k_reset", colour = G.C.RED, message_card = card, repetitions = 0}
end end

View file

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

View file

@ -1,76 +0,0 @@
---@meta
---@alias Attributes "mult"|"chips"|"xmult"|"xchips"|"score"|"xscore"|"blindsize"|"xblindsize"|"balance"|"swap"|"retrigger"|"scaling"|"reset"|"suit"|"diamonds"|"hearts"|"spades"|"clubs"|"hand_type"|"rank"|"ace"|"two"|"three"|"four"|"five"|"six"|"seven"|"eight"|"nine"|"ten"|"jack"|"queen"|"king"|"face"|"economy"|"generation"|"destroy_card"|"hands"|"discard"|"hand_size"|"chance"|"joker_slot"|"mod_chance"|"copying"|"full_deck"|"passive"|"joker"|"tarot"|"planet"|"spectral"|"enhancements"|"seals"|"editions"|"tag"|"skip"|"modify_card"|"perma_bonus"|"prevents_death"|"boss_blind"|"reroll"|"on_sell"|"sell_value"|"food"|"space"|"bakery_double_sided"|"bakery_usable"|"bakery_werewolf"
---@type Card[]
CardArea.cards = CardArea.cards
--- @overload fun(tbl: SMODS.Joker): SMODS.GameObject
Bakery_API.Charm = Bakery_API.Charm
--- @generic T: SMODS.GameObject
--- @param obj T
--- @return T
function Bakery_API.credit(obj)
error(obj)
end
---@type table
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?
CardSleeves = CardSleeves
--- @type table|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}
SMODS.Atlas.image_data = SMODS.Atlas.image_data
--- @type table
Talisman = Talisman
--- @type fun(obj: any): number
to_number = to_number
-- This exists to remove the @deprecated warning.
---Returns the elements from the given `list`. This function is equivalent to
---```lua
--- return list[i], list[i+1], ···, list[j]
---```
---
---
---[View documents](command:extension.lua.doc?["en-us/52/manual.html/pdf-unpack"])
---
---@generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@param list {
--- [1]?: T1,
--- [2]?: T2,
--- [3]?: T3,
--- [4]?: T4,
--- [5]?: T5,
--- [6]?: T6,
--- [7]?: T7,
--- [8]?: T8,
--- [9]?: T9,
--- [10]?: T10,
---}
---@param i? integer
---@param j? integer
---@return T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@nodiscard
function unpack(list, i, j)
error {list, i, j}
end

View file

@ -1,59 +1,5 @@
local f = assert(SMODS.load_file "src/lib/funky.lua")() or require "lib.funky" 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) local function protect(fun)
return function() return function()
local res, ret = pcall(fun) local res, ret = pcall(fun)
@ -75,20 +21,69 @@ local function protect_ev(fun)
return Event { return Event {
blocking = false, blocking = false,
no_delete = true, no_delete = true,
func = protect(function() func = function()
if not Bakery_API or not Bakery_API.credit then if not Bakery_API or not Bakery_API.credit then
return false return false
end end
Bakery_API.credit(fun) Bakery_API.credit(fun)
end), return true
end,
} }
else else
fun.func = protect(fun.func or fun[1]) fun.func = protect(fun.func)
return getmetatable(fun) == Event and fun or Event(fun) return getmetatable(fun) == Event and fun or Event(fun)
end end
end end
---@alias Attributes "mult"|"chips"|"xmult"|"xchips"|"score"|"xscore"|"blindsize"|"xblindsize"|"balance"|"swap"|"retrigger"|"scaling"|"reset"|"suit"|"diamonds"|"hearts"|"spades"|"clubs"|"hand_type"|"rank"|"ace"|"two"|"three"|"four"|"five"|"six"|"seven"|"eight"|"nine"|"ten"|"jack"|"queen"|"king"|"face"|"economy"|"generation"|"destroy_card"|"hands"|"discard"|"hand_size"|"chance"|"joker_slot"|"mod_chance"|"copying"|"full_deck"|"passive"|"joker"|"tarot"|"planet"|"spectral"|"enhancements"|"seals"|"editions"|"tag"|"skip"|"modify_card"|"perma_bonus"|"prevents_death"|"boss_blind"|"reroll"|"on_sell"|"sell_value"|"food"|"space"|"bakery_double_sided"
if false then
-- This allows for better type inference.
SMODS.Mods.Roland.config = require "config"
---@type Card[]
CardArea.cards = CardArea.cards
---@type userdata|{getWidth: fun(self: self): number}
SMODS.Atlas.image_data = SMODS.Atlas.image_data
--- @type { constants?: { TEN: table }, new: (fun(self: self, arr?: number[], sign?: number, noNormalize?: boolean): table), pow: (fun(x: number, y: number): number) }
_G["Big"] = _G["Big"]
--- @type fun(area: CardArea, ...: ...): Card
create_card_for_shop = create_card_for_shop
-- This exists to remove the @deprecated warning.
---Returns the elements from the given `list`. This function is equivalent to
---```lua
--- return list[i], list[i+1], ···, list[j]
---```
---
---
---[View documents](command:extension.lua.doc?["en-us/52/manual.html/pdf-unpack"])
---
---@generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@param list {
--- [1]?: T1,
--- [2]?: T2,
--- [3]?: T3,
--- [4]?: T4,
--- [5]?: T5,
--- [6]?: T6,
--- [7]?: T7,
--- [8]?: T8,
--- [9]?: T9,
--- [10]?: T10,
---}
---@param i? integer
---@param j? integer
---@return T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@nodiscard
function unpack(list, i, j)
error {list, i, j}
end
end
--- Queues an event to be run. --- Queues an event to be run.
--- Note that events added this way implicitly `return true` unless you explicitly `return false`. --- Note that events added this way implicitly `return true` unless you explicitly `return false`.
--- For `front`; boolean `true` to add the event to the front of the queue, rather than the end. --- For `front`; boolean `true` to add the event to the front of the queue, rather than the end.
@ -110,35 +105,4 @@ local function u()
G.STATE ~= G.STATES.PLAY_TAROT) G.STATE ~= G.STATES.PLAY_TAROT)
end end
--- Creates one or more cards. return {f, q, u}
---@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,
})}

View file

@ -52,32 +52,18 @@ q {
-- G.ARGS.LOC_COLOURS["Bakery_credit_bg_Roland_" .. k] = v.bg -- G.ARGS.LOC_COLOURS["Bakery_credit_bg_Roland_" .. k] = v.bg
end) end)
if not SMODS.Mods.DebugPlus or not SMODS.Mods.Roland.config.import_funky then if SMODS.Mods.DebugPlus and SMODS.Mods.Roland.config.import_funky then
return _G.f, _G.q, _G.u = unpack(qol)
end end
_G.f, _G.q, _G.u, _G.c = unpack(qol)
end, end,
} }
f { f {"challenge", "spectral", "edition", "tweaks", "blind", "charm", "joker", "tarot", "back", "seal", "tag"}
"challenge", :each(function(v)
"spectral",
"edition",
"tweaks",
"blind",
"charm",
"joker",
"tarot",
"back",
"seal",
"tag",
"voucher",
}:each(function(v)
assert(SMODS.load_file("src/" .. v .. ".lua"))(qol) assert(SMODS.load_file("src/" .. v .. ".lua"))(qol)
end) end)
if Balatest then if _G["Balatest"] then
f {"joker", "blind", "spectral"}:each(function(v) f {"joker", "blind", "spectral"}:each(function(v)
assert(SMODS.load_file("src/tests/" .. v .. ".tests.lua"))(qol) assert(SMODS.load_file("src/tests/" .. v .. ".tests.lua"))(qol)
end) end)
@ -113,7 +99,6 @@ function SMODS.current_mod.config_tab()
config = {minw = 1, minh = 1, align = "tl", padding = 0.1, colour = G.C.CLEAR}, config = {minw = 1, minh = 1, align = "tl", padding = 0.1, colour = G.C.CLEAR},
nodes = { nodes = {
toggle "animated_icon", toggle "animated_icon",
toggle "cool_phones",
toggle "faster_planets", toggle "faster_planets",
-- toggle "illusion_seal", -- toggle "illusion_seal",
-- toggle "no_wild_debuff", -- toggle "no_wild_debuff",
@ -121,7 +106,7 @@ function SMODS.current_mod.config_tab()
toggle "equinox_assist", toggle "equinox_assist",
SMODS.Mods.DebugPlus and toggle "import_funky", SMODS.Mods.DebugPlus and toggle "import_funky",
G.P_CENTERS.c_Bakery_Scribe and toggle "scribable_basket", G.P_CENTERS.c_Bakery_Scribe and toggle "scribable_basket",
Talisman and toggle "harsh_ante_scaling", _G["Talisman"] and toggle "harsh_ante_scaling",
}, },
}}, }},
} }

View file

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

View file

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

View file

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

View file

@ -2,6 +2,22 @@ if not Balatest then
return return
end 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 { Balatest.TestPlay {
category = {"spectral", "void"}, category = {"spectral", "void"},
name = "spectral", 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) SMODS.Joker:take_ownership("joker", {cost = 1}, true)
local orig_set_debuff = Card.set_debuff local orig_set_debuff = Card.set_debuff
function Card:set_debuff(should_debuff, ...) function Card:set_debuff(...)
if self.ability.Roland_crimson ~= nil then
self.ability.Roland_crimson = not not should_debuff
return
end
if SMODS.get_enhancements(self).m_wild and SMODS.Mods.Roland.config.no_wild_debuff then if SMODS.get_enhancements(self).m_wild and SMODS.Mods.Roland.config.no_wild_debuff then
self.debuff = false self.debuff = false
else else
orig_set_debuff(self, should_debuff, ...) orig_set_debuff(self, ...)
end end
end end
@ -67,10 +62,7 @@ function G.FUNCS.use_card(e, ...)
discover_card(ref.config.center) discover_card(ref.config.center)
ref:use_consumeable(ref.area) ref:use_consumeable(ref.area)
SMODS.calculate_context {area = ref.area, consumeable = ref, using_consumeable = true} SMODS.calculate_context {area = ref.area, consumeable = ref, using_consumeable = true}
q(function()
ref:remove() ref:remove()
end)
if normal then if normal then
return return
@ -102,11 +94,11 @@ local orig_get_blind_amount = get_blind_amount
---@param ante number ---@param ante number
---@return table|number ---@return table|number
local function blind(ante) local function blind(ante)
return ante == 39 and 1e294 or (to_number or f.id)(orig_get_blind_amount(ante)) return ante == 39 and 1e294 or (_G["to_number"] or f.id)(orig_get_blind_amount(ante))
end end
local function no_harsh_ante_scaling() local function no_harsh_ante_scaling()
return not Talisman or not SMODS.Mods.Roland.config.harsh_ante_scaling return not _G["Talisman"] or not SMODS.Mods.Roland.config.harsh_ante_scaling
end end
function get_blind_amount(ante, ...) function get_blind_amount(ante, ...)
@ -120,10 +112,10 @@ function get_blind_amount(ante, ...)
return 1 / 0 return 1 / 0
end end
local rem = tonumber(blind((ante % loop) + 1)) local big, rem = _G["Big"], tonumber(blind((ante % loop) + 1))
return ante / 15 >= loop and Big:new(f(blind(ante - (loop * 15))):map(f.const(10)):table()) or return ante / 15 >= loop and big:new(f(blind(ante - (loop * 15))):map(f.const(10)):table()) or
(ante / 9 >= loop and Big:new(f(ante / loop - 8):map(f.const(10)):concat {rem}:table()) or (ante / 9 >= loop and big:new(f(ante / loop - 8):map(f.const(10)):concat {rem}:table()) or
(ante / 2 >= loop and Big:new {rem, ante / loop} or (ante / 2 >= loop and big:new {rem, ante / loop} or
(Big.constants and Big.constants.TEN or Big:new {10}):pow(rem))) (big.constants and big.constants.TEN or big:new {10}):pow(rem)))
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,
}