Compare commits

..

8 commits
3.0.4 ... main

Author SHA1 Message Date
713276e568
Improve performance of Arctic Circle 2026-10-14 18:23:58 +02:00
4a922f8d0d
Use nodes directly instead of finding them 2026-10-14 18:22:43 +02:00
3119a8561d
Add unused patch 2026-10-14 18:16:47 +02:00
7b491d3bb3 Bump version
Signed-off-by: emik <emik.cuber@gmail.com>
2026-10-13 12:32:32 +00:00
a762cff1fe Fix crash related to Suitable
Signed-off-by: emik <emik.cuber@gmail.com>
2026-10-13 12:31:56 +00:00
8a18b78d2c
Add unused utility function
(purposely not bumping version)
2026-10-13 03:27:33 +02:00
d1ca223855
Patch reroll exploit, rebalance challenges 2026-10-11 22:39:41 +02:00
a99b47a646
Check discovery instead 2026-10-10 21:37:50 +02:00
6 changed files with 86 additions and 36 deletions

View file

@ -212,3 +212,13 @@ match_indent = true
# position = "at" # position = "at"
# payload = 'local cmdName = inputText' # payload = 'local cmdName = inputText'
# match_indent = true # match_indent = true
# [[patches]]
# [patches.pattern]
# target = '=[SMODS DebugPlus "debugplus/util.lua"]'
# pattern = "function global.stringifyTable(tab, depth, num, dec, indent)"
# position = "after"
# payload = """if type(tab) == "table" and type(tab.table) == "function" then
# tab = tab:table()
# end"""
# match_indent = true

View file

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

View file

@ -232,24 +232,21 @@ blind {
boss = {min = 3}, boss = {min = 3},
boss_colour = HEX "102a41ff", boss_colour = HEX "102a41ff",
pronouns = "it_its", pronouns = "it_its",
defeat = function(self) defeat = function()
self.cards():where "Roland_blizzard":each(set_freeze()) f(G.I.CARD):where "Roland_blizzard":each(set_freeze())
G.GAME.blind.disabled = true G.GAME.blind.disabled = true
end, end,
disable = function(self) disable = function(self)
self:defeat() self:defeat()
end, end,
calculate = function(self, b, context) calculate = function(_, b, context)
return not b.disabled and return not b.disabled and
context.hand_drawn and context.hand_drawn and
self.cards() f(G.I.CARD)
: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()
return f(G):where(getmetatable, CardArea):flatmap("cards", ipairs)
end,
} }
blind { blind {

View file

@ -34,7 +34,7 @@ local function is_center_banned_from_pastry(v)
p_arcana_mega_2 = true, p_arcana_mega_2 = true,
} }
local pastries_targets = {Enhanced = true, Joker = true, Tarot = true, Spectral = true} local pastries_targets = {Enhanced = true, Joker = true, Tarot = true, Spectral = true, Voucher = true}
return arcana_boosters[v.key] or pastries_targets[v.set] and is_banned_from_pastry(v) return arcana_boosters[v.key] or pastries_targets[v.set] and is_banned_from_pastry(v)
end end
@ -48,7 +48,7 @@ end
---@return fun(v: SMODS.Blind, k: string): boolean? ---@return fun(v: SMODS.Blind, k: string): boolean?
local function is_showdown_except(key) local function is_showdown_except(key)
return function(v, k) return function(v, k)
return v.boss and v.boss.showdown and key ~= k return key ~= k and v.boss and (v.boss.hardcore or v.boss.no_orb or v.boss.showdown)
end end
end end
@ -129,6 +129,7 @@ local spin_to_win = SMODS.Challenge {
jokers = f(4):map(f.const {id = "j_joker", eternal = true}):table(), jokers = f(4):map(f.const {id = "j_joker", eternal = true}):table(),
restrictions = {banned_cards = {{id = "c_Roland_coolheaded"}}}, restrictions = {banned_cards = {{id = "c_Roland_coolheaded"}}},
pronouns = "they_them", pronouns = "they_them",
rotation = 0,
apply = function(self) apply = function(self)
self.rotation = 0 self.rotation = 0
end, end,
@ -140,7 +141,6 @@ local spin_to_win = SMODS.Challenge {
context.card.ability.extra.rotation = self.rotation context.card.ability.extra.rotation = self.rotation
self.rotation = self.rotation + 1 self.rotation = self.rotation + 1
end, end,
rotation = 0,
} }
SMODS.Challenge { SMODS.Challenge {
@ -157,7 +157,7 @@ SMODS.Challenge {
restrictions = amber, restrictions = amber,
pronouns = "they_them", pronouns = "they_them",
unlocked = function() unlocked = function()
return G.P_CENTERS.j_Roland_amber and G.P_CENTERS.j_Roland_amber.check_for_unlock() return G.P_CENTERS.j_Roland_amber.discovered
end, end,
calculate = function(_, context) calculate = function(_, context)
local function slide(pitch) local function slide(pitch)
@ -199,7 +199,7 @@ SMODS.Challenge {
restrictions = cerulean, restrictions = cerulean,
pronouns = "she_her", pronouns = "she_her",
unlocked = function() unlocked = function()
return G.P_CENTERS.j_Roland_cerulean and G.P_CENTERS.j_Roland_cerulean.check_for_unlock() return G.P_CENTERS.j_Roland_cerulean.discovered
end, end,
calculate = function(_, context) calculate = function(_, context)
if not context.hand_drawn then if not context.hand_drawn then
@ -229,7 +229,7 @@ SMODS.Challenge {
restrictions = crimson, restrictions = crimson,
pronouns = "she_her", pronouns = "she_her",
unlocked = function() unlocked = function()
return G.P_CENTERS.j_Roland_crimson and G.P_CENTERS.j_Roland_crimson.check_for_unlock() return G.P_CENTERS.j_Roland_crimson.discovered
end, end,
calculate = function(_, context) calculate = function(_, context)
local _ = context.blind_defeated and f(G.jokers.cards):each(function(v) local _ = context.blind_defeated and f(G.jokers.cards):each(function(v)
@ -292,7 +292,7 @@ SMODS.Challenge {
restrictions = verdant, restrictions = verdant,
pronouns = "she_her", pronouns = "she_her",
unlocked = function() unlocked = function()
return G.P_CENTERS.j_Roland_verdant and G.P_CENTERS.j_Roland_verdant.check_for_unlock() return G.P_CENTERS.j_Roland_verdant.discovered
end, end,
calculate = function(_, context) calculate = function(_, context)
if context.setting_blind then if context.setting_blind then
@ -318,7 +318,7 @@ SMODS.Challenge {
restrictions = violet, restrictions = violet,
pronouns = "she_they", pronouns = "she_they",
unlocked = function() unlocked = function()
return G.P_CENTERS.j_Roland_violet and G.P_CENTERS.j_Roland_violet.check_for_unlock() return G.P_CENTERS.j_Roland_violet.discovered
end, end,
calculate = function(_, context) calculate = function(_, context)
if not context.setting_blind then if not context.setting_blind then

View file

@ -589,9 +589,7 @@ joker {
end end
return SMODS.merge_effects( return SMODS.merge_effects(
f(G) f(G.I.CARD)
:where(getmetatable, CardArea)
:flatmap("cards", ipairs)
:where(is_frozen) :where(is_frozen)
:map(copy(card, context)) :map(copy(card, context))
:where(type, "table") :where(type, "table")
@ -741,8 +739,13 @@ joker {
crimson = function() crimson = function()
local _ = G.jokers and f(G.jokers.cards, f.revpairs):where(is_frozen, false):each(function(v) local _ = G.jokers and f(G.jokers.cards, f.revpairs):where(is_frozen, false):each(function(v)
local right = G.jokers.cards[v.rank + 1] local right = G.jokers.cards[v.rank + 1]
local orig_calculate_reroll_cost = v.config.center.key == "j_chaos" and calculate_reroll_cost or nil
local debuffed_by_crimson = right and not right.debuff and right.config.center.key == "j_Roland_crimson" local debuffed_by_crimson = right and not right.debuff and right.config.center.key == "j_Roland_crimson"
if orig_calculate_reroll_cost then
calculate_reroll_cost = f.noop
end
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 local debuff = not not v.debuff
v:set_debuff(true) v:set_debuff(true)
@ -752,6 +755,10 @@ joker {
v.ability.Roland_crimson = nil v.ability.Roland_crimson = nil
v:set_debuff(debuff) v:set_debuff(debuff)
end end
if orig_calculate_reroll_cost then
calculate_reroll_cost = orig_calculate_reroll_cost
end
end) end)
end, end,
} }
@ -1197,7 +1204,8 @@ end
local orig_get_enhancements = SMODS.get_enhancements local orig_get_enhancements = SMODS.get_enhancements
function SMODS.get_enhancements(card, ...) function SMODS.get_enhancements(card, ...)
if (G.GAME.modifiers.Roland_suitable or 0) <= 0 or if not card.base or
(G.GAME.modifiers.Roland_suitable or 0) <= 0 or
card.base.suit ~= G.GAME.current_round.Roland_suitable.suit or card.base.suit ~= G.GAME.current_round.Roland_suitable.suit or
(card.area ~= G.hand and card.area ~= G.play) then (card.area ~= G.hand and card.area ~= G.play) then
return orig_get_enhancements(card, ...) return orig_get_enhancements(card, ...)

View file

@ -3,7 +3,7 @@
---@license MPL-2.0 ---@license MPL-2.0
---@version 1.0.0 ---@version 1.0.0
--- ---
---@alias FFrom (fun<K, V>(iter: table<K, V>, fpairs?: fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K?, V?)): F | { [K]: V })|(fun(iter: number, fpairs?: number, step?: number): F | { [number]: number })|(fun<V>(iter: string): (fun(table: { [string]: V }): V))|(fun<V>(iter: string): (fun(table: { [string]: V }): V))|(fun<K, V>(iter: fun(): K, V): F | { [K]: V })|(fun(iter: false): fun(): false)|(fun(iter: true): fun(): true)|(fun(iter: nil): F) ---@alias FFrom (fun<K, V>(iter: { [K]: V }, fpairs?: fun(t: { [K]: V }): (fun(table: { [K]: V }, index?: K): K?, V?)): F | { [K]: V })|(fun(iter: number, fpairs?: number, step?: number): F | { [number]: number })|(fun<V>(iter: string): (fun(table: { [string]: V }): V))|(fun<V>(iter: string): (fun(table: { [string]: V }): V))|(fun<K, V>(iter: fun(): K, V): F | { [K]: V })|(fun(iter: false): fun(): false)|(fun(iter: true): fun(): true)|(fun(iter: nil): F)
---@class F ---@class F
local f = {} local f = {}
@ -254,10 +254,10 @@ local function autofunc(func)
end end
---@generic K, V ---@generic K, V
---@param tbl table<K, V> ---@param tbl { [K]: V }
---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V) ---@param fpairs? fun(t: { [K]: V }): (fun(table: { [K]: V }, index?: K): K, V)
---@return fun(tbl: table<K, V>, key: K): K, V ---@return fun(tbl: { [K]: V }, key: K): K, V
---@return table<K, V> ---@return { [K]: V }
---@return V ---@return V
---@nodiscard ---@nodiscard
local function autopairs(tbl, fpairs) local function autopairs(tbl, fpairs)
@ -393,19 +393,44 @@ function f.insert(v, k)
end end
end end
---@generic T local chairs = function(s, i)
---@param tbl T[] local k = i and i + 1 or 1
---@return fun(t: T[], i?: integer): integer, T local v = s:sub(k, k)
---@return T[]
function f.revpairs(tbl) if v == nil then
return function(t, i) return
end
local t = type(v)
if t ~= "string" and t ~= "table" or #v ~= 0 then
return k, v
end
end
---@generic T: string|{sub: fun(self: self, i: integer, j: integer)}
---@param str T
---@return fun(t: T, i?: integer): integer, T
---@return T
function f.chairs(str)
return chairs, str
end
local revpairs = function(t, i)
local k = i and i - 1 or #t local k = i and i - 1 or #t
local v = t[k] local v = t[k]
if v ~= nil then if v ~= nil then
return k, v return k, v
end end
end, tbl end
---@generic T
---@param tbl T[]
---@return fun(t: T[], i?: integer): integer, T
---@return T[]
function f.revpairs(tbl)
return revpairs, tbl
end end
---@param any any ---@param any any
@ -471,6 +496,7 @@ function f.new(fnext)
arg = f.arg, arg = f.arg,
call = f.call, call = f.call,
chain = f.chain, chain = f.chain,
chairs = f.chairs,
compose = f.compose, compose = f.compose,
concat = f.concat, concat = f.concat,
const = f.const, const = f.const,
@ -524,7 +550,16 @@ function f.from(iter, fpairs, step)
local t = type(iter) local t = type(iter)
if t == "string" then if t == "string" then
if not fpairs then
return f.indices(iter) return f.indices(iter)
end
local next, tbl, k, v = fpairs(iter --[[@as { sub: fun(self: self, i: integer, j?: integer) }]])
return f.new(function()
k, v = next(tbl, k)
return k, v
end)
elseif t == "number" then elseif t == "number" then
local ik, is, start = 0, step or 1, fpairs and iter or 1 local ik, is, start = 0, step or 1, fpairs and iter or 1
@ -647,9 +682,9 @@ 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 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: { [K]: V }): (fun(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 } ---@overload fun(self: F|{ [K]: V }, func: string, fpairs?: fun(t: { [K]: V }): (fun(table: { [K]: V }, index?: K): K, V)): F|{ [K]: U }
---@nodiscard ---@nodiscard
function f:flatmap(func, fpairs) function f:flatmap(func, fpairs)
self = f.isf(self) and self or f.from(self) self = f.isf(self) and self or f.from(self)