Jane/src/slugcat.lua

837 lines
25 KiB
Lua

SMODS.Sound({key = "enlightened", path = "enlightened.ogg"})
SMODS.Sound({key = "warning_heartbeat", path = "warning_heartbeat.ogg"})
for i = 1, 8 do
SMODS.Sound({key = "gore" .. i, path = "gore" .. i .. ".ogg"})
end
SMODS.Sound({
key = "music_attuned",
path = "music_attuned.ogg",
volume = 1,
select_music_track = function()
for _, v in pairs(SMODS.find_card("j_jane_saint")) do
if v.ability.extra.is_attuned then
return 201
end
end
return -1 / 0
end,
})
SMODS.Sound({
key = "music_attuned_sinister",
path = "music_attuned_sinister.ogg",
volume = 1,
select_music_track = function()
if not Jane.sinister then
return -1 / 0
end
for _, v in pairs(SMODS.find_card("j_jane_saint")) do
if v.ability.extra.is_attuned then
return 202
end
end
return -1 / 0
end,
})
for _, v in pairs({
"artificer",
"hunter",
"gourmand",
"monk",
"rivulet",
"rot",
"saint",
"spearmaster",
"survivor",
}) do
SMODS.Atlas {
key = "jane" .. v,
px = 71,
py = 95,
path = Jane.config.texture_pack .. "/j_jane_" .. v .. ".png",
}
end
local epic = Jane.cry and "cry_epic" or 3
local exotic = Jane.cry and "cry_exotic" or 4
SMODS.Rarity {
key = "junk",
default_weight = 0.05,
pools = {Joker = true},
loc_txt = {name = "Junk"},
badge_colour = G.C.JOKER_GREY,
}
local monk_limit = 25
SMODS.Joker {
key = "monk",
atlas = "janemonk",
loc_txt = {
name = "The Monk",
text = {
"{C:attention}Retrigger {}scored",
"cards {C:attention}#1# time#2# {}if",
"hand contains {C:attention}#3#",
"or fewer card#4#",
},
},
config = {extra = {retriggers = 2, requirement = 4}},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 20,
rarity = 4,
blueprint_compat = true,
loc_vars = function(_, _, card)
local extra = card.ability.extra
local retriggers = extra.retriggers
local requirement = extra.requirement
return {
vars = {
retriggers,
retriggers == 1 and "" or "s",
requirement,
requirement == 1 and "" or "s",
},
}
end,
calculate = function(_, card, context)
if not context.repetition or
context.cardarea ~= G.play or not
context.other_card or
#G.play.cards > card.ability.extra.requirement then
return
end
local min = math.min(card.ability.extra.retriggers, monk_limit)
card.ability.extra.retriggers = min
return {
card = card,
repetitions = min,
colour = G.C.ORANGE,
message = localize("k_again_ex"),
}, true
end,
}
SMODS.Joker {
key = "survivor",
atlas = "janesurvivor",
loc_txt = {
name = "The Survivor",
text = {
"All cards held in",
"hand also {C:attention}score" .. (Jane.cry and " {}and" or ""),
Jane.cry and "considered as the" or nil,
Jane.cry and "{C:attention}first {}played card" or nil,
},
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 20,
rarity = 4,
}
local hunter = {5, 4, 3, 2, 1}
SMODS.Joker {
key = "hunter",
atlas = "janehunter",
loc_txt = {
name = "The Hunter",
text = {
"{E:1}Succumbs to the {X:black,C:white,E:1}Rot{},",
(Jane.cry and "creating an {C:spectral}Empowered" or "creating a {C:dark_edition}Negative"),
(Jane.cry and "{C:spectral}Tag" or "{C:spectral}Soul") .. " {}after #1#",
},
},
config = {extra = {rounds_left = hunter[1]}},
pos = {x = 0, y = 0},
eternal_compat = false,
blueprint_compat = false,
perishable_compat = false,
soul_pos = {x = 1, y = 0},
cost = Jane.cry and 15 or 8,
rarity = Jane.cry and epic or 3,
loc_vars = function(_, info_queue, card)
local function rounds(amount)
return " round" .. ((math.abs(amount) > 1 or math.abs(amount) == 0) and "s" or "")
end
info_queue[#info_queue + 1] = G.P_CENTERS.j_jane_rot
info_queue[#info_queue + 1] = Jane.cry and G.P_CENTERS.c_cry_empowered or G.P_CENTERS.c_soul
local rounds_left = card.ability.extra.rounds_left
local sold = rounds_left - hunter[5]
return {
vars = {
rounds_left .. rounds(rounds_left) .. (rounds_left <= 0 and "...?" or ""),
sold <= 0 and "" or " after " .. sold .. rounds(sold),
sold <= 0 and " into" or "",
sold <= 0 and "" or "into ",
},
}
end,
update = function(_, card, _)
if card.added_to_deck and card.children.center and card.children.floating_sprite then
for k, v in ipairs(hunter) do
if card.ability.extra.rounds_left <= v then
card.children.center:set_sprite_pos({x = 0, y = k - 1})
card.children.floating_sprite:set_sprite_pos({x = 1, y = k - 1})
else
break
end
end
end
end,
calculate = function(_, card, context)
local function spawn_rot()
card:flip()
card:juice_up(2, 0.8)
card.getting_sliced = true
Jane.card_status_text(card, "Dead!", nil, 0.05 * card.T.h, G.C.BLACK, 2, 0, 0, nil, "bm", "jane_gore6")
Jane.q(function()
local rot = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jane_rot", "hunter_rot_death")
rot:add_to_deck()
G.jokers:emplace(rot)
card:set_eternal(nil)
rot:set_eternal(true)
rot:set_edition(card.edition)
play_sound("jane_gore5")
end)
Jane.q(function()
card:start_dissolve()
end, 1)
end
local function die()
Jane.q(function()
Jane.empowered()
end, 0.1)
spawn_rot()
end
if context.blueprint or
context.individual or
context.repetition or
context.retrigger_joker or
not context.end_of_round then
return
end
card.ability.extra.rounds_left = card.ability.extra.rounds_left - 1
local rl = card.ability.extra.rounds_left
Jane.card_status_text(card, tostring(card.ability.extra.rounds_left), nil, nil, G.C.RED, nil, nil, nil, nil,
nil, "generic1")
if rl > hunter[2] then
card:juice_up(0.6, 0.1)
elseif rl > hunter[3] then
if rl == hunter[2] then
Jane.play_sound("jane_gore1")
end
card:juice_up(0.6, 0.1)
elseif rl > hunter[4] then
if rl == hunter[3] then
Jane.play_sound("jane_gore3")
end
card:juice_up(0.6, 0.1)
elseif rl > hunter[5] then
if rl == hunter[4] then
Jane.play_sound("jane_gore8")
end
card:juice_up(0.6, 0.1)
Jane.play_sound("jane_warning_heartbeat")
elseif rl > 0 then
if rl == hunter[5] then
Jane.play_sound("jane_gore4")
end
card:juice_up(1.8, 0.3)
Jane.play_sound("jane_warning_heartbeat")
else
card:juice_up(2, 0.8)
Jane.play_sound("jane_warning_heartbeat")
Jane.q(die, 0)
end
end,
}
SMODS.Joker {
key = "gourmand",
atlas = "janegourmand",
loc_txt = {
name = "The Gourmand",
text = {"All cards are {C:dark_edition}Jumbo"},
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
blueprint_compat = false,
cost = 20,
rarity = 4,
loc_vars = function(_, info_queue, card)
info_queue[#info_queue + 1] =
(not card.edition or not card.edition.e_jane_jumbo) and
G.P_CENTERS.e_jane_jumbo or nil
end,
}
local function add_to_consumable_ability_by(n)
local f = SMODS.Mods.Roland.qol[1]
local function new(v)
return type(v) == "number" and v + n or type(v) == "table" and f(v):map(new):table() or v
end
---@param card Card
return function(card)
local ability = card.ability or {}
local function go(key)
---@type { [string]: number }|number
local value = ability[key]
--print(key)
if not value then
return
end
if type(value) == "number" then
ability[key] = value + n
return
end
if type(value) ~= "table" then
return
end
local new_value = f(value):map(new):table()
f(new_value):each(function(v, k)
ability[k] = v
end)
ability[key] = new_value
end
local center_key = (card.config or {}).center_key
if center_key and center_key:sub(1, 2) == "c_" then
f {"extra", "consumeable"}:each(go)
end
end
end
local orig_set_ability = Card.set_ability
---@diagnostic disable-next-line: duplicate-set-field
function Card:set_ability(center, initial, delay_sprites)
orig_set_ability(self, center, initial, delay_sprites)
if next(SMODS.find_card("j_jane_rivulet")) and
self.gc and
self:gc().key ~= "c_base" and
string.sub(self:gc().key, 1, 2) == "c_" then
if Jane.cry or not ((SMODS.Mods.Roland or {}).qol or {})[1] then
local mod = 1
for _, v in pairs(SMODS.find_card("j_jane_rivulet")) do
mod = mod * v.ability.modifier
end
Jane.misprintize(self, {min = mod, max = mod}, nil, true)
else
local mod = 0
for _, v in pairs(SMODS.find_card("j_jane_rivulet")) do
mod = mod + v.ability.modifier
end
add_to_consumable_ability_by(mod)(self)
end
end
end
SMODS.Joker {
key = "artificer",
atlas = "janeartificer",
loc_txt = {
name = "The Artificer",
text = {
"Use to {C:red}destroy",
"selected {C:attention}playing cards",
not Jane.cry and "{C:red,E:1}Self-destructs" or nil,
not Jane.cry and "after {C:attention}#1# #2#" or nil,
},
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
config = {extra = {uses = 2}},
eternal_compat = false,
blueprint_compat = false,
cost = Jane.cry and 50 or 7,
rarity = Jane.cry and exotic or 3,
loc_vars = function(_, _, card)
card.ability = card.ability or {}
card.ability.extra = card.ability.extra or {}
card.ability.extra.uses = card.ability.extra.uses or 2
local uses = card.ability.extra.uses
return {vars = {uses, uses == 1 and "use" or "uses"}}
end,
Bakery_use_button_text = function(_, _) return "DESTROY" end,
Bakery_can_use = function(_, card)
return not card.debuff and Jane.can_use() and next(G.hand.highlighted)
end,
Bakery_use_joker = function(_, card)
for _, v in pairs(SMODS.find_card("j_jane_oxy")) do
Jane.oxy(v, G.hand.highlighted)
end
for _, v in pairs(G.hand.highlighted) do
v:start_dissolve()
end
if Jane.cry then
return
end
card.ability.extra.uses = card.ability.extra.uses - 1
local _ = card.ability.extra.uses <= 0 and card:start_dissolve()
local _ = card.ability.extra.uses == 1 and juice_card_until(card, function()
return card.area == G.jokers
end, true)
end,
}
local spearmaster_limit = 25
SMODS.Joker {
key = "spearmaster",
atlas = "janespearmaster",
loc_txt = {
name = "The Spearmaster",
text = {
"You can choose {C:attention}any",
"number of cards",
"in {C:attention}Booster Packs",
"{C:attention}Booster Packs{} have",
"{C:green}+#1#{} additional cards",
},
},
config = {extra = {choices = 1}},
loc_vars = function(_, _, card)
return {vars = {card.ability.extra.choices}}
end,
calculate = function(_, card, _)
card.ability.extra.choices = math.min(card.ability.extra.choices, spearmaster_limit)
end,
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
blueprint_compat = false,
cost = Jane.cry and 12 or 20,
rarity = Jane.cry and epic or 4,
Bakery_can_use = function(_, card)
if card.debuff or not
Jane.can_use() or not
((G.pack_cards or {}).cards or {})[1] then
return false
end
local ret = false
for _, v in pairs(G.pack_cards.cards) do
if v.jane_spearmaster then
return false
elseif ((v.ability or {}).consumeable or {}).hand_type then
ret = true
end
end
return ret
end,
Bakery_use_button_text = function() return "USE ALL" end,
Bakery_use_joker = function(self, card)
if not self:Bakery_can_use(card) then
return
end
local c = G.pack_cards.cards
for i = #c, 1, -1 do
if ((c[i].ability or {}).consumeable or {}).hand_type then
c[i].jane_spearmaster = true
c[i]:use_consumeable()
SMODS.calculate_context {using_consumeable = true, consumeable = c[i], area = c[i].from_area}
local normal = c[i].area ~= G.pack_cards
c[i]:start_dissolve()
if not normal then
G.GAME.pack_choices = G.GAME.pack_choices - 1
local _ = G.GAME.pack_choices <= 0 and G.FUNCS.end_consumeable()
end
end
end
end,
}
local orig_open = Card.open
---@diagnostic disable-next-line: duplicate-set-field
function Card:open()
local orig = self.ability.extra or 1
local spearmasters = SMODS.find_card("j_jane_spearmaster")
if next(spearmasters) then
for _, v in pairs(spearmasters) do
orig = orig + v.ability.extra.choices
end
self.config.choose = math.floor(orig)
self.ability.extra = math.floor(orig)
end
orig_open(self)
Jane.q(function()
if next(spearmasters) then
G.GAME.pack_choices = math.floor(self.ability.extra)
end
end, 0.5, "REAL")
end
SMODS.Joker {
key = "rivulet",
atlas = "janerivulet",
loc_txt = {
name = "The Rivulet",
text = {
"Values on {C:attention}consumables",
"are {C:attention}" .. (Jane.cry and "multiplied" or "added") .. "{} by {C:attention}#1#" .. (Jane.cry and "" or "{} when"),
(Jane.cry and "when " or "") .. "they are created",
"{C:inactive}(If possible)",
},
},
config = {modifier = Jane.cry and 2 or 1},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
blueprint_compat = false,
cost = 8,
rarity = 3,
loc_vars = function(_, _, center)
return {vars = {center.ability.modifier}}
end,
}
local orig_draw = Card.draw
function Card:draw(layer)
local cen = self.gc and self:gc()
if self.children.floating_sprite and cen.sinis then
if Jane.sinister and not self.shows_sinister then
self.shows_sinister = true
self.children.floating_sprite:set_sprite_pos(cen.sinis)
elseif not Jane.sinister and self.shows_sinister then
self.shows_sinister = nil
self.children.floating_sprite:set_sprite_pos(cen.soul_pos)
end
if self.shows_sinister then
self:juice_up(0, math.random())
end
end
if cen and
self.facing == "front" and
self.config and
(self.added_to_deck or (self.area and self.area == G.hand)) and
not self.edition and
(self.area == G.consumeables or cen.set ~= "Booster") and
next(SMODS.find_card("j_jane_gourmand")) then
self:set_edition("e_jane_jumbo", true, true)
end
orig_draw(self, layer)
end
local orig_debuff = Card.set_debuff
function Card:set_debuff(should_debuff)
if should_debuff and ((self.config or {}).center or {}).debuff_immune then
Jane.card_status_text(self, "Immune!", nil, 0.05 * self.T.h, G.C.RED, nil, 0.6, nil, nil, "bm", "cancel", 1, 0.9)
return false
else
orig_debuff(self, should_debuff)
end
end
SMODS.Joker {
key = "saint",
atlas = "janesaint",
loc_txt = {
name = {"{}", "#1#{C:jane_RGB,s:0.8}#2#", "{C:jane_RGB,s:1.6}#3#"},
text = {
"{C:attention}Use {}to toggle itself or",
"other Jokers to its",
"left's {C:legendary}Eternal{} sticker",
"{C:attention}#4#{}#5#{C:spectral}#6#{C:planet}#7#{}#8#",
"{C:inactive,s:1.25}#9#{C:attention,s:1.25}#10#{C:inactive,s:1.25}#11#",
Jane.cry and "{C:inactive}(Gateway has no downside)" or nil,
},
},
config = {extra = {changes_editions = false, jokers = 1, karma = -10}},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 20,
rarity = 4,
debuff_immune = true,
eternal_compat = true,
blueprint_compat = false,
perishable_compat = true,
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = Jane.cry and G.P_CENTERS.c_cry_gateway or nil
local extra = card.ability.extra
local attuned = extra.karma >= 0
return {
vars = {
attuned and "" or "The Saint",
attuned and "The" or "",
attuned and "Attuned" or "",
attuned and "" or "Attune ",
attuned and "" or "at ",
attuned and (Jane.cry and "Gateways " or "Spectrals ") or
(Jane.cry and -self.config.extra.karma .. " Gateways" or ""),
(attuned or Jane.cry) and "" or -self.config.extra.karma .. " Planets",
attuned and "add Joker slots" or "",
attuned and "[Currently " or "[",
attuned and "+" .. extra.karma or extra.karma - self.config.extra.karma,
attuned and "]" or " / " .. -self.config.extra.karma .. "]",
},
}
end,
update = function(_, card, _)
if card.edition and card.edition.Roland_frozen then
card:set_edition("e_base")
elseif card.children.floating_sprite then
card.children.floating_sprite:set_sprite_pos({x = card.ability.extra.is_attuned and 2 or 1, y = 0})
end
end,
calculate = function(self, card, context)
local extra = card.ability.extra
extra.is_attuned = extra.is_attuned and extra.karma >= 0
local function ascend()
if extra.is_attuning then
return
end
extra.is_attuning = true
Jane.card_status_text(
card,
"!!!",
nil,
0.05 * card.T.h,
G.C.DARK_EDITION,
0.6,
0.6,
2,
2,
"bm",
"jane_enlightened"
)
Jane.q(function()
card:flip()
play_sound("card1")
end, 0.1)
Jane.q(function()
card:flip()
card:juice_up(1, 1)
play_sound("card1")
extra.is_attuned = true
end, 1)
Jane.q(function()
extra.karma = extra.karma + extra.jokers
G.jokers:change_size(extra.jokers)
end, 1)
end
if extra.karma >= 0 then
local karma = extra.is_attuning and 0 or (extra.is_attuned and 1 or extra.karma)
G.jokers:change_size(karma * extra.jokers)
ascend()
end
if not (not context.blueprint and
(not context.retrigger_joker_check and not context.retrigger_joker) and
context.using_consumeable and
context.consumeable) then
return
end
local gc = context.consumeable:gc()
if Jane.cry and gc.key ~= "c_cry_gateway" or
not Jane.cry and gc.set ~= (extra.karma > 0 and "Spectral" or "Planet") then
return
end
local quota = context.consumeable.getEvalQty and context.consumeable:getEvalQty() or 1
extra.karma = extra.karma + quota
card_eval_status_text(
card,
"extra",
nil,
nil,
nil,
{message = "+" .. quota .. " Karma", colour = G.C.PALE_GREEN}
)
local _ = extra.karma <= 0 and card_eval_status_text(
card,
"extra",
nil,
nil,
nil,
{
message = (extra.karma - self.config.extra.karma) ..
" / " ..
-self.config.extra.karma,
colour = G.C.GREEN,
}
)
if extra.karma < 0 then
return
end
G.jokers:change_size((extra.is_attuned and quota or extra.karma) * extra.jokers)
ascend()
end,
add_to_deck = function(_, card)
local extra = card.ability.extra
local _ = extra.karma > 0 and
(extra.is_attuning or extra.is_attuned) and
G.jokers:change_size(extra.karma * extra.jokers)
end,
remove_from_deck = function(_, card)
local extra = card.ability.extra
local _ = extra.karma > 0 and
(extra.is_attuning or extra.is_attuned) and
G.jokers:change_size(-extra.karma * extra.jokers)
end,
Bakery_can_use = function(_, card)
return Jane.can_use() and not card.debuff
end,
Bakery_use_joker = function(_, card)
for joker_index = 1, math.max(card.rank - 1, 1) do
local joker = G.jokers.cards[joker_index]
joker:set_eternal(not joker.ability.eternal)
if joker.Roland_frozen_ability then
joker.Roland_frozen_ability.eternal = joker.ability.eternal
end
if not card.ability.extra.changes_editions or
not card.ability.extra.is_attuned or
joker.ability.eternal then
joker:juice_up()
goto continue
end
local edition = 1
for ii, vv in ipairs(joker.edition and G.P_CENTER_POOLS.Edition or {}) do
if vv.key == joker.edition.key then
edition = ii
break
end
end
local next = edition < #G.P_CENTER_POOLS.Edition and edition + 1 or 1
joker:set_edition(G.P_CENTER_POOLS.Edition[next].key)
::continue::
end
end,
}
if Cryptid and Cryptid.aliases then
Cryptid.aliases["saint"] = "j_jane_saint"
Cryptid.aliases["attuned"] = "j_jane_saint"
Cryptid.aliases["the saint"] = "j_jane_saint"
Cryptid.aliases["the attuned"] = "j_jane_saint"
end
SMODS.Joker {
key = "rot",
atlas = "janerot",
loc_txt = {
name = "The Rot",
text = {
Jane.cry and "{C:attention}Duplicates itself{} at the" or "{C:inactive,E:1}Does nothing",
Jane.cry and "end of {C:attention}every ante" or nil,
},
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
blueprint_compat = true,
cost = 1,
rarity = "jane_junk",
in_pool = function(_, _)
if next(SMODS.find_card("j_jane_rot")) then
return true, {allow_duplicates = true}
end
for _, v in pairs(SMODS.find_card("j_jane_honey")) do
if tonumber(v.ability.extra.level) == #Jane.rarity_ids - 1 then
return true, {allow_duplicates = true}
end
end
end,
calculate = Jane.cry and function(_, card, context)
local function has_room()
return G.jokers.config.card_count < G.jokers.config.card_limit
end
local function spawn()
if card.cloned then
return
end
local rot = copy_card(card)
rot.cloned = true
rot:add_to_deck()
G.jokers:emplace(rot)
Jane.card_status_text(rot, "...", nil, 0.05 * card.T.h, G.C.BLACK, 3, 0, 0, nil, "bm")
end
if has_room() and not card.cloned and Jane.is_end_of_ante(context, card) then
Jane.q(spawn, 0.5)
else
card.cloned = false
end
end or nil,
}