Add 2 new jokers, add credit, fix credit

This commit is contained in:
Emik 2026-06-17 02:43:22 +02:00
parent bf0562dfab
commit 3f7a8685be
Signed by: emik
GPG key ID: 6B0CD72A5E503BDF
19 changed files with 230 additions and 144 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 66 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: 82 KiB

After

Width:  |  Height:  |  Size: 82 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: 242 KiB

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

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

View file

@ -355,6 +355,18 @@ return {
"{C:attention}#2# {}scored cards",
},
},
j_Roland_violet = {
name = "Violet Vessel",
text = {
"{X:mult,C:white}X#1#{} Mult,",
"but {X:mult,C:white}X#2#{} Mult",
"{C:attention}before {}scoring",
},
},
j_Roland_venerable = {
name = "Venerable Visage",
text = {"{X:red,C:white}X#1#{} discards each round"},
},
j_Roland_yard = {
name = "Yard Sale",
text = {

View file

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

View file

@ -3,14 +3,20 @@ local f, q = unpack(... or require "lib.shared")
local blind = (function()
local y = 0
---@param tbl SMODS.Blind
---@param tbl SMODS.Blind|{idea?: string}
---@return SMODS.Blind
return function(tbl)
tbl.pos = {x = 0, y = y}
tbl.atlas = "blind"
local ret = SMODS.Blind(tbl)
tbl.idea = tbl.idea and "Roland_" .. tbl.idea or nil
local blind = SMODS.Blind(tbl)
y = y + 1
return ret
q(function()
Bakery_API.credit(blind)
end)
return blind
end
end)()
@ -237,6 +243,7 @@ blind {
blind {
key = "blizzard",
idea = "redstoad",
boss = {min = 3},
boss_colour = HEX "102a41ff",
pronouns = "it_its",

View file

@ -22,10 +22,11 @@ local joker = (function()
return ret
end
---@param tbl SMODS.Joker|{artist?: string, Bakery_can_use: (fun(self: self, card: Card): boolean?), Bakery_use_button_text: (fun(self: self, card: Card): string|table|nil), Bakery_use_joker: fun(self: self, card: Card), attributes?: Attributes[]}
---@param tbl SMODS.Joker|{artist?: string, idea?: string, Bakery_can_use: (fun(self: self, card: Card): boolean?), Bakery_use_button_text: (fun(self: self, card: Card): string|table|nil), Bakery_use_joker: fun(self: self, card: Card), attributes?: Attributes[]}
return function(tbl)
tbl.pos = inc()
tbl.atlas = "joker"
tbl.idea = tbl.idea and "Roland_" .. tbl.idea or nil
tbl.artist = tbl.artist and "Roland_" .. tbl.artist or nil
if ((tbl.config or {}).extra or {}).flipped ~= nil then
@ -38,6 +39,8 @@ local joker = (function()
q(function()
Bakery_API.credit(joker)
end)
return joker
end
end)()
@ -161,6 +164,7 @@ joker {
key = "mrsbones",
pronouns = "she_her",
artist = "ghostlyfield",
idea = "redstoad",
config = {extra = {xmult = 4, requirement = 4}},
attributes = {"xmult"},
cost = G.P_CENTERS.j_mr_bones.cost - 1,
@ -197,6 +201,7 @@ joker {
key = "sunny",
pronouns = "it_its",
artist = "char",
idea = "redstoad",
attributes = {"food", "on_sell"},
cost = 2,
rarity = 1,
@ -510,6 +515,7 @@ joker {
key = "sapling",
pronouns = "they_them",
artist = "char",
idea = "redstoad",
attributes = {"mult", "suit"},
cost = 4,
rarity = 1,
@ -580,103 +586,6 @@ joker {
end,
}
joker {
key = "bulldozer",
pronouns = "she_her",
attributes = {"xmult"},
cost = 6,
rarity = 2,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(self, _, card)
return {vars = {self:xmult_frozen(card)}}
end,
calculate = function(self, card, context)
return (context.joker_main or context.forcetrigger) and {card = card, xmult = self:xmult_frozen(card)} or nil
end,
xmult_frozen = function(self, card)
if not is_frozen(card) then
return self.xmult()
end
local ability = card.Roland_frozen_ability or {}
card.Roland_frozen_ability = ability
ability.Roland_frozen_xmult = ability.Roland_frozen_xmult or self.xmult()
return ability.Roland_frozen_xmult
end,
xmult = function()
local function mult(id)
local next = {Select = true, Upcoming = true}
local states = G.GAME.round_resets.blind_states
local choices = G.GAME.round_resets.blind_choices
return next[states[id]] and G.P_BLINDS[choices[id]].mult
end
local m = ((G.GAME or {}).blind or {}).mult
return m and m ~= 0 and m or mult "Small" or mult "Big" or mult "Boss" or 1
end,
}
joker {
key = "phytoestrogens",
pronouns = "she_her",
artist = "ghostlyfield",
config = {extra = {xmult = 0.3}},
attributes = {"mult", "xmult"},
cost = 8,
rarity = 3,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(_, _, card)
return {vars = {card.ability.extra.xmult}}
end,
calculate = function(_, card, context)
if not context.joker_main and not context.forcetrigger then
return
end
SMODS.calculate_effect({mult = hand_chips}, card)
SMODS.calculate_effect({xmult = card.ability.extra.xmult}, card)
end,
}
joker {
key = "nilly",
pronouns = "any_all",
artist = "ghostlyfield",
config = {extra = {flipped = false}},
attributes = {"xmult", "bakery_double_sided"},
cost = 0,
rarity = 2,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(_, _, card)
local key = card.ability.extra.flipped and "b_Roland_enabled" or "b_Roland_disabled"
return {vars = {localize {type = "variable", key = key}}}
end,
calculate = function(_, card, context)
if (not context.joker_main or not card.ability.extra.flipped) and not context.forcetrigger then
return
end
mult = mod_mult(0)
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
return {sound = "Roland_nilly", message = "0X " .. localize "k_mult", colour = G.C.MULT, message_card = card}
end,
Bakery_can_use = function(_, card)
return not card.debuff
end,
Bakery_use_button_text = function(_, card)
return localize {type = "variable", key = card.debuff and "b_Roland_debuffed" or "b_Roland_toggle"}
end,
Bakery_use_joker = function(_, card)
local _ = card.debuff or Bakery_API.flip_double_sided(card)
end,
}
joker {
key = "amber",
pronouns = "they_them",
@ -731,7 +640,7 @@ joker {
pixel_size = {w = 68, h = 68},
attributes = {"xmult"},
cost = 6,
rarity = 2,
rarity = 3,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
@ -852,9 +761,163 @@ function CardArea:unhighlight_all(...)
return orig_unhighlight_all(self, ...)
end
joker {
key = "violet",
pronouns = "she_they",
idea = "hamester",
config = {extra = {before = 1 / 3, xmult = 9}},
pixel_size = {w = 68, h = 68},
attributes = {"xmult"},
cost = 6,
rarity = 3,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(_, _, card)
local extra = card.ability.extra
return {vars = {extra.xmult, extra.before}}
end,
calculate = function(_, card, context)
local extra = card.ability.extra
return (context.joker_main or context.forcetrigger) and
{card = card, xmult = extra.xmult} or
(context.before and {card = card, xmult = extra.before} or nil)
end,
}
joker {
key = "venerable",
pronouns = "any_all",
config = {extra = {xdiscard = 3}},
pixel_size = {w = 68, h = 68},
attributes = {"discard", "passive"},
cost = 6,
rarity = 3,
eternal_compat = true,
blueprint_compat = false,
perishable_compat = true,
loc_vars = function(_, _, card)
local extra = card.ability.extra
return {vars = {extra.xdiscard}}
end,
add_to_deck = function(self, card)
self:venerable(math.ceil(G.GAME.round_resets.discards * card.ability.extra.xdiscard))
end,
remove_from_deck = function(self, card)
self:venerable(math.floor(G.GAME.round_resets.discards / card.ability.extra.xdiscard))
end,
venerable = function(_, amount)
local round_resets = G.GAME.round_resets
local discards = round_resets.discards
round_resets.discards = amount
ease_discard(round_resets.discards - discards)
end,
}
joker {
key = "bulldozer",
pronouns = "she_her",
idea = "redstoad",
attributes = {"xmult"},
cost = 6,
rarity = 2,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(self, _, card)
return {vars = {self:xmult_frozen(card)}}
end,
calculate = function(self, card, context)
return (context.joker_main or context.forcetrigger) and {card = card, xmult = self:xmult_frozen(card)} or nil
end,
xmult_frozen = function(self, card)
if not is_frozen(card) then
return self.xmult()
end
local ability = card.Roland_frozen_ability or {}
card.Roland_frozen_ability = ability
ability.Roland_frozen_xmult = ability.Roland_frozen_xmult or self.xmult()
return ability.Roland_frozen_xmult
end,
xmult = function()
local function mult(id)
local next = {Select = true, Upcoming = true}
local states = G.GAME.round_resets.blind_states
local choices = G.GAME.round_resets.blind_choices
return next[states[id]] and G.P_BLINDS[choices[id]].mult
end
local m = ((G.GAME or {}).blind or {}).mult
return m and m ~= 0 and m or mult "Small" or mult "Big" or mult "Boss" or 1
end,
}
joker {
key = "phytoestrogens",
pronouns = "she_her",
artist = "ghostlyfield",
config = {extra = {xmult = 0.3}},
attributes = {"mult", "xmult"},
cost = 8,
rarity = 3,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(_, _, card)
return {vars = {card.ability.extra.xmult}}
end,
calculate = function(_, card, context)
if not context.joker_main and not context.forcetrigger then
return
end
SMODS.calculate_effect({mult = hand_chips}, card)
SMODS.calculate_effect({xmult = card.ability.extra.xmult}, card)
end,
}
joker {
key = "nilly",
pronouns = "any_all",
artist = "ghostlyfield",
idea = "redstoad",
config = {extra = {flipped = false}},
attributes = {"xmult", "bakery_double_sided"},
cost = 0,
rarity = 2,
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(_, _, card)
local key = card.ability.extra.flipped and "b_Roland_enabled" or "b_Roland_disabled"
return {vars = {localize {type = "variable", key = key}}}
end,
calculate = function(_, card, context)
if (not context.joker_main or not card.ability.extra.flipped) and not context.forcetrigger then
return
end
mult = mod_mult(0)
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
return {sound = "Roland_nilly", message = "0X " .. localize "k_mult", colour = G.C.MULT, message_card = card}
end,
Bakery_can_use = function(_, card)
return not card.debuff
end,
Bakery_use_button_text = function(_, card)
return localize {type = "variable", key = card.debuff and "b_Roland_debuffed" or "b_Roland_toggle"}
end,
Bakery_use_joker = function(_, card)
local _ = card.debuff or Bakery_API.flip_double_sided(card)
end,
}
joker {
key = "martingale",
pronouns = "he_him",
idea = "redstoad",
config = {extra = {odds = 2}},
attributes = {"xmult", "chance"},
cost = 8,
@ -1033,6 +1096,8 @@ end
joker {
key = "artemis",
pronouns = "any_all",
artist = "hamester",
idea = "redstoad",
cost = 6,
rarity = 2,
config = {extra = {
@ -1097,6 +1162,7 @@ joker {
joker {
key = "excalibur",
pronouns = "he_him",
idea = "redstoad",
cost = 8,
rarity = 3,
config = {extra = {

View file

@ -6,31 +6,19 @@ local function protect(fun)
sendErrorMessage(tostring(ret), "Roland")
end
return not res or ret ~= false
return not res or ret ~= false and not (SMODS.current_mod and ret ~= true)
end
end
local function protect_ev(fun, retries)
if type(fun) == "function" and retries then
local orig_fun = fun
fun = function(...)
local ret = orig_fun(...)
if ret == true then
retries = retries - 1
return retries <= 0
else
return ret
end
end
end
local function protect_ev(fun)
local no_delete = not not SMODS.current_mod
if type(fun) == "table" then
fun.func = protect(fun.func)
fun.no_delete = no_delete
fun = getmetatable(fun) == Event and fun or Event(fun)
elseif type(fun) == "function" then
fun = Event {func = protect(fun)}
fun = Event {func = protect(fun), no_delete = no_delete}
else
error("Expected a function or event, got a " .. type(fun), 3)
end
@ -89,11 +77,12 @@ end
local f = assert(SMODS.load_file "src/lib/funky.lua")() or require "lib.funky"
--- Queues an event to be run.
--- Note that events added this way implicitly `return true` unless you explicitly `return false`, unlike the vanilla ones.
--- @param fun (fun():boolean?)|Event The event or a function to run turn into an event.
--- @param front? boolean `true` to add the event to the front of the queue, rather than the end.
local function q(fun, front)
G.E_MANAGER:add_event(protect_ev(fun, SMODS.current_mod and 100), nil, front)
--- 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.
--- @param fun fun():boolean?|{front?: boolean}|Event The event or a function to run turn into an event.
local function q(fun)
local ev = protect_ev(fun)
G.E_MANAGER:add_event(ev, nil, ev.front)
end
--- Determines if a center is allowed to be usable.

View file

@ -1,17 +1,6 @@
local qol = assert(SMODS.load_file "src/lib/shared.lua")() or require "lib.shared"
local f, q = unpack(qol)
f {"challenge", "spectral", "edition", "tweaks", "blind", "charm", "joker", "tarot", "back", "seal", "tag"}
:each(function(v)
assert(SMODS.load_file("src/" .. v .. ".lua"))(qol)
end)
if _G["Balatest"] then
f {"joker", "blind", "spectral"}:each(function(v)
assert(SMODS.load_file("src/tests/" .. v .. ".tests.lua"))(qol)
end)
end
q(function()
local contributors = (Bakery_API or {}).contributors
@ -20,7 +9,7 @@ q(function()
end
-- Special shoutout to all contributors. <3
f {
local credits = {
aster = {
name = "asterSSH",
fg = HEX "f8f8f2ff",
@ -41,14 +30,37 @@ q(function()
fg = HEX "ffffffff",
bg = HEX "b290e6ff",
},
}:each(function(v, k)
hamester = {
name = "Hamester",
fg = HEX "ffffffff",
bg = HEX "ffa100ff",
},
redstoad = {
name = "RedsToad",
fg = HEX "ffffffff",
bg = HEX "da4044ff",
},
}
f(credits):each(function(v, k)
contributors["Roland_" .. k] = v
end)
if SMODS.Mods.DebugPlus and SMODS.Mods.Roland.config.import_funky then
_G.f, _G.q, _G.u = unpack(qol)
end
end, true)
end)
f {"challenge", "spectral", "edition", "tweaks", "blind", "charm", "joker", "tarot", "back", "seal", "tag"}
:each(function(v)
assert(SMODS.load_file("src/" .. v .. ".lua"))(qol)
end)
if _G["Balatest"] then
f {"joker", "blind", "spectral"}:each(function(v)
assert(SMODS.load_file("src/tests/" .. v .. ".tests.lua"))(qol)
end)
end
local function toggle(id)
return create_toggle {
@ -71,8 +83,6 @@ SMODS.Atlas {
atlas_table = animated and "ANIMATION_ATLAS" or "ASSET_ATLAS",
}
SMODS.current_mod.qol = qol
function SMODS.current_mod.config_tab()
return {
n = G.UIT.ROOT,
@ -93,3 +103,5 @@ function SMODS.current_mod.config_tab()
}},
}
end
SMODS.current_mod.qol = qol

View file

@ -11,14 +11,14 @@ local spectral = (function()
tbl.atlas = "spectral"
tbl.pos = {x = x, y = 0}
tbl.artist = tbl.artist and "Roland_" .. tbl.artist or nil
local ret = SMODS.Consumable(tbl)
local spectral = SMODS.Consumable(tbl)
x = x + 1
q(function()
Bakery_API.credit(ret)
Bakery_API.credit(spectral)
end)
return ret
return spectral
end
end)()