527 lines
13 KiB
Lua
527 lines
13 KiB
Lua
local f, q = unpack(... or require "lib.shared")
|
|
|
|
local blind = (function()
|
|
local y = 0
|
|
|
|
---@param tbl SMODS.Blind
|
|
---@return SMODS.Blind
|
|
return function(tbl)
|
|
tbl.pos = {x = 0, y = y}
|
|
tbl.atlas = "blind"
|
|
local ret = SMODS.Blind(tbl)
|
|
y = y + 1
|
|
return ret
|
|
end
|
|
end)()
|
|
|
|
SMODS.Atlas {
|
|
px = 34,
|
|
py = 34,
|
|
frames = 21,
|
|
key = "blind",
|
|
path = "blind.png",
|
|
atlas_table = "ANIMATION_ATLAS",
|
|
}
|
|
|
|
SMODS.Sound {
|
|
key = "kick",
|
|
path = "kick.ogg",
|
|
}
|
|
|
|
local function common_rank()
|
|
---@type { [integer]: integer }, { [integer]: string }
|
|
local tally, to_name = {}, {}
|
|
|
|
f(G.playing_cards):where(function(v)
|
|
return not SMODS.has_no_rank(v)
|
|
end):each(function(v)
|
|
local id = v:get_id()
|
|
to_name[id] = v.base.value
|
|
tally[id] = (tally[id] or 0) + 1
|
|
end)
|
|
|
|
local max_key = f(tally, pairs):fold({-1 / 0, -1 / 0}, function(a, v, k)
|
|
return (v > a[1] or v == a[1] and k > a[2]) and {v, k} or a
|
|
end)[2]
|
|
|
|
return max_key, to_name[max_key]
|
|
end
|
|
|
|
local function disable_improbable()
|
|
G.GAME.modifiers.Roland_improbable = nil
|
|
local orig = (getmetatable(G.GAME.probabilities) or {}).orig
|
|
|
|
if orig then
|
|
G.GAME.probabilities = orig
|
|
end
|
|
end
|
|
|
|
local function has_enhancement(card)
|
|
local e = SMODS.get_enhancements(card)
|
|
return not not (e and next(e))
|
|
end
|
|
|
|
local function set_freeze(state)
|
|
local function copy(x)
|
|
return type(x) == "table" and f(x):map(copy):table() or x
|
|
end
|
|
|
|
---@param card Card|{ Roland_blizzard: true|nil }
|
|
return function(card)
|
|
local last_edition = card.Roland_blizzard
|
|
card.Roland_blizzard = state and (copy(card.edition) or true) or nil
|
|
|
|
q {
|
|
delay = 0.1,
|
|
func = function()
|
|
local edition = state and {Roland_frozen = true} or last_edition or card.Roland_blizzard
|
|
card:set_edition(edition ~= true and edition or nil)
|
|
end,
|
|
}
|
|
end
|
|
end
|
|
|
|
local function sort_by_enhancement(v1, v2)
|
|
return has_enhancement(v1) and not has_enhancement(v2)
|
|
end
|
|
|
|
local function hsv_to_rgb(h, s, v)
|
|
s, v = s or 1, v or 1
|
|
|
|
if s <= 0 then
|
|
return v, v, v
|
|
end
|
|
|
|
local c = v * s
|
|
local r, g, b = 0, 0, 0
|
|
local x, m = (1 - math.abs((h * 6 % 2) - 1)) * c, v - c
|
|
|
|
if h < 1 / 6 then
|
|
r, g, b = c, x, 0
|
|
elseif h < 2 / 6 then
|
|
r, g, b = x, c, 0
|
|
elseif h < 3 / 6 then
|
|
r, g, b = 0, c, x
|
|
elseif h < 4 / 6 then
|
|
r, g, b = 0, x, c
|
|
elseif h < 5 / 6 then
|
|
r, g, b = x, 0, c
|
|
else
|
|
r, g, b = c, 0, x
|
|
end
|
|
|
|
return r + m, g + m, b + m
|
|
end
|
|
|
|
local function is_locked()
|
|
return G.STATE ~= G.STATES.SELECTING_HAND or G.CONTROLLER.locked or
|
|
(G.GAME.STOP_USE and G.GAME.STOP_USE > 0)
|
|
end
|
|
|
|
blind {
|
|
key = "nimble",
|
|
boss = {min = 1},
|
|
boss_colour = HEX "0291fbff",
|
|
pronouns = "she_her",
|
|
config = {draw = 5},
|
|
drawn_to_hand = function(self)
|
|
local function force_hand()
|
|
if is_locked() then
|
|
return false
|
|
end
|
|
|
|
f(G.hand.cards):take(self.config.draw):each(function(v)
|
|
G.hand:add_to_highlighted(v, true)
|
|
end)
|
|
|
|
G.FUNCS.play_cards_from_highlighted()
|
|
end
|
|
|
|
local g = G.GAME
|
|
|
|
if not g.blind.disabled and not g.Roland_nimble_disabled then
|
|
g.Roland_nimble_disabled = true
|
|
q {func = force_hand, blocking = false}
|
|
end
|
|
end,
|
|
set_blind = function()
|
|
G.GAME.Roland_nimble_disabled = nil
|
|
end,
|
|
}
|
|
|
|
blind {
|
|
key = "falseshuffle",
|
|
boss = {min = 3},
|
|
boss_colour = HEX "ff7f3dff",
|
|
pronouns = "any_all",
|
|
disable = function()
|
|
G.FUNCS.draw_from_hand_to_deck()
|
|
|
|
q(function()
|
|
pseudoshuffle(G.deck.cards, pseudoseed "RolandFalseShuffle")
|
|
end)
|
|
end,
|
|
calculate = function(_, b, context)
|
|
local _ = not b.disabled and context.drawing_cards and table.sort(G.deck.cards, sort_by_enhancement)
|
|
end,
|
|
in_pool = function()
|
|
return G.playing_cards and f(G.playing_cards):any(has_enhancement)
|
|
end,
|
|
}
|
|
|
|
blind {
|
|
key = "divide",
|
|
boss = {min = 1},
|
|
boss_colour = HEX "b18480ff",
|
|
pronouns = "he_they",
|
|
disable = function()
|
|
-- Ensures that this runs after 'set_blind' since it also gets added to queue.
|
|
q {
|
|
delay = 0.8,
|
|
trigger = "after",
|
|
func = function()
|
|
G.FUNCS.draw_from_discard_to_deck()
|
|
end,
|
|
}
|
|
end,
|
|
set_blind = function()
|
|
-- Allows the background to ease in first before drawing cards.
|
|
q(function()
|
|
local count = (#G.deck.cards - 2) / 2
|
|
|
|
for i = 0, count do
|
|
local card = G.deck.cards[#G.deck.cards - i]
|
|
draw_card(G.deck, G.hand, i / count * 100, "down", false, card, nil, nil, true)
|
|
end
|
|
|
|
for i = 0, count do
|
|
local card = G.deck.cards[#G.deck.cards - i]
|
|
draw_card(G.hand, G.discard, i / count * 100, "down", false, card, nil, nil, true)
|
|
end
|
|
end)
|
|
end,
|
|
}
|
|
|
|
blind {
|
|
key = "mitotic",
|
|
boss = {min = 3},
|
|
boss_colour = HEX "80b48eff",
|
|
pronouns = "it_its",
|
|
calculate = function(_, b, context)
|
|
if b.disabled or not context.pre_discard then
|
|
return
|
|
end
|
|
|
|
local cards_added = {}
|
|
local count = #G.hand.highlighted
|
|
|
|
f(G.hand.highlighted):take(count):each(function(v, i)
|
|
local copy = copy_card(v)
|
|
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 / count * 100, "down", false, copy, nil, nil, true)
|
|
end)
|
|
|
|
b:wiggle()
|
|
b.triggered = true
|
|
playing_card_joker_effects(cards_added)
|
|
end,
|
|
}
|
|
|
|
blind {
|
|
key = "blizzard",
|
|
boss = {min = 3},
|
|
boss_colour = HEX "102a41ff",
|
|
pronouns = "it_its",
|
|
defeat = function(self)
|
|
self.cards():each(set_freeze())
|
|
G.GAME.blind.disabled = true
|
|
end,
|
|
disable = function(self)
|
|
self:defeat()
|
|
end,
|
|
calculate = function(self, b)
|
|
return not b.disabled and self.cards():where(function(v)
|
|
return not v.Roland_blizzard and v.facing == "front"
|
|
end):each(set_freeze(true)) or nil
|
|
end,
|
|
cards = function()
|
|
return f(G):where(function(v)
|
|
return type(v) == "table" and type(v.cards) == "table"
|
|
end):flatmap("cards", ipairs)
|
|
end,
|
|
}
|
|
|
|
blind {
|
|
key = "tranquilizer",
|
|
boss = {min = 6},
|
|
boss_colour = HEX "bdaeccff",
|
|
pronouns = "they_them",
|
|
collection_loc_vars = function(_)
|
|
return {
|
|
vars = {localize {
|
|
type = "variable",
|
|
key = "b_Roland_most_common_card",
|
|
}},
|
|
}
|
|
end,
|
|
loc_vars = function(_)
|
|
local _, name = common_rank()
|
|
return {vars = {localize(name or "Ace", "ranks")}}
|
|
end,
|
|
calculate = function(self, b, context)
|
|
if not context.card_added and
|
|
not context.drawing_cards and
|
|
not context.playing_card_added and
|
|
not context.pre_discard and
|
|
not context.press_play then
|
|
return
|
|
end
|
|
|
|
local needs_text_change
|
|
|
|
---comment
|
|
---@param card_area CardArea|{cards: Card[]}
|
|
local function process(card_area)
|
|
f(card_area.cards):each(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
|
|
|
|
process(G.deck)
|
|
process(G.hand)
|
|
process(G.discard)
|
|
|
|
if needs_text_change then
|
|
b:wiggle()
|
|
b:set_text()
|
|
end
|
|
end,
|
|
recalc_debuff = function(self, card)
|
|
local id, _ = common_rank()
|
|
local ret = not self.disabled and id == card:get_id()
|
|
self.triggered = ret
|
|
return ret
|
|
end,
|
|
}
|
|
|
|
blind {
|
|
key = "improbable",
|
|
boss = {min = 3},
|
|
boss_colour = HEX "009966ff",
|
|
pronouns = "it_its",
|
|
mult = 2,
|
|
dollars = 5,
|
|
defeat = disable_improbable,
|
|
disable = disable_improbable,
|
|
set_blind = function(_)
|
|
G.GAME.modifiers.Roland_improbable = true
|
|
end,
|
|
}
|
|
|
|
if cry_prob then
|
|
local orig_cry_prob = cry_prob
|
|
|
|
function cry_prob(...)
|
|
return G.GAME.modifiers.Roland_improbable and 0 or orig_cry_prob(...)
|
|
end
|
|
end
|
|
|
|
function SMODS.current_mod:calculate(context)
|
|
if context.setting_blind and G.GAME.blind.name == "bl_mp_nemesis" then
|
|
local modifiers = G.GAME.modifiers
|
|
modifiers.Roland_martingale_seed = (modifiers.Roland_martingale_seed or 0) + 1
|
|
end
|
|
|
|
local _ = type(G.calc) == "function" and G.calc(f(context):keys():string())
|
|
local improbable, orig = G.GAME.modifiers.Roland_improbable, G.GAME.probabilities
|
|
|
|
-- Normally unreachable since we set it to nil ourselves,
|
|
-- but other mods may want to use this modifier.
|
|
if improbable == false then
|
|
disable_improbable()
|
|
return
|
|
end
|
|
|
|
if not improbable or getmetatable(orig) then
|
|
return
|
|
end
|
|
|
|
local normal = orig.normal
|
|
|
|
local mt = {
|
|
orig = orig,
|
|
__index = function(_, k)
|
|
return k == "normal" and 0 or orig[k]
|
|
end,
|
|
__newindex = function(_, k, v)
|
|
orig[k] = (k == "normal" and v == 0) and normal or v
|
|
end,
|
|
}
|
|
|
|
local proxy = {}
|
|
setmetatable(proxy, mt)
|
|
G.GAME.probabilities = proxy
|
|
end
|
|
|
|
blind {
|
|
key = "equinox",
|
|
boss = {min = 6},
|
|
boss_colour = HEX "000000ff",
|
|
pronouns = "any_all",
|
|
defeat = function()
|
|
G.GAME.modifiers.Roland_equinox = nil
|
|
end,
|
|
disable = function()
|
|
G.GAME.modifiers.Roland_equinox = nil
|
|
end,
|
|
set_blind = function()
|
|
play_sound("Roland_kick", 1, 0.7)
|
|
G.GAME.modifiers.Roland_equinox = true
|
|
end,
|
|
}
|
|
|
|
local function equinox()
|
|
return G.GAME and
|
|
G.GAME.modifiers and
|
|
G.GAME.modifiers.Roland_equinox and
|
|
G.STATE ~= G.STATES.GAME_OVER
|
|
end
|
|
|
|
local orig_draw = Card.draw
|
|
|
|
function Card:draw(...)
|
|
if equinox() and
|
|
not SMODS.Mods.Roland.config.equinox_assist and
|
|
not self.states.hover.is and
|
|
not self.states.focus.is then
|
|
add_to_drawhash(self)
|
|
else
|
|
return orig_draw(self, ...)
|
|
end
|
|
end
|
|
|
|
local orig_draw_self = UIElement.draw_self
|
|
|
|
function UIElement:draw_self(...)
|
|
if equinox() and
|
|
not self.config.button and
|
|
not self.config.button_UIE then
|
|
add_to_drawhash(self)
|
|
else
|
|
return orig_draw_self(self, ...)
|
|
end
|
|
end
|
|
|
|
local venerable_visage = blind {
|
|
key = "venerable_visage",
|
|
boss = {showdown = true},
|
|
boss_colour = HEX "f6f6f2ff",
|
|
pronouns = "any_all",
|
|
dollars = 8,
|
|
calculate = function(self, b, context)
|
|
if b.disabled then
|
|
return
|
|
end
|
|
|
|
if context.pre_discard and next(G.hand.highlighted) then
|
|
q(function()
|
|
b:wiggle()
|
|
ease_discard(1)
|
|
end)
|
|
end
|
|
|
|
if b.Roland_vitriol or
|
|
not context.end_of_round or
|
|
G.GAME.chips == G.GAME.blind.chips or
|
|
not (next(G.deck.cards) or next(G.hand.cards)) then
|
|
return
|
|
end
|
|
|
|
b.Roland_vitriol = true
|
|
self.vitriol(b)
|
|
|
|
q {
|
|
trigger = "before",
|
|
func = function()
|
|
G.STATE = G.STATES.GAME_OVER
|
|
G.STATE_COMPLETE = false
|
|
end,
|
|
}
|
|
end,
|
|
vitriol = function(b)
|
|
if type(b) == "table" and type(b.wiggle) == "function" then
|
|
b:wiggle()
|
|
end
|
|
|
|
local fail = G.localization.descriptions.Blind.bl_Roland_venerable_visage.fail
|
|
local speed = 0.1
|
|
|
|
SMODS.draw_cards(#G.deck.cards)
|
|
play_sound("gong", 0.6)
|
|
|
|
attention_text {
|
|
text = pseudorandom_element(fail, pseudoseed "RolandVenerableVisage"),
|
|
offset = {x = 0, y = -3.6},
|
|
major = G.play,
|
|
scale = 3,
|
|
hold = 2,
|
|
}
|
|
|
|
delay(1)
|
|
|
|
f(G.playing_cards):each(function(v)
|
|
q {
|
|
trigger = "before",
|
|
delay = speed,
|
|
func = function()
|
|
v:start_dissolve()
|
|
v:shatter()
|
|
end,
|
|
}
|
|
end)
|
|
|
|
delay(1)
|
|
|
|
f(G.P_CARDS):each(function(v)
|
|
q {
|
|
delay = speed,
|
|
func = function()
|
|
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
|
|
|
|
local card = Card(
|
|
b and b.T.x or 0,
|
|
b and b.T.y or 0,
|
|
G.CARD_W,
|
|
G.CARD_H,
|
|
v,
|
|
G.P_CENTERS.m_Bakery_Curse or G.P_CENTERS.c_base,
|
|
{playing_card = G.playing_card}
|
|
)
|
|
|
|
table.insert(G.playing_cards, card)
|
|
G.deck:emplace(card)
|
|
play_sound "card1"
|
|
card:add_to_deck()
|
|
end,
|
|
}
|
|
end)
|
|
|
|
delay(1)
|
|
end,
|
|
}
|
|
|
|
local orig_game_draw = Game.draw
|
|
|
|
function Game.draw(...)
|
|
orig_game_draw(...)
|
|
local boss_colour = venerable_visage.boss_colour
|
|
|
|
if type(boss_colour) == "table" then
|
|
boss_colour[1], boss_colour[2], boss_colour[3] = hsv_to_rgb(os.clock() / 6 % 1, 0.25, 0.75)
|
|
end
|
|
end
|