Add new joker

This commit is contained in:
Emik 2026-05-23 12:01:40 +02:00
parent 28e95e1045
commit d3fe7379bd
Signed by: emik
GPG key ID: 6B0CD72A5E503BDF
15 changed files with 182 additions and 47 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 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: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 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: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -2,7 +2,7 @@
directory=$(dirname $(readlink -f "$0")) directory=$(dirname $(readlink -f "$0"))
mkdir -p "$directory/1x/jokers" mkdir -p "$directory/1x/jokers"
for i in $(seq 0 25); do for i in $(seq 0 $(find *.png | tail -n +2 | wc -l)); do
files="$files $directory/1x/jokers/$i.png"; files="$files $directory/1x/jokers/$i.png";
done done

View file

@ -118,7 +118,7 @@ return {
}, },
}, },
Joker = { Joker = {
j_Roland_amberacorn = { j_Roland_amber = {
name = "Amber Acorn", name = "Amber Acorn",
text = {"{X:mult,C:white}X#1#{} Mult", "Moves before", "hand is played"}, text = {"{X:mult,C:white}X#1#{} Mult", "Moves before", "hand is played"},
}, },
@ -152,7 +152,7 @@ return {
"{C:inactive}(Currently {X:red,C:white}X#1#{C:inactive})", "{C:inactive}(Currently {X:red,C:white}X#1#{C:inactive})",
}, },
}, },
j_Roland_coldturkey = { j_Roland_cold = {
name = "Cold Turkey", name = "Cold Turkey",
text = { text = {
"Third scored card", "Third scored card",
@ -177,6 +177,23 @@ return {
"hand {C:dark_edition}Frozen", "hand {C:dark_edition}Frozen",
}, },
}, },
j_Roland_idle = {
name = "The Idle",
text = {
"Use to add card's",
"rank and suit",
"First {C:attention}scoring {}card",
"of each entry",
"gives {X:mult,C:white}X#19#{} Mult",
"{C:inactive}(Must be distinct)",
"{V:1,s:0.75}#1#{C:inactive,s:0.75}#2#{V:2,s:0.75}#3#{C:inactive,s:0.75}, " ..
"{V:3,s:0.75}#4#{C:inactive,s:0.75}#5#{V:4,s:0.75}#6#{C:inactive,s:0.75}",
"{V:5,s:0.75}#7#{C:inactive,s:0.75}#8#{V:6,s:0.75}#9#, " ..
"{V:7,s:0.75}#10#{C:inactive,s:0.75}#11#{V:8,s:0.75}#12#{C:inactive,s:0.75}",
"{V:9,s:0.75}#13#{C:inactive,s:0.75}#14#{V:10,s:0.75}#15#{C:inactive,s:0.75}, " ..
"{V:11,s:0.75}#16#{C:inactive,s:0.75}#17#{V:12,s:0.75}#18#",
},
},
j_Roland_jokersr = { j_Roland_jokersr = {
name = "Joker Sr.", name = "Joker Sr.",
text = {"{X:mult,C:white}X#1#{} Mult"}, text = {"{X:mult,C:white}X#1#{} Mult"},
@ -401,12 +418,15 @@ return {
c_Roland_Showdown = "Showdown", c_Roland_Showdown = "Showdown",
}, },
v_dictionary = { v_dictionary = {
b_Roland_add = "ADD",
b_Roland_bye = "Bye!", b_Roland_bye = "Bye!",
b_Roland_comma = ", ",
b_Roland_debuffed = "DEBUFFED",
b_Roland_disabled = "Disabled", b_Roland_disabled = "Disabled",
b_Roland_enabled = "Enabled", b_Roland_enabled = "Enabled",
b_Roland_comma = ", ",
b_Roland_entering_shop = "Entering shop!", b_Roland_entering_shop = "Entering shop!",
b_Roland_equinox_assist = "Assist: Only hide text (Equinox)", b_Roland_equinox_assist = "Assist: Only hide text (Equinox)",
b_Roland_full = "FULL",
b_Roland_scribable_basket = "Scribable Basket (overpowered)", b_Roland_scribable_basket = "Scribable Basket (overpowered)",
b_Roland_harsh_ante_scaling = "Harsh ante scaling (Ante 40+)", b_Roland_harsh_ante_scaling = "Harsh ante scaling (Ante 40+)",
b_Roland_illusion_seal = "Allow seals from Illusion voucher", b_Roland_illusion_seal = "Allow seals from Illusion voucher",
@ -416,6 +436,7 @@ return {
b_Roland_na = "N/A", b_Roland_na = "N/A",
b_Roland_of = " of ", b_Roland_of = " of ",
b_Roland_toggle = "TOGGLE", b_Roland_toggle = "TOGGLE",
b_Roland_unassigned = "(Unassigned)",
}, },
labels = { labels = {
Roland_frozen = "Frozen", Roland_frozen = "Frozen",

View file

@ -6,7 +6,7 @@
"author": [ "author": [
"Emik" "Emik"
], ],
"version": "2.5.2", "version": "2.5.3",
"badge_colour": "8BE9FD", "badge_colour": "8BE9FD",
"main_file": "src/main.lua", "main_file": "src/main.lua",
"badge_text_colour": "44475A", "badge_text_colour": "44475A",

View file

@ -12,7 +12,7 @@ local joker = (function()
return ret return ret
end end
---@param tbl SMODS.Joker|{artist?: string, sinis?: boolean|{x: number, y: number}, soul_pos?: boolean|{x: number, y: number}, attributes?: Attributes[]} ---@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), sinis?: boolean|{x: number, y: number}, soul_pos?: boolean|{x: number, y: number}, attributes?: Attributes[]}
return function(tbl) return function(tbl)
tbl.pos = inc() tbl.pos = inc()
tbl.atlas = "joker" tbl.atlas = "joker"
@ -38,6 +38,37 @@ local function is_frozen(card)
return card.edition and card.edition.key == "e_Roland_frozen" return card.edition and card.edition.key == "e_Roland_frozen"
end end
---@param card? Card|false|{suit: string, value: string}
---@param fallback? string
---@return {vars: {[number]: string|table|nil}|{colours: {[number]: {[1]: number, [2]: number, [3]: number, [4]: number}}}}
local function localize_card(card, fallback)
if not card then
return {
vars = {
"",
localize {type = "variable", key = fallback or "b_Roland_na"},
"",
colours = {G.C.JOKER_GREY, G.C.JOKER_GREY},
},
}
end
local suit = (card.base or card).suit
local value = (card.base or card).value
local name = (card.ability or card).name or ""
local no_suit = card.base and SMODS.has_no_suit(card) or not suit
local no_rank = card.base and SMODS.has_no_rank(card) or not value
return {
vars = {
(value and not no_rank) and localize(value or 14, "ranks") or name,
(not no_rank and not no_suit) and localize {type = "variable", key = "b_Roland_of"} or "",
no_suit and "" or localize(suit, "suits_plural"),
colours = {G.C.IMPORTANT, G.C.SUITS[suit] or G.C.JOKER_GREY},
},
}
end
SMODS.Atlas { SMODS.Atlas {
key = "joker", key = "joker",
path = "joker.png", path = "joker.png",
@ -135,33 +166,7 @@ joker {
blueprint_compat = true, blueprint_compat = true,
perishable_compat = true, perishable_compat = true,
loc_vars = function(_, _, card) loc_vars = function(_, _, card)
local last = ((G.deck or {}).cards or {})[1] return localize_card(card.area == G.jokers and ((G.deck or {}).cards or {})[1])
if not last or card.area ~= G.jokers then
return {
vars = {
"",
localize {type = "variable", key = "b_Roland_na"},
"",
colours = {G.C.JOKER_GREY, G.C.JOKER_GREY},
},
}
end
local suit = last.base.suit
local value = last.base.value
local name = last.ability.name or ""
local no_rank = SMODS.has_no_rank(last)
local no_suit = SMODS.has_no_suit(last)
return {
vars = {
value and not no_rank and localize(value or 14, "ranks") or name,
not no_rank and not no_suit and localize {type = "variable", key = "b_Roland_of"} or "",
no_suit and "" or localize(suit, "suits_plural"),
colours = {G.C.IMPORTANT, G.C.SUITS[suit] or G.C.JOKER_GREY},
},
}
end, end,
calculate = function(_, _, context) calculate = function(_, _, context)
if not context.selling_self and not context.forcetrigger then if not context.selling_self and not context.forcetrigger then
@ -350,7 +355,7 @@ joker {
} }
joker { joker {
key = "coldturkey", key = "cold",
pronouns = "he_him", pronouns = "he_him",
config = {extra = {xmult = 2}}, config = {extra = {xmult = 2}},
attributes = {"xmult"}, attributes = {"xmult"},
@ -613,12 +618,8 @@ joker {
blueprint_compat = true, blueprint_compat = true,
perishable_compat = true, perishable_compat = true,
loc_vars = function(_, _, card) loc_vars = function(_, _, card)
return { local key = card.ability.extra.flipped and "b_Roland_enabled" or "b_Roland_disabled"
vars = {localize { return {vars = {localize {type = "variable", key = key}}}
type = "variable",
key = card.ability.extra.flipped and "b_Roland_enabled" or "b_Roland_disabled",
}},
}
end, end,
inject = function(...) inject = function(...)
SMODS.Joker.inject(...) SMODS.Joker.inject(...)
@ -637,10 +638,7 @@ joker {
return not card.debuff return not card.debuff
end, end,
Bakery_use_button_text = function(_, card) Bakery_use_button_text = function(_, card)
return localize { return localize {type = "variable", key = card.debuff and "b_Roland_debuffed" or "b_Roland_toggle"}
type = "variable",
key = card.debuff and "b_Roland_debuffed" or "b_Roland_toggle",
}
end, end,
Bakery_use_joker = function(_, card) Bakery_use_joker = function(_, card)
local _ = card.debuff or Bakery_API.flip_double_sided(card) local _ = card.debuff or Bakery_API.flip_double_sided(card)
@ -648,7 +646,7 @@ joker {
} }
joker { joker {
key = "amberacorn", key = "amber",
pronouns = "he_they", pronouns = "he_they",
config = {extra = {xmult = 3}}, config = {extra = {xmult = 3}},
attributes = {"xmult"}, attributes = {"xmult"},
@ -670,7 +668,7 @@ joker {
end end
local keys = f(card.area.cards):where(f().nq(card)):keys():table() local keys = f(card.area.cards):where(f().nq(card)):keys():table()
local k = pseudorandom_element(keys, pseudoseed "Roland_stubborn") or card.rank local k = pseudorandom_element(keys, pseudoseed "Roland_amber") or card.rank
card.area.cards[k], card.area.cards[card.rank] = card.area.cards[k], card.area.cards[card.rank] =
card.area.cards[card.rank], card.area.cards[k] card.area.cards[card.rank], card.area.cards[k]
@ -869,3 +867,69 @@ joker {
end end
end, end,
} }
joker {
key = "idle",
pronouns = "they_them",
cost = 9,
rarity = 3,
Roland_idle_capacity = 6,
config = {extra = {cards = {}, xmult = 2}},
attributes = {"rank", "suit", "xmult"},
eternal_compat = true,
blueprint_compat = true,
perishable_compat = true,
loc_vars = function(self, _, card)
local extra = card.ability.extra
local col = {}
---@type { [number]: string|table|nil }|{colours: {[number]: {[1]: number, [2]: number, [3]: number, [4]: number}}}
local vars = f(self.Roland_idle_capacity):flatmap(function(v)
local l = localize_card(extra.cards[v], "b_Roland_unassigned").vars
f(l.colours):each(function(c)
col[#col + 1] = c
end)
l.colours = nil
return l
end):values():table()
table.insert(vars, extra.xmult)
vars.colours = col
return {vars = vars}
end,
calculate = function(_, card, context)
return context.forcetrigger or
context.individual and
context.cardarea == G.play and
f(card.ability.extra.cards or {}):map(function(v)
return f(context.scoring_hand):any(function(x)
return x:is_suit(v.suit) and x.base.value == v.value
end) or {}
end):any(f().eq(context.other_card)) and {xmult = card.ability.extra.xmult} or nil
end,
Bakery_can_use = function(self, card)
return not card.debuff and
next(G.hand.highlighted) and
self.Roland_idle_capacity - #card.ability.extra.cards - #G.hand.highlighted >= 0 and
f(G.hand.highlighted):all(function(v)
return not SMODS.has_no_rank(v) and
not SMODS.has_no_suit(v) and
f(card.ability.extra.cards or {}):all(function(o)
return v.base.suit ~= o.suit or v.base.value ~= o.value
end)
end)
end,
Bakery_use_button_text = function(self, card)
local key = card.debuff and "b_Roland_debuffed" or
(self.Roland_idle_capacity - #card.ability.extra.cards > 0 and "b_Roland_add" or "b_Roland_full")
return localize {type = "variable", key = key}
end,
Bakery_use_joker = function(_, card)
f(G.hand.highlighted):each(function(v)
table.insert(card.ability.extra.cards, {suit = v.base.suit, value = v.base.value})
end)
end,
}

View file

@ -22,7 +22,7 @@ local none
---@param tbl table<K, V> ---@param tbl table<K, V>
---@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 fun(tbl: table<K, V>, key: K): K, V ---@return fun(tbl: table<K, V>, key: K): K, V
---@return K ---@return table<K, V>
---@return V ---@return V
---@nodiscard ---@nodiscard
local function autopairs(tbl, fpairs) local function autopairs(tbl, fpairs)
@ -297,6 +297,56 @@ function f:flatmap(func, fpairs)
end) end)
end end
---@generic K, V, U
---@param self F|{ [K]: V }
---@param func F|fun(v: V, k: K): { [any]: U }
---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)
---@return F|{ [K]: U }
---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string, fpairs?: fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)): F|{ [K]: U }
function f:flatmap(func, fpairs)
-- local i = 0
local vt, vk, vv, vp
func = type(func) == "string" and f.index(func) or 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 ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func F|fun(v: V, k: K): boolean ---@param func F|fun(v: V, k: K): boolean