balatro-mods/Incantation/Incantation.lua
2025-01-19 15:01:49 +08:00

1067 lines
32 KiB
Lua

--- STEAMODDED HEADER
--- MOD_NAME: Incantation
--- MOD_ID: incantation
--- MOD_AUTHOR: [jenwalter666, MathIsFun_]
--- MOD_DESCRIPTION: Enables the ability to stack identical consumables.
--- PRIORITY: 89999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
--- BADGE_COLOR: 000000
--- PREFIX: inc
--- VERSION: 0.5.10
--- LOADER_VERSION_GEQ: 1.0.0
Incantation = {consumable_in_use = false, accelerate = false} --will port more things over to this global later, but for now it's going to be mostly empty
local CFG = SMODS.current_mod.config
local MaxStack = 9999
local BulkUseLimit = 9999
local NaiveBulkUseCancel = 100
local AccelerateThreshold = 3
local HardLimit = 9007199254740992
SMODS.current_mod.config_tab = function()
return {n = G.UIT.ROOT, config = {r = 0.1, align = "cm", padding = 0.1, colour = G.C.BLACK, minw = 8, minh = 4}, nodes = {
{n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = {
{n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = {
create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "NegativesOnly" },
}},
{n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = {
{ n = G.UIT.T, config = { text = localize('incant_negatives_only'), scale = 0.35, colour = G.C.UI.TEXT_LIGHT }},
}},
}},
{n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = {
{n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = {
create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "StackAnything" },
}},
{n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = {
{ n = G.UIT.T, config = { text = localize('incant_stack_anything'), scale = 0.35, colour = G.C.RED }},
}},
}},
{n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = {
{n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = {
create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "UnsafeMode" },
}},
{n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = {
{ n = G.UIT.T, config = { text = localize('incant_unsafe_mode'), scale = 0.35, colour = G.C.RED }},
}},
}}
}}
end
local function tablecontains(haystack, needle)
for k, v in pairs(haystack) do
if v == needle then
return true
end
end
return false
end
local Stackable = {
'Planet',
'Tarot',
'Spectral'
}
local StackableIndividual = {
'c_black_hole',
'c_cry_white_hole'
}
local Divisible = {
'Planet',
'Tarot',
'Spectral'
}
local DivisibleIndividual = {
'c_black_hole',
'c_cry_white_hole'
}
local BulkUsable = {
'Planet'
}
local BulkUsableIndividual = {
'c_black_hole',
'c_cry_white_hole'
}
--Allow mods to add/remove their own card types to the list
function AllowStacking(set)
if not tablecontains(Stackable, set) then
table.insert(Stackable, set)
end
end
function AllowStackingIndividual(key)
if not tablecontains(StackableIndividual, key) then
table.insert(StackableIndividual, key)
end
end
function AllowDividing(set)
AllowStacking(set)
if not tablecontains(Divisible, set) then
table.insert(Divisible, set)
end
end
function AllowDividingIndividual(key)
AllowStackingIndividual(key)
if not tablecontains(DivisibleIndividual, key) then
table.insert(DivisibleIndividual, key)
end
end
function AllowBulkUse(set)
AllowStacking(set)
AllowDividing(set)
if not tablecontains(BulkUsable, set) then
table.insert(BulkUsable, set)
end
end
function AllowBulkUseIndividual(key)
AllowStackingIndividual(key)
AllowDividingIndividual(key)
if not tablecontains(BulkUsableIndividual, key) then
table.insert(BulkUsableIndividual, key)
end
end
--[[
+++ MOD SUPPORT SKELETON : ALWAYS INCLUDE THIS IN YOUR MOD'S INITIALISATION SOMEWHERE +++
if not IncantationAddons then
IncantationAddons = {
Stacking = {},
Dividing = {},
BulkUse = {},
StackingIndividual = {},
DividingIndividual = {},
BulkUseIndividual = {}
}
end
then you can use table.insert() to insert sets/keys into the subtables contained within IncantationAddons
]]
if IncantationAddons then
if #IncantationAddons.Stacking > 0 then
for _, v in pairs(IncantationAddons.Stacking) do
AllowStacking(v)
end
end
if #IncantationAddons.StackingIndividual > 0 then
for _, v in pairs(IncantationAddons.StackingIndividual) do
AllowStackingIndividual(v)
end
end
if #IncantationAddons.Dividing > 0 then
for _, v in pairs(IncantationAddons.Dividing) do
AllowDividing(v)
end
end
if #IncantationAddons.DividingIndividual > 0 then
for _, v in pairs(IncantationAddons.DividingIndividual) do
AllowDividingIndividual(v)
end
end
if #IncantationAddons.BulkUse > 0 then
for _, v in pairs(IncantationAddons.BulkUse) do
AllowBulkUse(v)
end
end
if #IncantationAddons.BulkUseIndividual > 0 then
for _, v in pairs(IncantationAddons.BulkUseIndividual) do
AllowBulkUseIndividual(v)
end
end
end
function Card:getQty()
return (self.ability or {}).qty or 1
end
function Card:setQty(quantity, dontupdatecost)
if not quantity then quantity = 1 end
if self:CanStack() then
if self.ability then
self.ability.qty = math.min(HardLimit, math.floor(quantity))
self:create_stack_display()
if not dontupdatecost then self:set_cost() end
end
end
end
function Card:addQty(quantity, dontupdatecost)
self:setQty(self:getQty() + math.floor(quantity), dontupdatecost)
end
function Card:subQty(quantity, dont_dissolve, dontupdatecost)
if quantity >= self:getQty() and not dont_dissolve then
self:setQty(0)
self.ignorestacking = true
self:start_dissolve()
else
self:setQty(math.max(0, self:getQty() - math.ceil(quantity)), dontupdatecost)
end
end
function Card:CanStack()
if self.area and G.consumeables and self.area ~= G.consumeables then
return false
elseif self.ability and (self.ability.set == 'Booster' or self.ability.set == 'Voucher') then
return false
elseif CFG.NegativesOnly and not (self.edition and self.edition.negative) then
return false
elseif CFG.StackAnything then
return true
end
return (self.config.center and (type(self.config.center.can_stack) == 'function' and self.config.center:can_stack() or self.config.center.can_stack)) or tablecontains(Stackable, self.ability.set) or tablecontains(StackableIndividual, self.config.center_key)
end
function Card:CanDivide()
if CFG.StackAnything then
return true
end
return (self.config.center and (type(self.config.center.can_divide) == 'function' and self.config.center:can_divide() or self.config.center.can_divide)) or tablecontains(Divisible, self.ability.set) or tablecontains(DivisibleIndividual, self.config.center_key)
end
function Card:CanBulkUse(ignoreunsafe)
return (not ignoreunsafe and CFG.UnsafeMode) or not self.config.center.no_bulkuse and ((self.config.center and (type(self.config.center.can_bulk_use) == 'function' and self.config.center:can_bulk_use() or (self.config.center.can_bulk_use or (self.config.center.bulk_use and (type(self.config.center.bulk_use) == 'function'))))) or tablecontains(BulkUsable, self.ability.set) or tablecontains(BulkUsableIndividual, self.config.center_key))
end
function Card:getmaxuse()
--let modders define their own bulk-use limit in case of concerns with performance
return (self.config.center.bulk_use_limit or UseBulkCap) and math.min((self.config.center.bulk_use_limit or BulkUseLimit), self:getQty()) or (self:getQty())
end
function set_consumeable_usage(card, qty)
qty = math.floor(qty or 1)
if card.config.center_key and card.ability.consumeable then
if G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] then
G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count = G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count + qty
else
G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order}
end
if G.GAME.consumeable_usage[card.config.center_key] then
G.GAME.consumeable_usage[card.config.center_key].count = G.GAME.consumeable_usage[card.config.center_key].count + qty
else
G.GAME.consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order, set = card.ability.set}
end
G.GAME.consumeable_usage_total = G.GAME.consumeable_usage_total or {tarot = 0, planet = 0, spectral = 0, tarot_planet = 0, all = 0}
if card.config.center.set == 'Tarot' then
G.GAME.consumeable_usage_total.tarot = G.GAME.consumeable_usage_total.tarot + qty
G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + qty
elseif card.config.center.set == 'Planet' then
G.GAME.consumeable_usage_total.planet = G.GAME.consumeable_usage_total.planet + qty
G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + qty
elseif card.config.center.set == 'Spectral' then G.GAME.consumeable_usage_total.spectral = G.GAME.consumeable_usage_total.spectral + qty
end
G.GAME.consumeable_usage_total.all = G.GAME.consumeable_usage_total.all + qty
if not card.config.center.discovered then
discover_card(card)
end
if card.config.center.set == 'Tarot' or card.config.center.set == 'Planet' then
G.E_MANAGER:add_event(Event({
trigger = 'immediate',
func = function()
G.E_MANAGER:add_event(Event({
trigger = 'immediate',
func = function()
G.GAME.last_tarot_planet = card.config.center_key
return true
end
}))
return true
end
}))
end
end
G:save_settings()
end
function Card:split(amount, forced, fullmax)
if not amount then amount = math.floor((self:getQty()) / 2) end
amount = math.max(1, amount)
if (self.ability.qty or 0) > (fullmax and 0 or 1) and (self:CanDivide() or forced) and not self.ignorestacking then
local traysize = G.consumeables.config.card_limit
if (self.edition or {}).negative then
traysize = traysize + 1
end
local split = copy_card(self)
local qty2 = math.min(self.ability.qty - (fullmax and 0 or 1), amount)
G.consumeables.config.card_limit = #G.consumeables.cards + 1
split.ignorestacking = true
split.created_from_split = true
split:add_to_deck()
G.consumeables:emplace(split, nil, nil, true)
split.ability.qty = qty2
self.ability.qty = self.ability.qty - qty2
G.consumeables.config.card_limit = traysize
if qty2 > 1 then
split:create_stack_display()
end
split:set_cost()
self:set_cost()
play_sound('card1')
return split
end
end
function Card:try_merge()
if self:CanStack() and not self.ignorestacking then
for _, v in pairs(G.consumeables.cards) do
if CFG.NegativesOnly and not (v.edition or {}).negative then
-- Ignore this
elseif v ~= self and not v.nomerging and not v.ignorestacking and v.config.center_key == self.config.center_key and (((v.edition or {}).type or '') == ((self.edition or {}).type or '')) and (v:getQty() < (UseStackCap and MaxStack or HardLimit)) then
local space = (UseStackCap and MaxStack or HardLimit) - (v:getQty())
v.ability.qty = (v:getQty()) + math.min((self:getQty()), space)
v:create_stack_display()
v:juice_up(0.5, 0.5)
play_sound('card1')
v:set_cost()
if (self:getQty()) - space < 1 then
self.ignorestacking = true
self.area:remove_card(self)
self:remove()
return true
else
self.ability.qty = (self:getQty()) - space
self:set_cost()
end
end
end
end
end
function Card:getEvalQty()
return self.cardinuse and math.min(self:getQty(), (self.bulkuse and not self.naivebulkuse) and self:getQty() or (self.OverrideBulkUseLimit or NaiveBulkUseCancel)) or self:getQty()
end
local useconsumeref = Card.use_consumeable
function Card:use_consumeable(area, copier)
local obj = self.config.center
local uselim = self.OverrideBulkUseLimit or NaiveBulkUseCancel
local qty = self:getQty()
if qty <= 0 then
self:setQty(1, true)
qty = 1
end
if self.ability then
self.ability.qty_initial = qty
end
if not self.naivebulkuse and self.bulkuse and obj.bulk_use and type(obj.bulk_use) == 'function' then
set_consumeable_usage(self, qty)
return obj:bulk_use(self, area, copier, qty)
elseif not self.naivebulkuse and self.ability.consumeable.hand_type then
update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize(self.ability.consumeable.hand_type, 'poker_hands'),chips = G.GAME.hands[self.ability.consumeable.hand_type].chips, mult = G.GAME.hands[self.ability.consumeable.hand_type].mult, level=G.GAME.hands[self.ability.consumeable.hand_type].level})
level_up_hand(copier or self, self.ability.consumeable.hand_type, nil, qty)
update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''})
set_consumeable_usage(self, qty)
elseif not self.naivebulkuse and self.ability.name == 'Black Hole' then
update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize('k_all_hands'),chips = '...', mult = '...', level=''})
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function()
play_sound('tarot1')
self:juice_up(0.8, 0.5)
G.TAROT_INTERRUPT_PULSE = true
return true end }))
update_hand_text({delay = 0}, {mult = '+', StatusText = true})
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function()
play_sound('tarot1')
self:juice_up(0.8, 0.5)
return true end }))
update_hand_text({delay = 0}, {chips = '+', StatusText = true})
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function()
play_sound('tarot1')
self:juice_up(0.8, 0.5)
G.TAROT_INTERRUPT_PULSE = nil
return true end }))
update_hand_text({sound = 'button', volume = 0.7, pitch = 0.9, delay = 0}, {level='+' .. qty})
delay(1.3)
for k, v in pairs(G.GAME.hands) do
level_up_hand(self, k, true, qty)
end
update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''})
set_consumeable_usage(self, qty)
else
Incantation.accelerate = qty > AccelerateThreshold or self.force_incantation_acceleration
self.cardinuse = true
Incantation.consumable_in_use = true
local lim = math.min(qty, uselim)
local newqty = math.max(0, qty - lim)
if not self.ability.qty then self.ability.qty = 1 end
for i = 1, lim do
useconsumeref(self,area,copier)
G.E_MANAGER:add_event(Event({
trigger = 'immediate',
delay = 0.1,
blockable = true,
func = function()
self.ability.qty = self.ability.qty - 1
play_sound('button', self.ability.qty <= 0 and 1 or 0.85, 0.7)
return true
end
}))
end
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.1,
blockable = true,
func = function()
Incantation.consumable_in_use = false
Incantation.accelerate = false
self.cardinuse = nil
self.bulkuse = nil
self.naivebulkuse = nil
self.OverrideBulkUseLimit = nil
if obj.keep_on_use and obj:keep_on_use(self) then
self.ignorestacking = false
self.ability.qty = obj.keep_on_use_retain_stack and qty or (newqty + 1)
else
if newqty > 0 then
self:split(newqty, true, true)
end
end
return true
end
}))
end
end
local startdissolveref = Card.start_dissolve
function Card:start_dissolve(a,b,c,d)
if self.ability.qty and self.ability.qty > 1 and Incantation.consumable_in_use and self.cardinuse and not self.ignore_incantation_consumable_in_use then return end
self.ignorestacking = true
return startdissolveref(self,a,b,c,d)
end
local usecardref = G.FUNCS.use_card
function CanUseStackButtons()
if ((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 then
return false
end
return true
end
G.FUNCS.use_card = function(e, mute, nosave)
local card = e.config.ref_table
local useamount = card.bulkuse and card:getmaxuse() or 1
if ((card.ability or {}).qty or 1) > useamount then
card.highlighted = false
card.bulkuse = false
card.OverrideBulkUseLimit = useamount
end
usecardref(e, mute, nosave)
end
G.FUNCS.can_split_card = function(e)
local card = e.config.ref_table
local splitone = e.config.issplitone
if (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then
e.config.colour = splitone and G.C.GREEN or G.C.PURPLE
e.config.button = splitone and 'split_one' or 'split_half'
e.states.visible = true
else
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
e.states.visible = false
end
end
function Card:MergeAvailable()
if ((self.config or {}).center or {}).set ~= 'Joker' and ((self.config or {}).center or {}).set ~= 'Booster' then
for k, v in pairs(G.consumeables.cards) do
if v then
if v ~= self and (v.config or {}).center_key == (self.config or {}).center_key and ((v.edition or {}).type or '') == ((self.edition or {}).type or '') then
return true
end
end
end
end
end
G.FUNCS.can_merge_card = function(e)
local card = e.config.ref_table
if card:CanStack() and card.highlighted and not card.ignorestacking and CanUseStackButtons() and card:MergeAvailable() then
e.config.colour = G.C.BLUE
e.config.button = 'merge_card'
e.states.visible = true
else
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
e.states.visible = false
end
end
G.FUNCS.can_use_all = function(e)
local card = e.config.ref_table
local obj = card.config.center
if card:CanBulkUse() and ((tablecontains(BulkUsable, card.ability.set) or tablecontains(BulkUsableIndividual, card.config.center_key)) or (obj.bulk_use and type(obj.bulk_use) == 'function')) and (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then
e.config.colour = G.C.DARK_EDITION
e.config.button = 'use_all'
e.states.visible = true
else
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
e.states.visible = false
end
end
G.FUNCS.can_use_every_planet = function(e)
local card = e.config.ref_table
local obj = card.config.center
if (((card.config or {}).center or {}).set or '') == 'Planet' and card:CanBulkUse() and CanUseStackButtons() and not card.ignorestacking and not obj.ignore_allplanets then
e.config.colour = G.C.SECONDARY_SET.Planet
e.config.button = 'use_every_planet'
e.states.visible = true
else
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
e.states.visible = false
end
end
G.FUNCS.can_use_naivebulk = function(e)
local card = e.config.ref_table
local obj = card.config.center
if (card:CanBulkUse() or CFG.UnsafeMode) and (CFG.UnsafeMode or not obj.bulk_use or type(obj.bulk_use) ~= 'function') and (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then
e.config.colour = G.C.BLACK
e.config.button = 'use_naivebulk'
e.states.visible = true
else
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
e.states.visible = false
end
end
G.FUNCS.split_half = function(e)
local card = e.config.ref_table
card:split(math.floor(card.ability.qty / 2))
end
G.FUNCS.split_one = function(e)
local card = e.config.ref_table
card:split(1)
end
G.FUNCS.merge_card = function(e)
local card = e.config.ref_table
card:try_merge()
end
G.FUNCS.use_all = function(e)
local card = e.config.ref_table
local obj = card.config.center
if card:CanBulkUse() and (not obj.can_use or obj:can_use(card)) and (card:getQty()) > 1 and card.highlighted then
card.bulkuse = true
G.FUNCS.use_card(e, false, true)
end
end
function runthrough_planets()
for i = 1, #G.play.cards do
local card = G.play.cards[i]
if card then
local obj = card.config.center
if (((card.config or {}).center or {}).set or '') == 'Planet' then
card.bulkuse = card:CanBulkUse() and math.max(1, card:getQty()) > 1
card.force_incantation_acceleration = true
card:use_consumeable(G.consumeables)
if G.betmma_abilities then
for i = 1, #G.betmma_abilities.cards do
G.betmma_abilities.cards[i]:calculate_joker({using_consumeable = true, consumeable = card})
end
end
for i = 1, #G.jokers.cards do
local effects = G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card})
if effects and effects.joker_repetitions then
rep_list = effects.joker_repetitions
for z=1, #rep_list do
if type(rep_list[z]) == 'table' and rep_list[z].repetitions then
for r=1, rep_list[z].repetitions do
card_eval_status_text(rep_list[z].card, 'jokers', nil, nil, nil, rep_list[z])
G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card, retrigger_joker = true})
end
end
end
end
end
G.E_MANAGER:add_event(Event({func = function()
card.ignore_incantation_consumable_in_use = true
card:start_dissolve()
return true
end}))
end
end
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.1,
blockable = true,
func = function()
Incantation.consumable_in_use = false
Incantation.accelerate = false
return true
end
}))
end
end
G.FUNCS.use_every_planet = function(e)
local targets = {}
for i = 1, #G.consumeables.cards do
local card = G.consumeables.cards[i]
if card then
local obj = card.config.center
if (((card.config or {}).center or {}).set or '') == 'Planet' and (not obj.can_use or obj:can_use(card)) and not obj.ignore_allplanets then
table.insert(targets, card)
end
end
end
for i = 1, #targets do
local card = targets[i]
G.E_MANAGER:add_event(Event({func = function()
if math.ceil(i/2) == i/2 then play_sound('card1') end
card.area:remove_card(card)
G.play:emplace(card)
return true
end}))
end
G.E_MANAGER:add_event(Event({func = function()
runthrough_planets()
return true
end}))
end
G.FUNCS.use_naivebulk = function(e)
local card = e.config.ref_table
local obj = card.config.center
if card:CanBulkUse() and (not obj.can_use or obj:can_use(card)) and (card:getQty()) > 1 and card.highlighted and CFG.UnsafeMode then
card.naivebulkuse = true
card.bulkuse = true
G.FUNCS.use_card(e, false, true)
end
end
G.FUNCS.disablestackdisplay = function(e)
local card = e.config.ref_table
e.states.visible = ((card:getQty()) > 1 and not card.ignorestacking) or card.cardinuse
end
function Card:create_stack_display()
if not self.children.stackdisplay and self:CanStack() and not self.playing_card then
self.children.stackdisplay = UIBox {
definition = {
n = G.UIT.ROOT,
config = {
minh = 0.6,
maxh = 1.2,
minw = 0.5,
maxw = 2,
r = 0.001,
padding = 0.1,
align = 'cm',
colour = adjust_alpha(darken(G.C.BLACK, 0.2), 0.6),
shadow = false,
func = 'disablestackdisplay',
ref_table = self
},
nodes = {
{
n = G.UIT.T,
config = {
text = 'x',
scale = 0.4,
colour = G.C.MULT
}
},
{
n = G.UIT.T,
config = {
ref_table = self.ability,
ref_value = 'qty',
scale = 0.4,
colour = G.C.UI.TEXT_LIGHT
}
}
}
},
config = {
align = 'cm',
bond = 'Strong',
parent = self
},
states = {
collide = { can = false },
drag = { can = true }
}
}
end
end
local card_load_ref = Card.load
function Card:load(cardTable, other_card)
card_load_ref(self, cardTable, other_card)
if self.ability then
if self.ability.qty then
self:create_stack_display()
end
end
end
local deckadd = Card.add_to_deck
function Card:add_to_deck(from_debuff)
deckadd(self, from_debuff)
if G.consumeables then
if self:CanStack() then
if not self.ignorestacking then
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.1,
blocking = false,
func = function()
if self and self.area and self.area ~= 'shop' and self.area ~= 'pack_cards' then
self:try_merge()
end
return true
end
}))
end
self.ignorestacking = nil
end
end
end
local hlref = Card.highlight
function Card:highlight(is_highlighted)
local isplanet = ((self.ability or {}).set or '') == 'Planet'
if self:CanStack() and self.added_to_deck and not self.ignorestacking then
if is_highlighted then
if isplanet then
self.children.everyplanetbutton = UIBox {
definition = {
n = G.UIT.ROOT,
config = {
minh = 0.3,
maxh = 0.6,
minw = 0.3,
maxw = 4,
r = 0.08,
padding = 0.1,
align = 'cm',
colour = G.C.SECONDARY_SET.Planet,
shadow = true,
button = 'use_every_planet',
func = 'can_use_every_planet',
ref_table = self
},
nodes = {
{
n = G.UIT.T,
config = {
text = 'MASS-USE PLANETS',
scale = 0.3,
colour = G.C.UI.TEXT_LIGHT
}
}
}
},
config = {
align = 'bmi',
offset = {
x = 0,
y = 1
},
bond = 'Strong',
parent = self
}
}
end
self.children.useallbutton = UIBox {
definition = {
n = G.UIT.ROOT,
config = {
minh = 0.3,
maxh = 0.6,
minw = 0.3,
maxw = 4,
r = 0.08,
padding = 0.1,
align = 'cm',
colour = G.C.DARK_EDITION,
shadow = true,
button = 'use_all',
func = 'can_use_all',
ref_table = self
},
nodes = {
{
n = G.UIT.T,
config = {
text = 'BULK USE' .. (CFG.UnsafeMode and ' (NORMAL)' or ''),
scale = 0.3,
colour = G.C.UI.TEXT_LIGHT
}
}
}
},
config = {
align = 'bmi',
offset = {
x = 0,
y = 0.5
},
bond = 'Strong',
parent = self
}
}
self.children.mergebutton = UIBox {
definition = {
n = G.UIT.ROOT,
config = {
minh = 0.3,
maxh = 0.6,
minw = 0.3,
maxw = 4,
r = 0.08,
padding = 0.1,
align = 'cm',
colour = G.C.BLUE,
shadow = true,
button = 'merge_card',
func = 'can_merge_card',
ref_table = self
},
nodes = {
{
n = G.UIT.T,
config = {
text = 'MERGE',
scale = 0.3,
colour = G.C.UI.TEXT_LIGHT
}
}
}
},
config = {
align = 'bmi',
offset = {
x = 0,
y = 1 + (isplanet and 0.5 or 0)
},
bond = 'Strong',
parent = self
}
}
self.children.splithalfbutton = UIBox {
definition = {
n = G.UIT.ROOT,
config = {
minh = 0.3,
maxh = 0.6,
minw = 0.3,
maxw = 4,
r = 0.08,
padding = 0.1,
align = 'cm',
colour = G.C.PURPLE,
shadow = true,
button = 'split_half',
func = 'can_split_card',
ref_table = self
},
nodes = {
{
n = G.UIT.T,
config = {
text = 'SPLIT HALF',
scale = 0.3,
colour = G.C.UI.TEXT_LIGHT
}
}
}
},
config = {
align = 'bmi',
offset = {
x = 0,
y = 1.5 + (isplanet and 0.5 or 0)
},
bond = 'Strong',
parent = self
}
}
self.children.splitonebutton = UIBox {
definition = {
n = G.UIT.ROOT,
config = {
minh = 0.3,
maxh = 0.6,
minw = 0.3,
maxw = 4,
r = 0.08,
padding = 0.1,
align = 'cm',
issplitone = true,
colour = G.C.GREEN,
shadow = true,
button = 'split_one',
func = 'can_split_card',
ref_table = self
},
nodes = {
{
n = G.UIT.T,
config = {
text = 'SPLIT ONE',
scale = 0.3,
colour = G.C.UI.TEXT_LIGHT
}
}
}
},
config = {
align = 'bmi',
offset = {
x = 0,
y = 2 + (isplanet and 0.5 or 0)
},
bond = 'Strong',
parent = self
}
}
if CFG.UnsafeMode then
self.children.useallnaivebutton = UIBox {
definition = {
n = G.UIT.ROOT,
config = {
minh = 0.3,
maxh = 0.6,
minw = 0.3,
maxw = 4,
r = 0.08,
padding = 0.1,
align = 'cm',
colour = G.C.BLACK,
shadow = true,
button = 'use_naivebulk',
func = 'can_use_naivebulk',
ref_table = self
},
nodes = {
{
n = G.UIT.T,
config = {
text = 'BULK USE (ONE-AT-A-TIME)',
scale = 0.3,
colour = G.C.RED
}
}
}
},
config = {
align = 'bmi',
offset = {
x = 0,
y = 2.5 + (isplanet and 0.5 or 0)
},
bond = 'Strong',
parent = self
}
}
end
else
if isplanet then
if self.children.everyplanetbutton then self.children.everyplanetbutton:remove();self.children.everyplanetbutton = nil end
end
if self.children.splithalfbutton then self.children.splithalfbutton:remove();self.children.splithalfbutton = nil end
if self.children.splitonebutton then self.children.splitonebutton:remove();self.children.splitonebutton = nil end
if self.children.mergebutton then self.children.mergebutton:remove();self.children.mergebutton = nil end
if self.children.useallbutton then self.children.useallbutton:remove();self.children.useallbutton = nil end
if CFG.UnsafeMode then
if self.children.useallnaivebutton then self.children.useallnaivebutton:remove();self.children.useallnaivebutton = nil end
end
end
end
return hlref(self,is_highlighted)
end
local ccr = copy_card
function copy_card(other, new_card, card_scale, playing_card, strip_edition)
local card = ccr(other, new_card, card_scale, playing_card, strip_edition)
if card then
if card:getQty() <= 0 then card:setQty(1) end
end
card.OverrideBulkUseLimit = nil
card.bulkuse = nil
card.naivebulkuse = nil
card.cardinuse = nil
card.eval_qty = nil
return card
end
local costref = Card.set_cost
function Card:set_cost()
costref(self)
self.sell_cost = self.sell_cost * math.max(1, (self.ability or {}).qty or 1)
self.sell_cost_label = self.facing == 'back' and '?' or self.sell_cost
end
if not (SMODS.Mods['jen'] or {}).can_load then
SMODS.Joker:take_ownership('perkeo', {
name = "Perkeo (Incantation)",
loc_vars = function(self, info_queue, center)
info_queue[#info_queue+1] = {key = 'e_negative_consumable', set = 'Edition', config = {extra = 1}}
return {vars = {center.ability.extra}}
end,
calculate = function(self, card, context)
if context.ending_shop then
if G.consumeables.cards[1] then
G.E_MANAGER:add_event(Event({
func = function()
local total, checked, center = 0, 0, nil
for i = 1, #G.consumeables.cards do
total = total + (G.consumeables.cards[i]:getQty())
end
local poll = pseudorandom(pseudoseed('perkeo'))*total
for i = 1, #G.consumeables.cards do
checked = checked + (G.consumeables.cards[i]:getQty())
if checked >= poll then
center = G.consumeables.cards[i]
break
end
end
local card = copy_card(center, nil)
card.ability.qty = 1
card:set_edition({negative = true}, true)
card:add_to_deck()
G.consumeables:emplace(card)
return true
end}))
card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex')})
return {calculated = true}
end
end
end
})
end