Use functional loops over imperative

This commit is contained in:
Emik 2026-01-30 12:37:38 +01:00
parent 341a12bf65
commit e62eec9de5
Signed by: emik
GPG key ID: 6B0CD72A5E503BDF
9 changed files with 83 additions and 93 deletions

View file

@ -1 +1 @@
../../BalatroBakery/src/
../../BalatroBakery/

View file

@ -1 +0,0 @@
../../Talisman/

View file

@ -1,4 +1,4 @@
local _, q = unpack(... or require "src.functional")
local f, q = unpack(... or require "src.functional")
SMODS.Atlas {
px = 34,
@ -16,26 +16,26 @@ local function common_rank()
--- Tallies up a card area's cards.
---@param card_area CardArea
local function tally_up(card_area)
for _, v in ipairs(card_area.cards) do
if not SMODS.has_no_rank(v) then
local id = v:get_id()
tally[id] = (tally[id] or 0) + 1
to_name[id] = v.base.value
end
end
f(card_area.cards):filter(function(v)
return not SMODS.has_no_rank(v)
end):foreach(function(v)
local id = v:get_id()
to_name[id] = v.base.value
tally[id] = (tally[id] or 0) + 1
end)
end
tally_up(G.deck)
tally_up(G.hand)
tally_up(G.discard)
local max_key, max_value = -1 / 0, -1 / 0
for k, v in pairs(tally) do
if v > max_value or k > max_key and v == max_value then
max_key = k
max_value = v
local max_key = f(tally):reduce({-1 / 0, -1 / 0}, function(a, v, k)
if v > a[1] or k > a[2] and v == a[1] then
return {v, k}
end
end
return a
end)[2]
return max_key, to_name[max_key]
end
@ -115,6 +115,7 @@ SMODS.Blind {
pronouns = "she_her",
mult = 2,
dollars = 5,
config = {draw = 5},
defeat = function(self)
self.disabled = false
end,
@ -127,13 +128,9 @@ SMODS.Blind {
return false
end
for i, v in ipairs(G.hand.cards) do
if i > 5 then
break
end
f(G.hand.cards, ipairs):take(self.config.draw):foreach(function(v)
G.hand:add_to_highlighted(v, true)
end
end)
G.FUNCS.play_cards_from_highlighted(nil)
end
@ -159,13 +156,15 @@ SMODS.Blind {
dollars = 5,
disable = function()
local function disable()
for i = 1, #G.discard.cards do
draw_card(G.discard, G.hand, i / #G.discard.cards * 100, "up", false, G.discard.cards[i], nil, nil, true)
end
local count = #G.discard.cards
for i = 1, #G.discard.cards do
draw_card(G.hand, G.deck, i / #G.discard.cards * 100, "up", false, G.discard.cards[i])
end
f(G.discard.cards):take(count):foreach(function(v, i)
draw_card(G.discard, G.hand, i / count * 100, "up", false, v, nil, nil, true)
end)
f(G.discard.cards):take(count):foreach(function(v, i)
draw_card(G.hand, G.deck, i / count * 100, "up", false, v)
end)
end
q(disable)
@ -225,11 +224,11 @@ SMODS.Blind {
local needs_text_change = false
local function process(card_area)
for _, v in pairs(card_area.cards) do
f(card_area.cards):foreach(function(v)
local debuff = v.debuff
v:set_debuff(self:recalc_debuff(v, false))
needs_text_change = needs_text_change or debuff ~= v.debuff
end
end)
end
process(G.deck)
@ -265,16 +264,16 @@ SMODS.Blind {
end
local cards_added = {}
local length = #G.hand.highlighted
local count = #G.hand.highlighted
for i = 1, length do
f(G.hand.highlighted, ipairs):take(count):foreach(function(v, i)
local copy = copy_card(G.hand.highlighted[i])
copy:add_to_deck()
table.insert(G.hand, copy)
table.insert(cards_added, copy)
table.insert(G.playing_cards, copy)
draw_card(G.hand, G.discard, i / length * 100, "down", false, copy, nil, nil, true)
end
draw_card(G.hand, G.discard, i / count * 100, "down", false, copy, nil, nil, true)
end)
playing_card_joker_effects(cards_added)
end,

View file

@ -23,7 +23,7 @@ local function protect_ev(fun)
return fun
end
local luaf = assert(SMODS.load_file("src/LuaFunctional/functional.lua"))()
local luaf = assert(SMODS.load_file("src/LuaFunctional/functional.lua"))() or require "src.LuaFunctional.functional"
if false then
---Returns the elements from the given `list`. This function is equivalent to
@ -54,17 +54,18 @@ if false then
function unpack(list, i, j)
error({list, i, j})
end
luaf = require "src.LuaFunctional.functional"
end
---@class Query<K, V>: metatable
local _ = luaf._proto
--- Creates a non-numerically-indexed query from the given table.
---@param obj table
---@param f_pairs? fun(t: table): unknown
---@param numeric boolean?
---@return Query
local function f(obj, f_pairs, numeric)
return f_pairs and luaf.from(obj, f_pairs, numeric ~= false) or luaf.pairs(obj)
return f_pairs and luaf.from(obj, f_pairs, numeric ~= false) or (obj[1] and luaf.ipairs(obj) or luaf.pairs(obj))
end
--- Queues an event to be run.
@ -75,7 +76,15 @@ local function q(fun, front)
G.E_MANAGER:add_event(protect_ev(fun), nil, front)
end
---@class Query<K, V>: metatable
local _ = luaf._proto
--- Determines if a center is allowed to be usable.
---@return boolean
local function u()
return not ((G.play and #G.play.cards > 0 or
G.CONTROLLER.locked or
(G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and
G.STATE ~= G.STATES.HAND_PLAYED and
G.STATE ~= G.STATES.DRAW_TO_HAND and
G.STATE ~= G.STATES.PLAY_TAROT)
end
return {f, q}
return {f, q, u}

View file

@ -1,13 +1,4 @@
local f, q = unpack(... or require "src.functional")
local function can_use()
return not ((G.play and #G.play.cards > 0 or
G.CONTROLLER.locked or
(G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and
G.STATE ~= G.STATES.HAND_PLAYED and
G.STATE ~= G.STATES.DRAW_TO_HAND and
G.STATE ~= G.STATES.PLAY_TAROT)
end
local f, q, u = unpack(... or require "src.functional")
local function common_hand()
return (G.GAME or {}).current_round and f(G.GAME.hands):reduce(
@ -103,7 +94,7 @@ SMODS.Joker {
end,
---@param card Card
Bakery_can_use = function(_, card)
return not card.debuff and can_use() and (
return not card.debuff and u() and (
#G.GAME.tags ~= 0 or
f(G.consumeables.cards):filter(destructible):any() or
f(G.jokers.cards):filter(is_mergeable_with(card)):any()

View file

@ -18,3 +18,7 @@ SMODS.Atlas {
key = "modicon",
path = "icon.png",
}
SMODS.current_mod.optional_features = function()
return {cardareas = {deck = true}}
end

View file

@ -1,3 +1,5 @@
local f, _ = unpack(... or require "src.functional")
-- SMODS.Atlas {
-- px = 71,
-- py = 95,
@ -10,23 +12,26 @@ SMODS.Seal {
badge_colour = HEX("12f254"),
atlas = "void",
pos = {x = 0, y = 0},
config = {ready = true},
calculate = function(_, card, context)
if not context.blind_defeated or card.area ~= G.deck then
if context.setting_blind or context.starting_shop then
card.Roland_frost = nil
end
if not context.end_of_round or card.area ~= G.deck or card.Roland_frost then
return
end
card.Roland_frost = true
local tag = Tag(get_next_tag_key("Roland_frost"))
if tag.name == "Orbital Tag" then
local hands = {}
for k, v in pairs(G.GAME.hands) do
if v.visible then
table.insert(hands, k)
end
end
tag.ability.orbital_hand = pseudorandom_element(hands, pseudoseed("Roland_frost"))
tag.ability.orbital_hand = pseudorandom_element(
f(G.GAME.hands):filter(function(v)
return v.visible
end):into(),
pseudoseed("Roland_frost")
)
end
add_tag(tag)

View file

@ -1,13 +1,4 @@
local f, q = unpack(... or require "src.functional")
local function can_use()
return not ((G.play and #G.play.cards > 0 or
G.CONTROLLER.locked or
(G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and
G.STATE ~= G.STATES.HAND_PLAYED and
G.STATE ~= G.STATES.DRAW_TO_HAND and
G.STATE ~= G.STATES.PLAY_TAROT)
end
local f, q, u = unpack(... or require "src.functional")
SMODS.Sound({key = "void", path = "void.ogg"})
@ -37,10 +28,10 @@ SMODS.Consumable {
return {vars = {card.ability.extra.amount, card.ability.extra.hand}}
end,
can_use = function(_, card)
return can_use() and card.ability.extra.amount == #Bakery_API.get_highlighted()
return u() and card.ability.extra.amount == #Bakery_API.get_highlighted()
end,
use = function(_, card, _, _)
for _, v in ipairs(Bakery_API.get_highlighted()) do
f(Bakery_API.get_highlighted()):foreach(function(v)
q {
delay = 0.1,
func = function()
@ -48,7 +39,7 @@ SMODS.Consumable {
v:juice_up(0.5, 0.5)
end,
}
end
end)
q {
delay = 0.1,
@ -75,7 +66,7 @@ SMODS.Consumable {
return {vars = {card.ability.extra.amount}}
end,
can_use = function(_, _)
return #G.playing_cards > 1 and can_use()
return #G.playing_cards > 1 and u()
end,
use = function(_, card, _, _)
local function destructible(v)
@ -102,17 +93,7 @@ SMODS.Consumable {
f(G.jokers.cards):foreach(calculate_joker)
for _ = 1, card.ability.extra.amount do
local cryptid = create_card(
nil,
G.consumeables,
nil,
nil,
nil,
nil,
"c_cryptid",
"void"
)
local cryptid = create_card(nil, G.consumeables, nil, nil, nil, nil, "c_cryptid", "void")
cryptid:set_edition({negative = true}, true)
cryptid:add_to_deck()
G.consumeables:emplace(cryptid)

View file

@ -1,3 +1,5 @@
local f = unpack(... or require "src.functional")
if not Balatest then
return
end
@ -139,13 +141,13 @@ Balatest.TestPlay {
Balatest.wait_for_input()
end,
assert = function()
for k, v in pairs(G.hand.cards) do
f(G.hand.cards):foreach(function(v, k)
local message = v.debuff and
"Card " .. k .. " should not be debuffed" or
"Card " .. k .. " should be debuffed"
Balatest.assert(v.debuff == (v.base.value == "Ace"), message)
end
end)
end,
}
@ -164,13 +166,13 @@ Balatest.TestPlay {
Balatest.play_hand {"2S"}
end,
assert = function()
for k, v in pairs(G.hand.cards) do
f(G.hand.cards):foreach(function(v, k)
local message = v.debuff and
"Card " .. k .. " should not be debuffed" or
"Card " .. k .. " should be debuffed"
Balatest.assert(v.debuff == (v.base.value == "2"), message)
end
end)
end,
}
@ -183,9 +185,9 @@ Balatest.TestPlay {
Balatest.wait_for_input()
end,
assert = function()
for k, v in pairs(G.hand.cards) do
f(G.hand.cards):foreach(function(v, k)
Balatest.assert(not v.debuff, "Card " .. k .. " should not be debuffed")
end
end)
end,
}