removed duplicate smods
This commit is contained in:
parent
9000c2b6d7
commit
feefd09596
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
**/lovely*.log
|
||||
**/lovely*.log
|
||||
lovely/
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = 'cd00b45ad63b1eaa00d4924a1eed9a6a330e531d1440767ad8b6703f1eec6dbf'
|
||||
LOVELY_INTEGRITY = 'd3c881055f5938773f6b44e7c8b207117963612b2eaf889f5b7ef8b61e1143cd'
|
||||
|
||||
--Class
|
||||
Back = Object:extend()
|
||||
|
@ -130,16 +130,9 @@ end
|
|||
function Back:trigger_effect(args)
|
||||
if not args then return end
|
||||
local obj = self.effect.center
|
||||
if type(obj.calculate) == 'function' then
|
||||
local o = {obj:calculate(self, args)}
|
||||
if next(o) ~= nil then return unpack(o) end
|
||||
elseif type(obj.trigger_effect) == 'function' then
|
||||
-- kept for compatibility
|
||||
if obj.trigger_effect and type(obj.trigger_effect) == 'function' then
|
||||
local o = {obj:trigger_effect(args)}
|
||||
if next(o) ~= nil then
|
||||
sendWarnMessage(('Found `trigger_effect` function on SMODS.Back object "%s". This field is deprecated; please use `calculate` instead.'):format(obj.key), 'Back')
|
||||
return unpack(o)
|
||||
end
|
||||
if o then return unpack(o) end
|
||||
end
|
||||
|
||||
if self.name == 'Anaglyph Deck' and args.context == 'eval' and G.GAME.last_blind and G.GAME.last_blind.boss then
|
||||
|
@ -208,33 +201,14 @@ end
|
|||
function Back:apply_to_run()
|
||||
local obj = self.effect.center
|
||||
if obj.apply and type(obj.apply) == 'function' then
|
||||
obj:apply(self)
|
||||
obj:apply()
|
||||
end
|
||||
|
||||
if self.effect.config.jokers then
|
||||
delay(0.4)
|
||||
G.E_MANAGER:add_event(Event({
|
||||
func = function()
|
||||
for k, v in ipairs(self.effect.config.jokers) do
|
||||
local card = create_card('Joker', G.jokers, nil, nil, nil, nil, v, 'deck')
|
||||
card:add_to_deck()
|
||||
G.jokers:emplace(card)
|
||||
card:start_materialize()
|
||||
end
|
||||
return true
|
||||
end
|
||||
}))
|
||||
end
|
||||
if self.effect.config.voucher then
|
||||
G.GAME.used_vouchers[self.effect.config.voucher] = true
|
||||
G.GAME.cry_owned_vouchers[self.effect.config.voucher] = true
|
||||
G.GAME.starting_voucher_count = (G.GAME.starting_voucher_count or 0) + 1
|
||||
G.E_MANAGER:add_event(Event({
|
||||
func = function()
|
||||
Card.apply_to_run(nil, G.P_CENTERS[self.effect.config.voucher])
|
||||
return true
|
||||
end
|
||||
}))
|
||||
Card.apply_to_run(nil, G.P_CENTERS[self.effect.config.voucher])
|
||||
end
|
||||
if self.effect.config.hands then
|
||||
G.GAME.starting_params.hands = G.GAME.starting_params.hands + self.effect.config.hands
|
||||
|
@ -292,12 +266,7 @@ function Back:apply_to_run()
|
|||
G.GAME.used_vouchers[v ] = true
|
||||
G.GAME.cry_owned_vouchers[v ] = true
|
||||
G.GAME.starting_voucher_count = (G.GAME.starting_voucher_count or 0) + 1
|
||||
G.E_MANAGER:add_event(Event({
|
||||
func = function()
|
||||
Card.apply_to_run(nil, G.P_CENTERS[v])
|
||||
return true
|
||||
end
|
||||
}))
|
||||
Card.apply_to_run(nil, G.P_CENTERS[v])
|
||||
end
|
||||
end
|
||||
if self.name == 'Checkered Deck' then
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = '2c00c26e55b0f21d108e0353b98757a950fc433f1f9951ac8a454c589450b12c'
|
||||
LOVELY_INTEGRITY = 'ceaf7d365cb8eac62c7dcf3be1ef07cc6b0dac3977bde52960496d7f844a668d'
|
||||
|
||||
--class
|
||||
Blind = Moveable:extend()
|
||||
|
@ -606,7 +606,7 @@ function Blind:debuff_hand(cards, hand, handname, check)
|
|||
end
|
||||
if self.name == 'The Arm' then
|
||||
self.triggered = false
|
||||
if to_big(G.GAME.hands[handname].level) > to_big(1) then
|
||||
if G.GAME.hands[handname].level > 1 then
|
||||
self.triggered = true
|
||||
if not check then
|
||||
level_up_hand(self.children.animatedSprite, handname, nil, -1)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = 'b7b1bb9e3088ca71ed96c4b7bdaf1ec1c67106c37ba37b8f4aef385f8743a60b'
|
||||
LOVELY_INTEGRITY = 'd333bdf58ea96ab32bdb8d74d1b3a666732aeed72e1f1374e45a87d167ad1109'
|
||||
|
||||
--class
|
||||
Card = Moveable:extend()
|
||||
|
@ -46,9 +46,9 @@ function Card:init(X, Y, W, H, card, center, params)
|
|||
self:set_base(card, true)
|
||||
|
||||
self.discard_pos = {
|
||||
r = 0,
|
||||
x = 0,
|
||||
y = 0,
|
||||
r = 3.6*(math.random()-0.5),
|
||||
x = math.random(),
|
||||
y = math.random()
|
||||
}
|
||||
|
||||
self.facing = 'front'
|
||||
|
@ -227,12 +227,6 @@ function Card:set_sprites(_center, _front)
|
|||
if _center.name == 'Square Joker' and (_center.discovered or self.bypass_discovery_center) then
|
||||
self.children.center.scale.y = self.children.center.scale.x
|
||||
end
|
||||
if _center.pixel_size and _center.pixel_size.h and (_center.discovered or self.bypass_discovery_center) then
|
||||
self.children.center.scale.y = self.children.center.scale.y*(_center.pixel_size.h/95)
|
||||
end
|
||||
if _center.pixel_size and _center.pixel_size.w and (_center.discovered or self.bypass_discovery_center) then
|
||||
self.children.center.scale.x = self.children.center.scale.x*(_center.pixel_size.w/71)
|
||||
end
|
||||
end
|
||||
|
||||
if _center.soul_pos then
|
||||
|
@ -347,20 +341,6 @@ function Card:set_ability(center, initial, delay_sprites)
|
|||
self.T.h = H
|
||||
self.T.w = W
|
||||
end
|
||||
if center.display_size and center.display_size.h and (center.discovered or self.bypass_discovery_center) then
|
||||
H = H*(center.display_size.h/95)
|
||||
self.T.h = H
|
||||
elseif center.pixel_size and center.pixel_size.h and (center.discovered or self.bypass_discovery_center) then
|
||||
H = H*(center.pixel_size.h/95)
|
||||
self.T.h = H
|
||||
end
|
||||
if center.display_size and center.display_size.w and (center.discovered or self.bypass_discovery_center) then
|
||||
W = W*(center.display_size.w/71)
|
||||
self.T.w = W
|
||||
elseif center.pixel_size and center.pixel_size.w and (center.discovered or self.bypass_discovery_center) then
|
||||
W = W*(center.pixel_size.w/71)
|
||||
self.T.w = W
|
||||
end
|
||||
|
||||
if delay_sprites then
|
||||
G.E_MANAGER:add_event(Event({
|
||||
|
@ -477,6 +457,7 @@ function Card:set_ability(center, initial, delay_sprites)
|
|||
self.ability.loyalty_remaining = self.ability.extra.every
|
||||
end
|
||||
|
||||
self.ability.cry_prob = 1
|
||||
self.base_cost = center.cost or 1
|
||||
|
||||
self.ability.hands_played_at_create = G.GAME and G.GAME.hands_played or 0
|
||||
|
@ -628,14 +609,6 @@ function Card:set_seal(_seal, silent, immediate)
|
|||
self.seal = nil
|
||||
if _seal then
|
||||
self.seal = _seal
|
||||
self.ability.seal = {}
|
||||
for k, v in pairs(G.P_SEALS[_seal].config or {}) do
|
||||
if type(v) == 'table' then
|
||||
self.ability.seal[k] = copy_table(v)
|
||||
else
|
||||
self.ability.seal[k] = v
|
||||
end
|
||||
end
|
||||
if not silent then
|
||||
G.CONTROLLER.locks.seal = true
|
||||
local sound = G.P_SEALS[_seal].sound or {sound = 'gold_seal', per = 1.2, vol = 0.4}
|
||||
|
@ -908,21 +881,10 @@ function Card:generate_UIBox_unlock_table(hidden)
|
|||
return generate_card_ui(self.config.center, nil, loc_vars, 'Locked')
|
||||
end
|
||||
|
||||
function Card:generate_UIBox_ability_table(vars_only)
|
||||
function Card:generate_UIBox_ability_table()
|
||||
local card_type, hide_desc = self.ability.set or "None", nil
|
||||
local loc_vars = nil
|
||||
local main_start, main_end = nil,nil
|
||||
if self.ability.name == 'cry-Machine Code' then
|
||||
--"Create a random // glitched consumable"
|
||||
main_start = {
|
||||
randomchar(codechars6),
|
||||
randomchar(codechars6),
|
||||
randomchar(codechars6),
|
||||
randomchar(codechars6),
|
||||
randomchar(codechars6),
|
||||
randomchar(codechars6),
|
||||
}
|
||||
end
|
||||
local no_badge = nil
|
||||
|
||||
if not self.bypass_lock and self.config.center.unlocked ~= false and
|
||||
|
@ -957,7 +919,7 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Fortune Teller' then loc_vars = {self.ability.extra, (G.GAME.consumeable_usage_total and G.GAME.consumeable_usage_total.tarot or 0)}
|
||||
elseif self.ability.name == 'Steel Joker' then loc_vars = {self.ability.extra, 1 + self.ability.extra*(self.ability.steel_tally or 0)}
|
||||
elseif self.ability.name == 'Chaos the Clown' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Space Joker' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra}
|
||||
elseif self.ability.name == 'Space Joker' then loc_vars = {cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged), self.ability.extra}
|
||||
elseif self.ability.name == 'Stone Joker' then loc_vars = {self.ability.extra, self.ability.extra*(self.ability.stone_tally or 0)}
|
||||
elseif self.ability.name == 'Drunkard' then loc_vars = {self.ability.d_size}
|
||||
elseif self.ability.name == 'Green Joker' then loc_vars = {self.ability.extra.hand_add, self.ability.extra.discard_sub, self.ability.mult}
|
||||
|
@ -977,18 +939,6 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Four Fingers' then
|
||||
elseif self.ability.name == 'Ceremonial Dagger' then loc_vars = {self.ability.mult}
|
||||
elseif self.ability.name == 'Banner' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'cry-Error' then
|
||||
if G.GAME and G.GAME.pseudorandom and G.STAGE == G.STAGES.RUN then
|
||||
cry_error_msgs[#cry_error_msgs].string = "%%" .. predict_card_for_shop()
|
||||
else
|
||||
cry_error_msgs[#cry_error_msgs].string = "%%J6"
|
||||
end
|
||||
main_start = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = cry_error_operators, colours = {G.C.DARK_EDITION,},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.30, scale = 0.32, min_cycle_time = 0})}},
|
||||
{n=G.UIT.O, config={object = DynaText({string = cry_error_numbers, colours = {G.C.DARK_EDITION,},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.33, scale = 0.32, min_cycle_time = 0})}},
|
||||
{n=G.UIT.O, config={object = DynaText({string = cry_error_msgs,
|
||||
colours = {G.C.UI.TEXT_DARK},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.4011, scale = 0.32, min_cycle_time = 0})}},
|
||||
}
|
||||
elseif self.ability.name == 'Misprint' then
|
||||
local r_mults = {}
|
||||
for i = self.ability.extra.min, self.ability.extra.max do
|
||||
|
@ -1006,18 +956,18 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Mystic Summit' then loc_vars = {self.ability.extra.mult, self.ability.extra.d_remaining}
|
||||
elseif self.ability.name == 'Marble Joker' then
|
||||
elseif self.ability.name == 'Loyalty Card' then loc_vars = {self.ability.extra.Xmult, self.ability.extra.every + 1, localize{type = 'variable', key = (self.ability.loyalty_remaining == 0 and 'loyalty_active' or 'loyalty_inactive'), vars = {self.ability.loyalty_remaining}}}
|
||||
elseif self.ability.name == '8 Ball' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1),self.ability.extra}
|
||||
elseif self.ability.name == '8 Ball' then loc_vars = {cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged),self.ability.extra}
|
||||
elseif self.ability.name == 'Dusk' then loc_vars = {self.ability.extra+1}
|
||||
elseif self.ability.name == 'Raised Fist' then
|
||||
elseif self.ability.name == 'Fibonacci' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Scary Face' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Abstract Joker' then loc_vars = {self.ability.extra, (G.jokers and G.jokers.cards and #G.jokers.cards or 0)*self.ability.extra}
|
||||
elseif self.ability.name == 'Delayed Gratification' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Gros Michel' then loc_vars = {self.ability.extra.mult, ''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds}
|
||||
elseif self.ability.name == 'Gros Michel' then loc_vars = {self.ability.extra.mult, cry_prob(self.ability.cry_prob, self.ability.extra.odds, self.ability.cry_rigged), self.ability.extra.odds}
|
||||
elseif self.ability.name == 'Even Steven' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Odd Todd' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Scholar' then loc_vars = {self.ability.extra.mult, self.ability.extra.chips}
|
||||
elseif self.ability.name == 'Business Card' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra}
|
||||
elseif self.ability.name == 'Business Card' then loc_vars = {cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged),self.ability.extra}
|
||||
elseif self.ability.name == 'Supernova' then
|
||||
elseif self.ability.name == 'Spare Trousers' then loc_vars = {self.ability.extra, localize('Two Pair', 'poker_hands'), self.ability.mult}
|
||||
elseif self.ability.name == 'Superposition' then loc_vars = {self.ability.extra}
|
||||
|
@ -1033,7 +983,7 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Hiker' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'To Do List' then loc_vars = {self.ability.extra.dollars, localize(self.ability.to_do_poker_hand, 'poker_hands')}
|
||||
elseif self.ability.name == 'Smeared Joker' then
|
||||
elseif self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' then
|
||||
elseif self.ability.name == 'Blueprint' then
|
||||
self.ability.blueprint_compat_ui = self.ability.blueprint_compat_ui or ''; self.ability.blueprint_compat_check = nil
|
||||
main_end = (self.area and self.area == G.jokers) and {
|
||||
{n=G.UIT.C, config={align = "bm", minh = 0.4}, nodes={
|
||||
|
@ -1055,7 +1005,7 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Throwback' then loc_vars = {self.ability.extra, self.ability.x_mult}
|
||||
elseif self.ability.name == 'Hanging Chad' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Rough Gem' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Bloodstone' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds, self.ability.extra.Xmult}
|
||||
elseif self.ability.name == 'Bloodstone' then loc_vars = {cry_prob(self.ability.cry_prob, self.ability.extra.odds, self.ability.cry_rigged), self.ability.extra.odds, self.ability.extra.Xmult}
|
||||
elseif self.ability.name == 'Arrowhead' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Onyx Agate' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Glass Joker' then loc_vars = {self.ability.extra, self.ability.x_mult}
|
||||
|
@ -1070,7 +1020,7 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'The Duo' or self.ability.name == 'The Trio'
|
||||
or self.ability.name == 'The Family' or self.ability.name == 'The Order' or self.ability.name == 'The Tribe' then loc_vars = {self.ability.x_mult, localize(self.ability.type, 'poker_hands')}
|
||||
|
||||
elseif self.ability.name == 'Cavendish' then loc_vars = {self.ability.extra.Xmult, ''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds}
|
||||
elseif self.ability.name == 'Cavendish' then loc_vars = {self.ability.extra.Xmult, cry_prob(self.ability.cry_prob, self.ability.extra.odds, self.ability.cry_rigged), self.ability.extra.odds}
|
||||
elseif self.ability.name == 'Card Sharp' then loc_vars = {self.ability.extra.Xmult}
|
||||
elseif self.ability.name == 'Red Card' then loc_vars = {self.ability.extra, self.ability.mult}
|
||||
elseif self.ability.name == 'Madness' then loc_vars = {self.ability.extra, self.ability.x_mult}
|
||||
|
@ -1102,10 +1052,10 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Gift Card' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Turtle Bean' then loc_vars = {self.ability.extra.h_size, self.ability.extra.h_mod}
|
||||
elseif self.ability.name == 'Erosion' then loc_vars = {self.ability.extra, math.max(0,self.ability.extra*(G.playing_cards and (G.GAME.starting_deck_size - #G.playing_cards) or 0)), G.GAME.starting_deck_size}
|
||||
elseif self.ability.name == 'Reserved Parking' then loc_vars = {self.ability.extra.dollars, ''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds}
|
||||
elseif self.ability.name == 'Reserved Parking' then loc_vars = {self.ability.extra.dollars, cry_prob(self.ability.cry_prob, self.ability.extra.odds, self.ability.cry_rigged), self.ability.extra.odds}
|
||||
elseif self.ability.name == 'Mail-In Rebate' then loc_vars = {self.ability.extra, localize(G.GAME.current_round.mail_card.rank, 'ranks')}
|
||||
elseif self.ability.name == 'To the Moon' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Hallucination' then loc_vars = {G.GAME.probabilities.normal, self.ability.extra}
|
||||
elseif self.ability.name == 'Hallucination' then loc_vars = {cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged), self.ability.extra}
|
||||
elseif self.ability.name == 'Lucky Cat' then loc_vars = {self.ability.extra, self.ability.x_mult}
|
||||
elseif self.ability.name == 'Baseball Card' then loc_vars = {self.ability.extra}
|
||||
elseif self.ability.name == 'Bull' then loc_vars = {self.ability.extra, self.ability.extra*math.max(0,G.GAME.dollars) or 0}
|
||||
|
@ -1122,7 +1072,7 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Campfire' then loc_vars = {self.ability.extra, self.ability.x_mult}
|
||||
elseif self.ability.name == 'Stuntman' then loc_vars = {self.ability.extra.chip_mod, self.ability.extra.h_size}
|
||||
elseif self.ability.name == 'Invisible Joker' then loc_vars = {self.ability.extra, self.ability.invis_rounds}
|
||||
elseif self.ability.name == 'Brainstorm' or self.config.center.key == 'j_cry_gemino' then
|
||||
elseif self.ability.name == 'Brainstorm' then
|
||||
self.ability.blueprint_compat_ui = self.ability.blueprint_compat_ui or ''; self.ability.blueprint_compat_check = nil
|
||||
main_end = (self.area and self.area == G.jokers) and {
|
||||
{n=G.UIT.C, config={align = "bm", minh = 0.4}, nodes={
|
||||
|
@ -1146,7 +1096,6 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
elseif self.ability.name == 'Perkeo' then loc_vars = {self.ability.extra}
|
||||
end
|
||||
end
|
||||
if vars_only then return loc_vars, main_start, main_end end
|
||||
local badges = {}
|
||||
if (card_type ~= 'Locked' and card_type ~= 'Undiscovered' and card_type ~= 'Default') or self.debuff then
|
||||
badges.card_type = card_type
|
||||
|
@ -1164,7 +1113,7 @@ function Card:generate_UIBox_ability_table(vars_only)
|
|||
end
|
||||
end
|
||||
if self.seal then badges[#badges + 1] = string.lower(self.seal)..'_seal' end
|
||||
if self.ability.eternal then badges[#badges + 1] = 'eternal' end
|
||||
if self.ability.eternal and not self.ability.cry_absolute then badges[#badges + 1] = 'eternal' end
|
||||
if self.ability.perishable and not layer then
|
||||
loc_vars = loc_vars or {}; loc_vars.perish_tally=self.ability.perish_tally
|
||||
badges[#badges + 1] = 'perishable'
|
||||
|
@ -1239,7 +1188,7 @@ function Card:get_chip_mult()
|
|||
if self.debuff then return 0 end
|
||||
if self.ability.set == 'Joker' then return 0 end
|
||||
if self.ability.effect == "Lucky Card" then
|
||||
if pseudorandom('lucky_mult') < G.GAME.probabilities.normal/5 then
|
||||
if pseudorandom('lucky_mult') < cry_prob(self.ability.cry_prob, 5, self.ability.cry_rigged)/5 then
|
||||
self.lucky_trigger = true
|
||||
return self.ability.mult
|
||||
else
|
||||
|
@ -1271,6 +1220,9 @@ function Card:get_edition()
|
|||
if self.debuff then return end
|
||||
if self.edition then
|
||||
local ret = {card = self}
|
||||
if self.edition.p_dollars then
|
||||
ret.p_dollars_mod = self.edition.p_dollars
|
||||
end
|
||||
if self.edition.x_mult then
|
||||
ret.x_mult_mod = self.edition.x_mult
|
||||
end
|
||||
|
@ -1330,7 +1282,7 @@ function Card:get_p_dollars()
|
|||
end
|
||||
if self.ability.p_dollars > 0 then
|
||||
if self.ability.effect == "Lucky Card" then
|
||||
if pseudorandom('lucky_money') < G.GAME.probabilities.normal/15 then
|
||||
if pseudorandom('lucky_money') < cry_prob(self.ability.cry_prob, 15, self.ability.cry_rigged)/15 then
|
||||
self.lucky_trigger = true
|
||||
ret = ret + self.ability.p_dollars
|
||||
end
|
||||
|
@ -1686,7 +1638,10 @@ function Card:use_consumeable(area, copier)
|
|||
ease_dollars(self.ability.extra.dollars)
|
||||
end
|
||||
delay(0.3)
|
||||
SMODS.calculate_context({ remove_playing_cards = true, removed = destroyed_cards })
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({remove_playing_cards = true, removed = destroyed_cards})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'remove_playing_cards', removed = destroyed_cards})
|
||||
end
|
||||
if self.ability.name == 'The Fool' then
|
||||
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function()
|
||||
|
@ -1785,7 +1740,7 @@ function Card:use_consumeable(area, copier)
|
|||
if self.ability.name == 'The Wheel of Fortune' or self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' then
|
||||
local temp_pool = (self.ability.name == 'The Wheel of Fortune' and self.eligible_strength_jokers) or
|
||||
((self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex') and self.eligible_editionless_jokers) or {}
|
||||
if self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' or pseudorandom('wheel_of_fortune') < G.GAME.probabilities.normal/self.ability.extra then
|
||||
if self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' or pseudorandom('wheel_of_fortune') < cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged)/self.ability.extra then
|
||||
if self.ability.name == 'The Wheel of Fortune' then self.cry_wheel_success = true end
|
||||
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function()
|
||||
local over = false
|
||||
|
@ -1931,8 +1886,18 @@ function Card:sell_card()
|
|||
if self.children.use_button then self.children.use_button:remove(); self.children.use_button = nil end
|
||||
if self.children.sell_button then self.children.sell_button:remove(); self.children.sell_button = nil end
|
||||
|
||||
local eval, post = eval_card(self, {selling_self = true})
|
||||
SMODS.trigger_effects({eval, post}, self)
|
||||
if self.config.center.set == 'Joker' then
|
||||
if G.GAME.jokers_sold then
|
||||
local contained = false
|
||||
for i = 1, #G.GAME.jokers_sold do
|
||||
if self.config.center.key == G.GAME.jokers_sold[i] then contained = true end
|
||||
end
|
||||
if not contained then table.insert(G.GAME.jokers_sold, self.config.center.key) end
|
||||
else
|
||||
G.GAME.jokers_sold = {self.config.center.key}
|
||||
end
|
||||
end
|
||||
self:calculate_joker{selling_self = true}
|
||||
|
||||
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function()
|
||||
if not G.GAME.modifiers.cry_no_sell_value then play_sound('coin2') end
|
||||
|
@ -1990,14 +1955,15 @@ end
|
|||
|
||||
function Card:calculate_dollar_bonus()
|
||||
if self.debuff then return end
|
||||
local obj = self.config.center
|
||||
if obj.calc_dollar_bonus and type(obj.calc_dollar_bonus) == 'function' then
|
||||
return obj:calc_dollar_bonus(self)
|
||||
end
|
||||
if self.ability.set == "Joker" then
|
||||
if self.ability.name == 'Golden Joker' then
|
||||
return self.ability.extra
|
||||
end
|
||||
--asdf
|
||||
local obj = self.config.center
|
||||
if obj.calc_dollar_bonus and type(obj.calc_dollar_bonus) == 'function' then
|
||||
return obj:calc_dollar_bonus(self)
|
||||
end
|
||||
if self.ability.name == 'Cloud 9' and self.ability.nine_tally and self.ability.nine_tally > 0 then
|
||||
return self.ability.extra*(self.ability.nine_tally)
|
||||
end
|
||||
|
@ -2051,6 +2017,7 @@ function Card:open()
|
|||
end
|
||||
|
||||
G.GAME.pack_choices = self.config.center.config.choose or 1
|
||||
G.GAME.pack_choices = ((self.ability.choose and self.ability.extra) and math.min(math.floor(self.ability.extra), self.ability.choose)) or 1
|
||||
if G.GAME.modifiers.cry_misprint_min then
|
||||
G.GAME.pack_size = self.ability.extra
|
||||
if G.GAME.pack_size < 1 then G.GAME.pack_size = 1 end
|
||||
|
@ -2187,7 +2154,10 @@ end
|
|||
end
|
||||
end}))
|
||||
|
||||
SMODS.calculate_context({open_booster = true, card = self})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({open_booster = true, card = self})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'open_booster', card = self})
|
||||
|
||||
if G.GAME.modifiers.inflation then
|
||||
G.GAME.inflation = G.GAME.inflation + 1
|
||||
|
@ -2244,14 +2214,18 @@ function Card:redeem()
|
|||
inc_career_stat('c_vouchers_bought', 1)
|
||||
set_voucher_usage(self)
|
||||
check_for_unlock({type = 'run_redeem'})
|
||||
G.GAME.current_round.voucher = nil
|
||||
-- G.GAME.current_round.voucher = nil
|
||||
if self.shop_cry_bonusvoucher then G.GAME.cry_bonusvouchersused[self.shop_cry_bonusvoucher] = true end
|
||||
|
||||
G.GAME.current_round.cry_voucher_edition = nil
|
||||
G.GAME.current_round.cry_voucher_stickers = {eternal = false, perishable = false, rental = false, pinned = false, banana = false}
|
||||
self:apply_to_run()
|
||||
|
||||
delay(0.6)
|
||||
SMODS.calculate_context({buying_card = true, card = self})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({buying_card = true, card = self})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'buying_card', card = self})
|
||||
if G.GAME.modifiers.inflation then
|
||||
G.GAME.inflation = G.GAME.inflation + 1
|
||||
G.E_MANAGER:add_event(Event({func = function()
|
||||
|
@ -2275,16 +2249,13 @@ function Card:redeem()
|
|||
end
|
||||
|
||||
function Card:apply_to_run(center)
|
||||
local card_to_save = self and copy_card(self) or Card(0, 0, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center)
|
||||
card_to_save.VT.x, card_to_save.VT.y = G.vouchers.T.x, G.vouchers.T.y
|
||||
G.vouchers:emplace(card_to_save)
|
||||
local center_table = {
|
||||
name = center and center.name or self and self.ability.name,
|
||||
extra = self and self.config and self.config.center_key and G.GAME and G.GAME.cry_voucher_centers and G.GAME.cry_voucher_centers[self.config.center_key] and G.GAME.cry_voucher_centers[self.config.center_key].config.extra
|
||||
extra = self and G.GAME.cry_voucher_centers[self.config.center_key].config.extra or center and center.config.extra
|
||||
}
|
||||
local obj = center or self.config.center
|
||||
if obj.redeem and type(obj.redeem) == 'function' then
|
||||
obj:redeem(card_to_save)
|
||||
obj:redeem(self)
|
||||
return
|
||||
end if center_table.name == 'Overstock' or center_table.name == 'Overstock Plus' then
|
||||
G.E_MANAGER:add_event(Event({func = function()
|
||||
|
@ -2649,10 +2620,7 @@ function Card:calculate_seal(context)
|
|||
if self.debuff then return nil end local obj = G.P_SEALS[self.seal] or {}
|
||||
if obj.calculate and type(obj.calculate) == 'function' then
|
||||
local o = obj:calculate(self, context)
|
||||
if o then
|
||||
if not o.card then o.card = self end
|
||||
return o
|
||||
end
|
||||
if o then return o end
|
||||
end
|
||||
if context.repetition then
|
||||
if self.seal == 'Red' then
|
||||
|
@ -2663,7 +2631,7 @@ function Card:calculate_seal(context)
|
|||
}
|
||||
end
|
||||
end
|
||||
if context.discard and context.other_card == self then
|
||||
if context.discard then
|
||||
if self.seal == 'Purple' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then
|
||||
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
|
||||
G.E_MANAGER:add_event(Event({
|
||||
|
@ -2677,7 +2645,6 @@ function Card:calculate_seal(context)
|
|||
return true
|
||||
end)}))
|
||||
card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_plus_tarot'), colour = G.C.PURPLE})
|
||||
return nil, true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -2704,13 +2671,31 @@ function Card:calculate_perishable()
|
|||
end
|
||||
|
||||
function Card:calculate_joker(context)
|
||||
for k, v in pairs(SMODS.Stickers) do
|
||||
if self.ability[v.key] then
|
||||
if v.calculate and type(v.calculate) == 'function' then
|
||||
local override_card = v:calculate(self, context)
|
||||
if override_card then return override_card end
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.debuff then return nil end
|
||||
local obj = self.config.center
|
||||
if self.ability.set ~= "Enhanced" and obj.calculate and type(obj.calculate) == 'function' then
|
||||
local o, t = obj:calculate(self, context)
|
||||
if o or t then return o, t end
|
||||
end
|
||||
if self.ability.set == "Planet" and not self.debuff then
|
||||
if context.joker_main then
|
||||
if G.GAME.used_vouchers.v_observatory and self.ability.consumeable.hand_type == context.scoring_name then
|
||||
return {
|
||||
message = localize{type = 'variable', key = 'a_xmult', vars = {G.GAME.cry_voucher_centers['v_observatory'].config.extra}},
|
||||
Xmult_mod = G.GAME.cry_voucher_centers['v_observatory'].config.extra
|
||||
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.ability.set == "Joker" and not self.debuff then
|
||||
if self.ability.name == "Blueprint" then
|
||||
local other_joker = nil
|
||||
|
@ -2719,14 +2704,15 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
if other_joker and other_joker ~= self and not context.no_blueprint then
|
||||
context.blueprint = (context.blueprint and (context.blueprint + 1)) or 1
|
||||
context.copy_depth = (context.copy_depth and (context.copy_depth + 1)) or 1
|
||||
context.blueprint_card = context.blueprint_card or self
|
||||
if context.blueprint > #G.jokers.cards + 1 then return end
|
||||
local other_joker_ret = other_joker:calculate_joker(context)
|
||||
context.blueprint = nil
|
||||
local eff_card = context.blueprint_card or self
|
||||
context.blueprint_card = nil
|
||||
context.no_callback = true
|
||||
local other_joker_ret, trig = other_joker:calculate_joker(context)
|
||||
if other_joker_ret then
|
||||
other_joker_ret.card = eff_card
|
||||
other_joker_ret.card = context.blueprint_card or self
|
||||
context.no_callback = not (context.copy_depth <= 1)
|
||||
context.copy_depth = context.copy_depth - 1;
|
||||
other_joker_ret.colour = G.C.BLUE
|
||||
return other_joker_ret
|
||||
end
|
||||
|
@ -2736,14 +2722,15 @@ function Card:calculate_joker(context)
|
|||
local other_joker = G.jokers.cards[1]
|
||||
if other_joker and other_joker ~= self and not context.no_blueprint then
|
||||
context.blueprint = (context.blueprint and (context.blueprint + 1)) or 1
|
||||
context.copy_depth = (context.copy_depth and (context.copy_depth + 1)) or 1
|
||||
context.blueprint_card = context.blueprint_card or self
|
||||
if context.blueprint > #G.jokers.cards + 1 then return end
|
||||
local other_joker_ret = other_joker:calculate_joker(context)
|
||||
context.blueprint = nil
|
||||
local eff_card = context.blueprint_card or self
|
||||
context.blueprint_card = nil
|
||||
context.no_callback = true
|
||||
local other_joker_ret, trig = other_joker:calculate_joker(context)
|
||||
if other_joker_ret then
|
||||
other_joker_ret.card = eff_card
|
||||
other_joker_ret.card = context.blueprint_card or self
|
||||
context.no_callback = not (context.copy_depth <= 1)
|
||||
context.copy_depth = context.copy_depth - 1;
|
||||
other_joker_ret.colour = G.C.RED
|
||||
return other_joker_ret
|
||||
end
|
||||
|
@ -2751,7 +2738,7 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
if context.open_booster then
|
||||
if self.ability.name == 'Hallucination' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then
|
||||
if pseudorandom('halu'..G.GAME.round_resets.ante) < G.GAME.probabilities.normal/self.ability.extra then
|
||||
if pseudorandom('halu'..G.GAME.round_resets.ante) < cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged)/self.ability.extra then
|
||||
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'before',
|
||||
|
@ -2764,7 +2751,6 @@ function Card:calculate_joker(context)
|
|||
return true
|
||||
end)}))
|
||||
card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_plus_tarot'), colour = G.C.PURPLE})
|
||||
return nil, true
|
||||
end
|
||||
end
|
||||
elseif context.buying_card then
|
||||
|
@ -3164,6 +3150,7 @@ function Card:calculate_joker(context)
|
|||
if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end
|
||||
return {
|
||||
message = localize('$')..self.ability.extra,
|
||||
dollars = self.ability.extra,
|
||||
colour = G.C.MONEY
|
||||
}
|
||||
end
|
||||
|
@ -3196,7 +3183,6 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
}))
|
||||
return {
|
||||
card = self,
|
||||
message = localize('k_eaten_ex'),
|
||||
colour = G.C.FILTER
|
||||
}
|
||||
|
@ -3204,7 +3190,6 @@ function Card:calculate_joker(context)
|
|||
self.ability.x_mult = self.ability.x_mult - self.ability.extra
|
||||
return {
|
||||
delay = 0.2,
|
||||
card = self,
|
||||
message = localize{type='variable',key='a_xmult_minus',vars={self.ability.extra}},
|
||||
colour = G.C.RED
|
||||
}
|
||||
|
@ -3215,7 +3200,6 @@ function Card:calculate_joker(context)
|
|||
self.ability.yorick_discards = self.ability.extra.discards
|
||||
self.ability.x_mult = self.ability.x_mult + self.ability.extra.xmult
|
||||
return {
|
||||
card = self,
|
||||
delay = 0.2,
|
||||
message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}},
|
||||
colour = G.C.RED
|
||||
|
@ -3346,7 +3330,6 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
}))
|
||||
return {
|
||||
card = self,
|
||||
message = localize('k_eaten_ex'),
|
||||
colour = G.C.FILTER
|
||||
}
|
||||
|
@ -3445,7 +3428,7 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
|
||||
if self.ability.name == 'Gros Michel' or self.ability.name == 'Cavendish' then
|
||||
if pseudorandom(self.ability.name == 'Cavendish' and 'cavendish' or 'gros_michel') < G.GAME.probabilities.normal/self.ability.extra.odds then
|
||||
if pseudorandom(self.ability.name == 'Cavendish' and 'cavendish' or 'gros_michel') < cry_prob(self.ability.cry_prob, self.ability.extra.odds, self.ability.cry_rigged)/self.ability.extra.odds then
|
||||
G.E_MANAGER:add_event(Event({
|
||||
func = function()
|
||||
play_sound('tarot1')
|
||||
|
@ -3532,7 +3515,7 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
end
|
||||
if self.ability.name == '8 Ball' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then
|
||||
if (context.other_card:get_id() == 8) and (pseudorandom('8ball') < G.GAME.probabilities.normal/self.ability.extra) then
|
||||
if (context.other_card:get_id() == 8) and (pseudorandom('8ball') < cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged)/self.ability.extra) then
|
||||
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
|
||||
return {
|
||||
extra = {focus = self, message = localize('k_plus_tarot'), func = function()
|
||||
|
@ -3602,7 +3585,7 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
if self.ability.name == 'Business Card' and
|
||||
context.other_card:is_face() and
|
||||
pseudorandom('business') < G.GAME.probabilities.normal/self.ability.extra then
|
||||
pseudorandom('business') < cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged)/self.ability.extra then
|
||||
G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + 2
|
||||
if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end
|
||||
return {
|
||||
|
@ -3674,7 +3657,7 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
if self.ability.name == 'Bloodstone' and
|
||||
context.other_card:is_suit("Hearts") and
|
||||
pseudorandom('bloodstone') < G.GAME.probabilities.normal/self.ability.extra.odds then
|
||||
pseudorandom('bloodstone') < cry_prob(self.ability.cry_prob, self.ability.extra.odds, self.ability.cry_rigged)/self.ability.extra.odds then
|
||||
return {
|
||||
x_mult = self.ability.extra.Xmult,
|
||||
card = self
|
||||
|
@ -3729,7 +3712,7 @@ function Card:calculate_joker(context)
|
|||
end
|
||||
if self.ability.name == 'Reserved Parking' and
|
||||
context.other_card:is_face() and
|
||||
pseudorandom('parking') < G.GAME.probabilities.normal/self.ability.extra.odds then
|
||||
pseudorandom('parking') < cry_prob(self.ability.cry_prob, self.ability.extra.odds, self.ability.cry_rigged)/self.ability.extra.odds then
|
||||
if context.other_card.debuff then
|
||||
return {
|
||||
message = localize('k_debuffed'),
|
||||
|
@ -3849,7 +3832,7 @@ function Card:calculate_joker(context)
|
|||
card = self
|
||||
}
|
||||
end
|
||||
if self.ability.name == 'Space Joker' and pseudorandom('space') < G.GAME.probabilities.normal/self.ability.extra then
|
||||
if self.ability.name == 'Space Joker' and pseudorandom('space') < cry_prob(self.ability.cry_prob, self.ability.extra, self.ability.cry_rigged)/self.ability.extra then
|
||||
return {
|
||||
card = self,
|
||||
level_up = true,
|
||||
|
@ -3926,6 +3909,7 @@ function Card:calculate_joker(context)
|
|||
if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end
|
||||
return {
|
||||
message = localize('$')..self.ability.extra.dollars,
|
||||
dollars = self.ability.extra.dollars,
|
||||
colour = G.C.MONEY
|
||||
}
|
||||
end
|
||||
|
@ -4154,6 +4138,7 @@ function Card:calculate_joker(context)
|
|||
if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end
|
||||
return {
|
||||
message = localize('$')..self.ability.extra,
|
||||
dollars = self.ability.extra,
|
||||
colour = G.C.MONEY
|
||||
}
|
||||
end
|
||||
|
@ -4171,7 +4156,7 @@ function Card:calculate_joker(context)
|
|||
}
|
||||
end
|
||||
if self.ability.name == 'Vagabond' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then
|
||||
if to_big(G.GAME.dollars) <= to_big(self.ability.extra) then
|
||||
if G.GAME.dollars <= self.ability.extra then
|
||||
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'before',
|
||||
|
@ -4363,7 +4348,7 @@ function Card:calculate_joker(context)
|
|||
colour = G.C.MULT
|
||||
}
|
||||
end
|
||||
if self.ability.name == 'Bull' and to_big(G.GAME.dollars + (G.GAME.dollar_buffer or 0)) > to_big(0) then
|
||||
if self.ability.name == 'Bull' and (G.GAME.dollars + (G.GAME.dollar_buffer or 0)) > 0 then
|
||||
return {
|
||||
message = localize{type='variable',key='a_chips',vars={self.ability.extra*math.max(0,(G.GAME.dollars + (G.GAME.dollar_buffer or 0))) }},
|
||||
chip_mod = self.ability.extra*math.max(0,(G.GAME.dollars + (G.GAME.dollar_buffer or 0))),
|
||||
|
@ -4473,7 +4458,7 @@ function Card:calculate_joker(context)
|
|||
Xmult_mod = self.ability.extra.Xmult,
|
||||
}
|
||||
end
|
||||
if self.ability.name == 'Bootstraps' and to_big(math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars)) >= to_big(1) then
|
||||
if self.ability.name == 'Bootstraps' and math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars) >= 1 then
|
||||
return {
|
||||
message = localize{type='variable',key='a_mult',vars={self.ability.extra.mult*math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars)}},
|
||||
mult_mod = self.ability.extra.mult*math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars)
|
||||
|
@ -4663,18 +4648,11 @@ function Card:update(dt)
|
|||
end
|
||||
end
|
||||
end
|
||||
if self.config.center.key == 'j_cry_gemino' then
|
||||
other_joker = G.jokers.cards[1]
|
||||
if other_joker and other_joker ~= self and not (Card.no(other_joker, "immutable", true)) then
|
||||
self.ability.blueprint_compat = 'compatible'
|
||||
else
|
||||
self.ability.blueprint_compat = 'incompatible'
|
||||
end end
|
||||
if self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' or self.ability.name == 'Brainstorm' then
|
||||
if self.ability.name == 'Blueprint' or self.ability.name == 'Brainstorm' then
|
||||
local other_joker = nil
|
||||
if self.ability.name == 'Brainstorm' then
|
||||
other_joker = G.jokers.cards[1]
|
||||
elseif self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' then
|
||||
elseif self.ability.name == 'Blueprint' then
|
||||
for i = 1, #G.jokers.cards do
|
||||
if G.jokers.cards[i] == self then other_joker = G.jokers.cards[i+1] end
|
||||
end
|
||||
|
@ -4795,6 +4773,7 @@ function Card:draw(layer)
|
|||
self.hover_tilt = 1
|
||||
|
||||
if not self.states.visible then return end
|
||||
if self.VT.x < -3 or self.VT.x > G.TILE_W + 2.5 then return end
|
||||
|
||||
if (layer == 'shadow' or layer == 'both') then
|
||||
self.ARGS.send_to_shader = self.ARGS.send_to_shader or {}
|
||||
|
@ -4891,9 +4870,6 @@ function Card:draw(layer)
|
|||
if (self.config.center.undiscovered and not self.config.center.undiscovered.no_overlay) or not( SMODS.UndiscoveredSprites[self.ability.set] and SMODS.UndiscoveredSprites[self.ability.set].no_overlay) then
|
||||
shared_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)
|
||||
else
|
||||
if SMODS.UndiscoveredSprites[self.ability.set] and SMODS.UndiscoveredSprites[self.ability.set].overlay_sprite then
|
||||
SMODS.UndiscoveredSprites[self.ability.set].overlay_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -5123,7 +5099,7 @@ function Card:draw(layer)
|
|||
end
|
||||
|
||||
for k, v in pairs(self.children) do
|
||||
if not v.custom_draw and k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end
|
||||
if not v.custom_draw and k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k ~= 'floating_sprite2' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end
|
||||
end
|
||||
|
||||
if (layer == 'card' or layer == 'both') and self.area == G.hand then
|
||||
|
@ -5209,6 +5185,8 @@ function Card:save()
|
|||
label = self.label,
|
||||
playing_card = self.playing_card,
|
||||
base = self.base,
|
||||
shop_voucher = self.shop_voucher,
|
||||
shop_cry_bonusvoucher = self.shop_cry_bonusvoucher,
|
||||
ability = self.ability,
|
||||
pinned = self.pinned,
|
||||
edition = self.edition,
|
||||
|
@ -5281,16 +5259,6 @@ function Card:load(cardTable, other_card)
|
|||
self.T.h = H*scale
|
||||
self.T.w = W*scale
|
||||
end
|
||||
if self.config.center.display_size and self.config.center.display_size.h then
|
||||
self.T.h = H*(self.config.center.display_size.h/95)
|
||||
elseif self.config.center.pixel_size and self.config.center.pixel_size.h then
|
||||
self.T.h = H*(self.config.center.pixel_size.h/95)
|
||||
end
|
||||
if self.config.center.display_size and self.config.center.display_size.w then
|
||||
self.T.w = W*(self.config.center.display_size.w/71)
|
||||
elseif self.config.center.pixel_size and self.config.center.pixel_size.w then
|
||||
self.T.w = W*(self.config.center.pixel_size.w/71)
|
||||
end
|
||||
self.VT.h = self.T.H
|
||||
self.VT.w = self.T.w
|
||||
|
||||
|
@ -5324,6 +5292,8 @@ function Card:load(cardTable, other_card)
|
|||
self.ignore_base_shader = cardTable.ignore_base_shader or {}
|
||||
self.ignore_shadow = cardTable.ignore_shadow or {}
|
||||
|
||||
self.shop_voucher = cardTable.shop_voucher
|
||||
self.shop_cry_bonusvoucher = cardTable.shop_cry_bonusvoucher
|
||||
self.ability = cardTable.ability
|
||||
self.pinned = cardTable.pinned
|
||||
self.edition = cardTable.edition
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = '0825b896143c8e8c22c2c18adfc937960d407b986f1c18addc68bedde1768967'
|
||||
LOVELY_INTEGRITY = 'e8693aca875516bf9cb3c84fcb6f392131135da095f221f2ef0e6ac727b926df'
|
||||
|
||||
--Class
|
||||
CardArea = Moveable:extend()
|
||||
|
@ -32,6 +32,9 @@ function CardArea:init(X, Y, W, H, config)
|
|||
end
|
||||
|
||||
function CardArea:emplace(card, location, stay_flipped)
|
||||
if self == G.jokers then
|
||||
Cartomancer.handle_joker_added(card)
|
||||
end
|
||||
if card.edition and card.edition.card_limit and (self == G.hand) then
|
||||
self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) + card.edition.card_limit
|
||||
self.config.card_limit = math.max(0, self.config.real_card_limit)
|
||||
|
@ -323,6 +326,9 @@ function CardArea:draw()
|
|||
}
|
||||
end
|
||||
self.children.area_uibox:draw()
|
||||
if self == G.jokers then
|
||||
Cartomancer.add_visibility_controls()
|
||||
end
|
||||
end
|
||||
|
||||
self:draw_boundingrect()
|
||||
|
@ -425,15 +431,39 @@ function CardArea:align_cards()
|
|||
end
|
||||
if (self == G.hand or self == G.deck or self == G.discard or self == G.play) and G.view_deck and G.view_deck[1] and G.view_deck[1].cards then return end
|
||||
if self.config.type == 'deck' then
|
||||
local display_limit
|
||||
if not Cartomancer.SETTINGS.compact_deck_enabled then
|
||||
display_limit = 999999
|
||||
else
|
||||
display_limit = Cartomancer.SETTINGS.compact_deck_visible_cards
|
||||
end
|
||||
|
||||
local deck_height = (self.config.deck_height or 0.15)/52
|
||||
local total_cards = #self.cards <= display_limit and #self.cards or display_limit -- limit height
|
||||
local fixedX, fixedY, fixedR = nil, nil, nil
|
||||
|
||||
for k, card in ipairs(self.cards) do
|
||||
if card.facing == 'front' then card:flip() end
|
||||
|
||||
|
||||
if not card.states.drag.is then
|
||||
card.T.x = self.T.x + 0.5*(self.T.w - card.T.w) + self.shadow_parrallax.x*deck_height*(#self.cards/(self == G.deck and 1 or 2) - k) + 0.9*self.shuffle_amt*(1 - k*0.01)*(k%2 == 1 and 1 or -0)
|
||||
card.T.y = self.T.y + 0.5*(self.T.h - card.T.h) + self.shadow_parrallax.y*deck_height*(#self.cards/(self == G.deck and 1 or 2) - k)
|
||||
card.T.r = 0 + 0.3*self.shuffle_amt*(1 + k*0.05)*(k%2 == 1 and 1 or -0)
|
||||
card.T.x = card.T.x + card.shadow_parrallax.x/30
|
||||
if fixedX then
|
||||
card.T.x = fixedX
|
||||
card.T.y = fixedY
|
||||
card.T.r = fixedR -- rotation
|
||||
card.states.visible = false
|
||||
else
|
||||
card.T.x = self.T.x + 0.5*(self.T.w - card.T.w) + self.shadow_parrallax.x*deck_height*(total_cards/(self == G.deck and 1 or 2) - k) + 0.9*self.shuffle_amt*(1 - k*0.01)*(k%2 == 1 and 1 or -0)
|
||||
card.T.y = self.T.y + 0.5*(self.T.h - card.T.h) + self.shadow_parrallax.y*deck_height*(total_cards/(self == G.deck and 1 or 2) - k)
|
||||
card.T.r = 0 + 0.3*self.shuffle_amt*(1 + k*0.05)*(k%2 == 1 and 1 or -0)
|
||||
card.T.x = card.T.x + card.shadow_parrallax.x/30
|
||||
card.states.visible = true
|
||||
|
||||
if k >= display_limit then
|
||||
fixedX = card.T.x
|
||||
fixedY = card.T.y
|
||||
fixedR = card.T.r
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -521,7 +551,18 @@ function CardArea:align_cards()
|
|||
end
|
||||
table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end)
|
||||
end
|
||||
if self.config.type == 'joker' or self.config.type == 'title_2' then
|
||||
if self == G.jokers and G.jokers.cart_jokers_expanded then
|
||||
local align_cards = Cartomancer.expand_G_jokers()
|
||||
|
||||
-- This should work fine without cryptid. But because cryptid's patch is priority=0, it has to be this way
|
||||
if not G.GAME.modifiers.cry_conveyor then
|
||||
table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end)
|
||||
end
|
||||
|
||||
if align_cards then
|
||||
G.jokers:hard_set_cards()
|
||||
end
|
||||
elseif self.config.type == 'joker' or self.config.type == 'title_2' then
|
||||
for k, card in ipairs(self.cards) do
|
||||
if not card.states.drag.is then
|
||||
card.T.r = 0.1*(-#self.cards/2 - 0.5 + k)/(#self.cards)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+card.T.x)
|
||||
|
@ -612,6 +653,9 @@ function CardArea:draw_card_from(area, stay_flipped, discarded_only)
|
|||
if area == G.discard then
|
||||
card.T.r = 0
|
||||
end
|
||||
if self == G.hand and not card.states.visible then
|
||||
card.states.visible = true
|
||||
end
|
||||
local stay_flipped = G.GAME and G.GAME.blind and G.GAME.blind:stay_flipped(self, card)
|
||||
if (self == G.hand) and G.GAME.modifiers.flipped_cards then
|
||||
if pseudorandom(pseudoseed('flipped_card')) < 1/G.GAME.modifiers.flipped_cards then
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = 'd9e5d51702216f37d8d0de3c89deb8d1a47bcad6430594945df872f263a14c04'
|
||||
LOVELY_INTEGRITY = 'd36e3953c2739cd4ea15794fb5e4031a2dac09fc33baadd0a2820e4726891304'
|
||||
|
||||
---@class Controller
|
||||
Controller = Object:extend()
|
||||
|
@ -768,6 +768,12 @@ function Controller:button_release_update(button, dt)
|
|||
end
|
||||
|
||||
function Controller:key_press_update(key, dt)
|
||||
if key == "escape" and Cartomancer.INTERNAL_in_config then
|
||||
Cartomancer.INTERNAL_in_config = false
|
||||
if not Cartomancer.use_smods() then
|
||||
Cartomancer.save_config()
|
||||
end
|
||||
end
|
||||
if key == "escape" and G.ACTIVE_MOD_UI then
|
||||
G.FUNCS.exit_mods()
|
||||
end
|
||||
|
@ -822,7 +828,7 @@ function Controller:key_press_update(key, dt)
|
|||
end
|
||||
end
|
||||
end
|
||||
if not _RELEASE_MODE then
|
||||
if not _RELEASE_MODE and require("debugplus.core").isOkayToHandleDebugForKey(key) then
|
||||
if key == 'tab' and not G.debug_tools then
|
||||
G.debug_tools = UIBox{
|
||||
definition = create_UIBox_debug_tools(),
|
||||
|
@ -853,6 +859,8 @@ function Controller:key_press_update(key, dt)
|
|||
add_joker(_card.config.center.key)
|
||||
_card:set_sprites(_card.config.center)
|
||||
end
|
||||
local debugplus = require("debugplus.core")
|
||||
debugplus.handleSpawn(self, _card)
|
||||
if _card.ability.consumeable and G.consumeables and #G.consumeables.cards < G.consumeables.config.card_limit then
|
||||
add_joker(_card.config.center.key)
|
||||
_card:set_sprites(_card.config.center)
|
||||
|
@ -904,13 +912,17 @@ function Controller:key_press_update(key, dt)
|
|||
if key == "space" then
|
||||
live_test()
|
||||
end
|
||||
local debugplus = require("debugplus.core")
|
||||
debugplus.handleKeys(self, key, dt)
|
||||
if key == 'v' then
|
||||
if not G.prof then G.prof = require "engine/profile"; G.prof.start()
|
||||
else G.prof:stop();
|
||||
print(G.prof.report()); G.prof = nil end
|
||||
debugplus.profileMessage()
|
||||
end
|
||||
if key == "p" then
|
||||
G.SETTINGS.perf_mode = not G.SETTINGS.perf_mode
|
||||
debugplus.togglePerfUI()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
LOVELY_INTEGRITY = 'a0a6222d417538f9ac51e6de9e5324e3e68a85bbdc35ead9c5c98bc11cb475e4'
|
||||
|
||||
require "love.system"
|
||||
|
||||
if (love.system.getOS() == 'OS X' ) and (jit.arch == 'arm64' or jit.arch == 'arm') then jit.off() end
|
||||
|
||||
require "love.timer"
|
||||
require "love.thread"
|
||||
require 'love.filesystem'
|
||||
require "engine/object"
|
||||
require "engine/string_packer"
|
||||
|
||||
--vars needed for sound manager thread
|
||||
CHANNEL = love.thread.getChannel("save_request")
|
||||
function tal_compress_and_save(_file, _data, talisman)
|
||||
local save_string = type(_data) == 'table' and STR_PACK(_data) or _data
|
||||
local fallback_save = STR_PACK({GAME = {won = true}}) --just bare minimum to not crash, maybe eventually display some info?
|
||||
if talisman == 'bignumber' then
|
||||
fallback_save = "if not BigMeta then " .. fallback_save
|
||||
elseif talisman == 'omeganum' then
|
||||
fallback_save = "if not OmegaMeta then " .. fallback_save
|
||||
else
|
||||
fallback_save = "if BigMeta or OmegaMeta then " .. fallback_save
|
||||
end
|
||||
fallback_save = fallback_save .. " end"
|
||||
save_string = fallback_save .. " " .. save_string
|
||||
save_string = love.data.compress('string', 'deflate', save_string, 1)
|
||||
love.filesystem.write(_file,save_string)
|
||||
end
|
||||
|
||||
while true do
|
||||
--Monitor the channel for any new requests
|
||||
local request = CHANNEL:demand() -- Value from channel
|
||||
if request then
|
||||
if request.type == 'kill' then return end
|
||||
--Saves progress for settings, unlocks, alerts and discoveries
|
||||
if request.type == 'save_progress' then
|
||||
local prefix_profile = (request.save_progress.SETTINGS.profile or 1)..''
|
||||
if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end
|
||||
prefix_profile = prefix_profile..'/'
|
||||
|
||||
if not love.filesystem.getInfo(prefix_profile..'meta.jkr') then
|
||||
love.filesystem.append( prefix_profile..'meta.jkr', 'return {}' )
|
||||
end
|
||||
|
||||
local meta = STR_UNPACK(get_compressed(prefix_profile..'meta.jkr') or 'return {}')
|
||||
meta.unlocked = meta.unlocked or {}
|
||||
meta.discovered = meta.discovered or {}
|
||||
meta.alerted = meta.alerted or {}
|
||||
|
||||
local _append = false
|
||||
|
||||
for k, v in pairs(request.save_progress.UDA) do
|
||||
if string.find(v, 'u') and not meta.unlocked[k] then
|
||||
meta.unlocked[k] = true
|
||||
_append = true
|
||||
end
|
||||
if string.find(v, 'd') and not meta.discovered[k] then
|
||||
meta.discovered[k] = true
|
||||
_append = true
|
||||
end
|
||||
if string.find(v, 'a') and not meta.alerted[k] then
|
||||
meta.alerted[k] = true
|
||||
_append = true
|
||||
end
|
||||
end
|
||||
if _append then compress_and_save( prefix_profile..'meta.jkr', STR_PACK(meta)) end
|
||||
|
||||
compress_and_save('settings.jkr', request.save_progress.SETTINGS)
|
||||
compress_and_save(prefix_profile..'profile.jkr', request.save_progress.PROFILE)
|
||||
|
||||
CHANNEL:push('done')
|
||||
--Saves the settings file
|
||||
elseif request.type == 'save_settings' then
|
||||
compress_and_save('settings.jkr', request.save_settings)
|
||||
compress_and_save(request.profile_num..'/profile.jkr', request.save_profile)
|
||||
--Saves the metrics file
|
||||
elseif request.type == 'save_metrics' then
|
||||
compress_and_save('metrics.jkr', request.save_metrics)
|
||||
--Saves any notifications
|
||||
elseif request.type == 'save_notify' then
|
||||
local prefix_profile = (request.profile_num or 1)..''
|
||||
if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end
|
||||
prefix_profile = prefix_profile..'/'
|
||||
|
||||
if not love.filesystem.getInfo(prefix_profile..'unlock_notify.jkr') then love.filesystem.append( prefix_profile..'unlock_notify.jkr', '') end
|
||||
local unlock_notify = get_compressed(prefix_profile..'unlock_notify.jkr') or ''
|
||||
|
||||
if request.save_notify and not string.find(unlock_notify, request.save_notify) then
|
||||
compress_and_save( prefix_profile..'unlock_notify.jkr', unlock_notify..request.save_notify..'\n')
|
||||
end
|
||||
|
||||
--Saves the run
|
||||
elseif request.type == 'save_run' then
|
||||
local prefix_profile = (request.profile_num or 1)..''
|
||||
if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end
|
||||
prefix_profile = prefix_profile..'/'
|
||||
|
||||
tal_compress_and_save(prefix_profile..'save.jkr', request.save_table, request.talisman)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = 'ed73ce7b68b17bbbc04af6cf80e1577d8c38357ce87c6952cf5f2319cb9a578d'
|
||||
LOVELY_INTEGRITY = '435a9e8ef1b3d548ef4488606196332b7d61c0a33e2dd2225ca6c7f97e3ec32b'
|
||||
|
||||
--Class
|
||||
UIBox = Moveable:extend()
|
||||
|
@ -990,6 +990,7 @@ function UIElement:click()
|
|||
G.NO_MOD_CURSOR_STACK = nil
|
||||
|
||||
if self.config.choice then
|
||||
local chosen_temp = self.config.chosen
|
||||
local chosen_temp = self.config.chosen
|
||||
local choices = self.UIBox:get_group(nil, self.config.group)
|
||||
for k, v in pairs(choices) do
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
LOVELY_INTEGRITY = 'c4606c194e720d427e9e0e1b2181bd1cd60d72f540c2377514fa195a2271958b'
|
||||
LOVELY_INTEGRITY = '824fe1307b104cf5bc0a6245ef8c8e76db9af62d790ab2106537987e589dd59b'
|
||||
|
||||
--Create a global UIDEF that contains all UI definition functions\
|
||||
--As a rule, these contain functions that return a table T representing the definition for a UIBox
|
||||
G.UIDEF = {}
|
||||
|
||||
function create_UIBox_debug_tools()
|
||||
local debugplus = require("debugplus.core")
|
||||
debugplus.registerButtons()
|
||||
G.debug_tool_config = G.debug_tool_config or {}
|
||||
G.FUNCS.DT_add_money = function() if G.STAGE == G.STAGES.RUN then ease_dollars(10) end end
|
||||
G.FUNCS.DT_add_round = function() if G.STAGE == G.STAGES.RUN then ease_round(1) end end
|
||||
|
@ -94,7 +96,40 @@ function create_UIBox_debug_tools()
|
|||
{n=G.UIT.T, config={text = "Hover over any Joker/Playing card", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "and press [Q] to cycle Edition", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
{n=G.UIT.T, config={text = "hold [" .. require("debugplus.util").ctrlText .. "] (togglable in config)", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "and press [Q] to cycle Edition", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [W] to cycle Enhancement", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [E] to cycle Seal", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [A/S/D] to toggle Eternal/Perishable/Rental", scale = 0.2, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [F] to toggle Coupon (make free)", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [R/C] to destroy/copy card", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [M] to reload atlases", scale = 0.25, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [UP/DOWN] to cycle rank", scale = 0.2, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.00}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [RIGHT/LEFT] to cycle suit", scale = 0.2, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [z] plus [1-3] to save a save state", scale = 0.2, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.00}, nodes={
|
||||
{n=G.UIT.T, config={text = "press [x] plus [1-3] to load a save state", scale = 0.2, colour = G.C.WHITE, shadow = true}}
|
||||
}},
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={
|
||||
|
@ -126,6 +161,8 @@ function create_UIBox_debug_tools()
|
|||
UIBox_button{ label = {"+1 Discard"}, button = "DT_add_discard", minw = 1.7, minh = 0.4, scale = 0.35},
|
||||
UIBox_button{ label = {"Boss Reroll"}, button = "DT_reroll_boss", minw = 1.7, minh = 0.4, scale = 0.35},
|
||||
UIBox_button{ label = {"Background"}, button = "DT_toggle_background", minw = 1.7, minh = 0.4, scale = 0.35},
|
||||
UIBox_button{ label = {"Win Blind"}, button = "DT_win_blind", minw = 1.7, minh = 0.4, scale = 0.35},
|
||||
UIBox_button{ label = {"Double Tag"}, button = "DT_double_tag", minw = 1.7, minh = 0.4, scale = 0.35},
|
||||
}},
|
||||
{n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes={
|
||||
UIBox_button{ label = {"+10 chips"}, button = "DT_add_chips", minw = 1.7, minh = 0.4, scale = 0.35},
|
||||
|
@ -262,15 +299,6 @@ function G.UIDEF.use_and_sell_buttons(card)
|
|||
}},
|
||||
}}
|
||||
end
|
||||
if card.ability.consumeable and booster_obj and booster_obj.select_card then
|
||||
if (card.area == G.pack_cards and G.pack_cards) then
|
||||
return {n=G.UIT.ROOT, config = {padding = 0, colour = G.C.CLEAR}, nodes={
|
||||
{n=G.UIT.R, config={ref_table = card, r = 0.08, padding = 0.1, align = "bm", minw = 0.5*card.T.w - 0.15, maxw = 0.9*card.T.w - 0.15, minh = 0.3*card.T.h, hover = true, shadow = true, colour = G.C.UI.BACKGROUND_INACTIVE, one_press = true, button = 'use_card', func = 'can_select_from_booster'}, nodes={
|
||||
{n=G.UIT.T, config={text = localize('b_select'),colour = G.C.UI.TEXT_LIGHT, scale = 0.45, shadow = true}}
|
||||
}},
|
||||
}}
|
||||
end
|
||||
end
|
||||
if card.ability.consumeable then
|
||||
if (card.area == G.pack_cards and G.pack_cards) then
|
||||
return {
|
||||
|
@ -519,6 +547,7 @@ function G.UIDEF.deck_preview(args)
|
|||
end
|
||||
|
||||
local suit_map = {'Spades', 'Hearts', 'Clubs', 'Diamonds'}
|
||||
local SUITS_SORTED = Cartomancer.tablecopy(SUITS)
|
||||
local stones = nil
|
||||
local rank_name_mapping = {'A','K','Q','J','10',9,8,7,6,5,4,3,2}
|
||||
|
||||
|
@ -1010,6 +1039,7 @@ end
|
|||
end
|
||||
|
||||
function create_UIBox_buttons()
|
||||
if G.hand and G.hand.cart_sorting == nil then G.hand.cart_sorting = true end
|
||||
local text_scale = 0.45
|
||||
local button_height = 1.3
|
||||
local play_button = {n=G.UIT.C, config={id = 'play_button', align = "tm", minw = 2.5, padding = 0.3, r = 0.1, hover = true, colour = G.C.BLUE, button = "play_cards_from_highlighted", one_press = true, shadow = true, func = 'can_play'}, nodes={
|
||||
|
@ -1031,6 +1061,9 @@ end
|
|||
{n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour =G.C.UI.TRANSPARENT_DARK, outline = 1.5, outline_colour = mix_colours(G.C.WHITE,G.C.JOKER_GREY, 0.7), line_emboss = 1}, nodes={
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0}, nodes={
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0}, nodes={
|
||||
Cartomancer.SETTINGS.improved_hand_sorting and
|
||||
create_toggle{ col = true, label = localize('b_sort_hand'), label_scale = text_scale*0.8, scale = 0.30, w = 0, shadow = true, ref_table = G.hand, ref_value = 'cart_sorting', callback = function () G.FUNCS.cartomancer_sort_hand_off() end }
|
||||
or
|
||||
{n=G.UIT.T, config={text = localize('b_sort_hand'), scale = text_scale*0.8, colour = G.C.UI.TEXT_LIGHT}}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={
|
||||
|
@ -1985,11 +2018,11 @@ function create_slider(args)
|
|||
local t =
|
||||
{n=G.UIT.C, config={align = "cm", minw = args.w, min_h = args.h, padding = 0.1, r = 0.1, colour = G.C.CLEAR, focus_args = {type = 'slider'}}, nodes={
|
||||
{n=G.UIT.C, config={align = "cl", minw = args.w, r = 0.1,min_h = args.h,collideable = true, hover = true, colour = G.C.BLACK,emboss = 0.05,func = 'slider', refresh_movement = true}, nodes={
|
||||
{n=G.UIT.B, config={w=startval,h=args.h, r = 0.1, colour = args.colour, ref_table = args, refresh_movement = true}},
|
||||
{n=G.UIT.B, config={id = args.id, w=startval,h=args.h, r = 0.1, colour = args.colour, ref_table = args, refresh_movement = true}},
|
||||
}},
|
||||
{n=G.UIT.C, config={align = "cm", minh = args.h,r = 0.1, minw = 0.8, colour = args.colour,shadow = true}, nodes={
|
||||
not args.hide_val and {n=G.UIT.C, config={align = "cm", minh = args.h,r = 0.1, minw = 0.8, colour = args.colour,shadow = true}, nodes={
|
||||
{n=G.UIT.T, config={ref_table = args, ref_value = 'text', scale = args.text_scale, colour = G.C.UI.TEXT_LIGHT, decimal_places = args.decimal_places}}
|
||||
}},
|
||||
}} or nil
|
||||
}}
|
||||
if args.label then
|
||||
t = {n=G.UIT.R, config={align = "cm", minh = 1, minw = 1, padding = 0.1*args.label_scale, colour = G.C.CLEAR}, nodes={
|
||||
|
@ -2395,6 +2428,21 @@ function create_UIBox_settings()
|
|||
tab_definition_function_args = 'Audio'
|
||||
}
|
||||
|
||||
if not require("debugplus.config").SMODSLoaded then
|
||||
tabs[#tabs+1] = {
|
||||
label = "DebugPlus",
|
||||
tab_definition_function = require("debugplus.config").fakeConfigTab,
|
||||
}
|
||||
end
|
||||
local settings_icon = Cartomancer.add_settings_icon()
|
||||
if settings_icon then
|
||||
tabs[#tabs+1] = {
|
||||
colour = G.C.MONEY,
|
||||
custom_button = {settings_icon},
|
||||
tab_definition_function = Cartomancer.config_tab,
|
||||
tab_definition_function_args = ''
|
||||
}
|
||||
end
|
||||
local t = create_UIBox_generic_options({back_func = 'options',contents = {create_tabs(
|
||||
{tabs = tabs,
|
||||
tab_h = 7.05,
|
||||
|
@ -2654,46 +2702,27 @@ function G.UIDEF.custom_deck_tab(_suit)
|
|||
|
||||
local rankCount = 0
|
||||
local lookup = {}
|
||||
for i, s in ipairs(SMODS.Suit:obj_list(true)) do
|
||||
local options = G.COLLABS.options[s.key]
|
||||
for i = 1, #options do
|
||||
local skin = SMODS.DeckSkins[options[i]]
|
||||
if skin.palettes and not (skin.display_ranks or skin.ranks) then
|
||||
for _, p in ipairs(skin.palettes) do
|
||||
local p_ranks = p.display_ranks or p.ranks
|
||||
for j = 1, #p_ranks do
|
||||
if not lookup[p_ranks[j]] then
|
||||
lookup[p_ranks[j]] = true
|
||||
rankCount = rankCount + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif not skin.palettes and (skin.display_ranks or skin.ranks) then
|
||||
local ranks = skin.display_ranks or skin.ranks
|
||||
for j = 1, #ranks do
|
||||
if not lookup[skin.ranks[j]] then
|
||||
lookup[skin.ranks[j]] = true
|
||||
rankCount = rankCount + 1
|
||||
end
|
||||
end
|
||||
local options = G.COLLABS.options[_suit]
|
||||
for i = 2, #options do
|
||||
local skin = SMODS.DeckSkins[options[i]]
|
||||
for j = 1, #skin.ranks do
|
||||
if not lookup[skin.ranks[j]] then
|
||||
lookup[skin.ranks[j]] = true
|
||||
rankCount = rankCount + 1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
G.cdds_cards = CardArea(
|
||||
local face_cards = CardArea(
|
||||
0,0,
|
||||
math.min(math.max(rankCount*G.CARD_W*0.6, 4*G.CARD_W), 10*G.CARD_W),
|
||||
1.4*G.CARD_H,
|
||||
1.4*G.CARD_H,
|
||||
{card_limit = rankCount, type = 'title', highlight_limit = 0})
|
||||
|
||||
G.cdds_cards.rankCount = rankCount
|
||||
|
||||
|
||||
|
||||
table.insert(t,
|
||||
{n=G.UIT.R, config={align = "cm", colour = G.C.BLACK, r = 0.1, padding = 0.07, no_fill = true}, nodes={
|
||||
{n=G.UIT.O, config={object = G.cdds_cards}}
|
||||
{n=G.UIT.O, config={object = face_cards}}
|
||||
}}
|
||||
)
|
||||
|
||||
|
@ -2715,25 +2744,26 @@ G.cdds_cards.rankCount = rankCount
|
|||
create_option_cycle({options = loc_options, w = 5.5, cycle_shoulders = true, curr_suit = _suit, opt_callback = 'change_collab', current_option = current_option, colour = G.C.RED, focus_args = {snap_to = true, nav = 'wide'}}),
|
||||
}}
|
||||
)
|
||||
local deckskin_key = G.COLLABS.options[_suit][current_option]
|
||||
|
||||
local palette_loc_options = SMODS.DeckSkin.get_palette_loc_options(deckskin_key, _suit)
|
||||
|
||||
local selected_palette = 1
|
||||
for i, v in ipairs(G.COLLABS.colour_palettes[deckskin_key]) do
|
||||
if G.SETTINGS.colour_palettes[_suit] == v then
|
||||
selected_palette = i
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(t,
|
||||
{n=G.UIT.R, config={align = "cm", id = 'palette_selector'}, nodes={
|
||||
create_option_cycle({options = palette_loc_options, w = 5.5, cycle_shoulders = false, curr_suit = _suit, curr_skin = deckskin_key, opt_callback = 'change_colour_palette', current_option = selected_palette, colour = G.C.ORANGE, focus_args = {snap_to = true, nav = 'wide'}}),
|
||||
}}
|
||||
)
|
||||
table.insert(t, create_toggle({label = localize('b_high_contrast_cards'), ref_table = G.SETTINGS, ref_value = 'colourblind_option', callback = G.FUNCS.refresh_contrast_mode}))
|
||||
|
||||
local faces = {'K','Q','J'}
|
||||
G.FUNCS.update_collab_cards(current_option, _suit, true)
|
||||
local rank = SMODS.Ranks['2']
|
||||
local cards = {}
|
||||
local smodSuit = SMODS.Suits[_suit]
|
||||
repeat
|
||||
if lookup[rank.key] then
|
||||
local card_code = smodSuit.card_key .. '_' .. rank.card_key
|
||||
local card = Card(0,0, G.CARD_W*1.2, G.CARD_H*1.2, G.P_CARDS[card_code], G.P_CENTERS.c_base)
|
||||
card.no_ui = true
|
||||
|
||||
cards[#cards + 1] = card
|
||||
end
|
||||
rank = SMODS.Ranks[rank.next[1]]
|
||||
until rank == SMODS.Ranks['2']
|
||||
|
||||
for i = #cards, 1, -1 do
|
||||
face_cards:emplace(cards[i])
|
||||
end
|
||||
|
||||
|
||||
return {n=G.UIT.ROOT, config={align = "cm", padding = 0, colour = G.C.CLEAR, r = 0.1, minw = 7, minh = 4.2}, nodes=t}
|
||||
|
@ -3195,8 +3225,8 @@ function create_UIBox_current_hand_row(handname, simple)
|
|||
(not simple and
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = darken(G.C.JOKER_GREY, 0.1), emboss = 0.05, hover = true, force_focus = true, on_demand_tooltip = {text = localize(handname, 'poker_hand_descriptions'), filler = {func = create_UIBox_hand_tip, args = handname}}}, nodes={
|
||||
{n=G.UIT.C, config={align = "cl", padding = 0, minw = 5}, nodes={
|
||||
{n=G.UIT.C, config={align = "cm", padding = 0.01, r = 0.1, colour = G.C.HAND_LEVELS[to_big(math.min(7, G.GAME.hands[handname].level)):to_number()], minw = 1.5, outline = 0.8, outline_colour = G.C.WHITE}, nodes={
|
||||
{n=G.UIT.T, config={text = localize('k_level_prefix')..number_format(G.GAME.hands[handname].level), scale = 0.5, colour = G.C.UI.TEXT_DARK}}
|
||||
{n=G.UIT.C, config={align = "cm", padding = 0.01, r = 0.1, colour = G.C.HAND_LEVELS[math.min(7, G.GAME.hands[handname].level)], minw = 1.5, outline = 0.8, outline_colour = G.C.WHITE}, nodes={
|
||||
{n=G.UIT.T, config={text = localize('k_level_prefix')..G.GAME.hands[handname].level, scale = 0.5, colour = G.C.UI.TEXT_DARK}}
|
||||
}},
|
||||
{n=G.UIT.C, config={align = "cm", minw = 4.5, maxw = 4.5}, nodes={
|
||||
{n=G.UIT.T, config={text = ' '..localize(handname,'poker_hands'), scale = 0.45, colour = G.C.UI.TEXT_LIGHT, shadow = true}}
|
||||
|
@ -3395,37 +3425,59 @@ function G.UIDEF.view_deck(unplayed_only)
|
|||
Diamonds = {},
|
||||
}
|
||||
local suit_map = {'Spades', 'Hearts', 'Clubs', 'Diamonds'}
|
||||
local SUITS_SORTED = Cartomancer.tablecopy(SUITS)
|
||||
for k, v in ipairs(G.playing_cards) do
|
||||
table.insert(SUITS[v.base.suit], v)
|
||||
local greyed
|
||||
if unplayed_only and not ((v.area and v.area == G.deck) or v.ability.wheel_flipped) then
|
||||
greyed = true
|
||||
end
|
||||
local card_string = v:cart_to_string()
|
||||
if greyed then
|
||||
card_string = card_string .. "Greyed"
|
||||
end
|
||||
if greyed and Cartomancer.SETTINGS.deck_view_hide_drawn_cards then
|
||||
-- Ignore this card.
|
||||
elseif not SUITS[v.base.suit][card_string] then
|
||||
table.insert(SUITS_SORTED[v.base.suit], card_string)
|
||||
|
||||
local _scale = 0.7
|
||||
local copy = copy_card(v, nil, _scale)
|
||||
|
||||
copy.greyed = greyed
|
||||
copy.stacked_quantity = 1
|
||||
|
||||
SUITS[v.base.suit][card_string] = copy
|
||||
else
|
||||
local stacked_card = SUITS[v.base.suit][card_string]
|
||||
stacked_card.stacked_quantity = stacked_card.stacked_quantity + 1
|
||||
end
|
||||
end
|
||||
for j = 1, 4 do
|
||||
if SUITS[suit_map[j]][1] then
|
||||
if SUITS_SORTED[suit_map[j]][1] then
|
||||
local view_deck = CardArea(
|
||||
G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h,
|
||||
6.5*G.CARD_W,
|
||||
0.6*G.CARD_H,
|
||||
{card_limit = #SUITS[suit_map[j]], type = 'title', view_deck = true, highlight_limit = 0, card_w = G.CARD_W*0.7, draw_layers = {'card'}})
|
||||
{card_limit = #SUITS_SORTED[suit_map[j]], type = 'title', view_deck = true, highlight_limit = 0, card_w = G.CARD_W*0.7, draw_layers = {'card'}})
|
||||
table.insert(deck_tables,
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0}, nodes={
|
||||
{n=G.UIT.O, config={object = view_deck}}
|
||||
}}
|
||||
)
|
||||
|
||||
for i = 1, #SUITS[suit_map[j]] do
|
||||
if SUITS[suit_map[j]][i] then
|
||||
local greyed, _scale = nil, 0.7
|
||||
if unplayed_only and not ((SUITS[suit_map[j]][i].area and SUITS[suit_map[j]][i].area == G.deck) or SUITS[suit_map[j]][i].ability.wheel_flipped) then
|
||||
greyed = true
|
||||
end
|
||||
local copy = copy_card(SUITS[suit_map[j]][i],nil, _scale)
|
||||
copy.greyed = greyed
|
||||
copy.T.x = view_deck.T.x + view_deck.T.w/2
|
||||
copy.T.y = view_deck.T.y
|
||||
|
||||
copy:hard_set_T()
|
||||
view_deck:emplace(copy)
|
||||
end
|
||||
for i = 1, #SUITS_SORTED[suit_map[j]] do
|
||||
local card_string = SUITS_SORTED[suit_map[j]][i]
|
||||
local card = SUITS[suit_map[j]][card_string]
|
||||
|
||||
card.T.x = view_deck.T.x + view_deck.T.w/2
|
||||
card.T.y = view_deck.T.y
|
||||
card:create_quantity_display()
|
||||
|
||||
card:hard_set_T()
|
||||
view_deck:emplace(card)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -3539,6 +3591,7 @@ function G.UIDEF.view_deck(unplayed_only)
|
|||
{n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}
|
||||
}},
|
||||
{n=G.UIT.R, config={align = "cm", minh = 0.8, padding = 0.05}, nodes={
|
||||
not unplayed_only and Cartomancer.add_unique_count() or nil,
|
||||
modded and {n=G.UIT.R, config={align = "cm"}, nodes={
|
||||
{n=G.UIT.C, config={padding = 0.3, r = 0.1, colour = mix_colours(G.C.BLUE, G.C.WHITE,0.7)}, nodes = {}},
|
||||
{n=G.UIT.T, config={text =' '..localize('ph_deck_preview_effective'),colour = G.C.WHITE, scale =0.3}},
|
||||
|
@ -3620,6 +3673,7 @@ function G.UIDEF.used_vouchers()
|
|||
card.ability.order = vv.order
|
||||
card:start_materialize(nil, silent)
|
||||
silent = true
|
||||
if not G.GAME.voucher_edition_index then G.GAME.voucher_edition_index = {} end
|
||||
if G.GAME.voucher_edition_index[card.ability.name] then -- i just made it a function so i can look at it less
|
||||
local edition = cry_edition_to_table(G.GAME.voucher_edition_index[card.ability.name])
|
||||
if edition then
|
||||
|
@ -4257,9 +4311,20 @@ function create_UIBox_your_collection_blinds(exit)
|
|||
end)
|
||||
}))
|
||||
|
||||
local min_ante = 1
|
||||
local max_ante = 16
|
||||
local spacing = 1 - 15*0.06
|
||||
if G.GAME and G.GAME.round_resets and G.GAME.round_resets.ante then
|
||||
local current_ante = G.GAME.round_resets.ante
|
||||
|
||||
if current_ante > 8 then
|
||||
min_ante = current_ante - 8 + 1
|
||||
max_ante = current_ante + 8
|
||||
end
|
||||
end
|
||||
local ante_amounts = {}
|
||||
for i = 1, math.min(16, math.max(16, G.PROFILES[G.SETTINGS.profile].high_scores.furthest_ante.amt)) do
|
||||
local spacing = 1 - math.min(20, math.max(15, G.PROFILES[G.SETTINGS.profile].high_scores.furthest_ante.amt))*0.06
|
||||
for i = min_ante, max_ante do
|
||||
-- :3
|
||||
if spacing > 0 and i > 1 then
|
||||
ante_amounts[#ante_amounts+1] = {n=G.UIT.R, config={minh = spacing}, nodes={}}
|
||||
end
|
||||
|
@ -6364,6 +6429,13 @@ function UIBox_button(args)
|
|||
local but_UI_label = {}
|
||||
|
||||
local button_pip = nil
|
||||
if args.dynamic_label then
|
||||
but_UI_label = {}
|
||||
|
||||
table.insert(but_UI_label, {n=G.UIT.R, config={align = "cm", padding = 0, minw = args.minw, maxw = args.maxw}, nodes={
|
||||
{n=G.UIT.T, config={ref_table = args.dynamic_label, ref_value = 'text', scale = args.scale, colour = args.text_colour, shadow = args.shadow, focus_args = button_pip and args.focus_args or nil, func = button_pip,}}
|
||||
}})
|
||||
end
|
||||
for k, v in ipairs(args.label) do
|
||||
if k == #args.label and args.focus_args and args.focus_args.set_button_pip then
|
||||
button_pip ='set_button_pip'
|
||||
|
@ -6388,7 +6460,7 @@ function UIBox_button(args)
|
|||
padding = args.padding or 0,
|
||||
r = 0.1,
|
||||
hover = true,
|
||||
colour = args.colour,
|
||||
colour = args.ref_table and args.ref_table.colour or args.colour, -- Cartomancer
|
||||
one_press = args.one_press,
|
||||
button = (args.button ~= 'nil') and args.button or nil,
|
||||
choice = args.choice,
|
||||
|
@ -6402,6 +6474,6 @@ function UIBox_button(args)
|
|||
ref_table = args.ref_table,
|
||||
mid = args.mid
|
||||
}, nodes=
|
||||
but_UI_label
|
||||
args.ref_table and args.ref_table.custom_button or but_UI_label -- Cartomancer
|
||||
}}}
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = 'e36471bd79b456102440b1c69061665aed2f0a92e67cfd840d15eafe4e55be17'
|
||||
LOVELY_INTEGRITY = '4a318253e70cba949d05bfb650e28bf79699539c4f0e57a07f3ad3d649a8688b'
|
||||
|
||||
--Moves the tutorial to the next step in queue
|
||||
--
|
||||
|
@ -55,7 +55,7 @@ end
|
|||
---@param e {}
|
||||
--**e** Is the UIE that called this function
|
||||
G.FUNCS.can_buy = function(e)
|
||||
if (to_big(e.config.ref_table.cost) > to_big(G.GAME.dollars) - to_big(G.GAME.bankrupt_at)) and (e.config.ref_table.cost > 0) then
|
||||
if (e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at) and (e.config.ref_table.cost > 0) then
|
||||
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
|
||||
e.config.button = nil
|
||||
else
|
||||
|
@ -77,7 +77,7 @@ end
|
|||
---@param e {}
|
||||
--**e** Is the UIE that called this function
|
||||
G.FUNCS.can_buy_and_use = function(e)
|
||||
if (((to_big(e.config.ref_table.cost) > to_big(G.GAME.dollars) - to_big(G.GAME.bankrupt_at)) and (e.config.ref_table.cost > 0)) or (not e.config.ref_table:can_use_consumeable())) then
|
||||
if (((e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at) and (e.config.ref_table.cost > 0)) or (not e.config.ref_table:can_use_consumeable())) then
|
||||
e.UIBox.states.visible = false
|
||||
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
|
||||
e.config.button = nil
|
||||
|
@ -96,7 +96,7 @@ end
|
|||
---@param e {}
|
||||
--**e** Is the UIE that called this function
|
||||
G.FUNCS.can_redeem = function(e)
|
||||
if to_big(e.config.ref_table.cost) > to_big(G.GAME.dollars) - to_big(G.GAME.bankrupt_at) then
|
||||
if e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at then
|
||||
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
|
||||
e.config.button = nil
|
||||
else
|
||||
|
@ -111,7 +111,7 @@ end
|
|||
---@param e {}
|
||||
--**e** Is the UIE that called this function
|
||||
G.FUNCS.can_open = function(e)
|
||||
if (e.config.ref_table.cost) > 0 and (to_big(e.config.ref_table.cost) > to_big(G.GAME.dollars) - to_big(G.GAME.bankrupt_at)) then
|
||||
if (e.config.ref_table.cost) > 0 and (e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at) then
|
||||
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
|
||||
e.config.button = nil
|
||||
else
|
||||
|
@ -2020,12 +2020,12 @@ G.FUNCS.flame_handler = function(e)
|
|||
local exptime = math.exp(-0.4*G.real_dt)
|
||||
|
||||
if to_big(G.ARGS.score_intensity.earned_score) >= to_big(G.ARGS.score_intensity.required_score) and to_big(G.ARGS.score_intensity.required_score) > to_big(0) then
|
||||
_F.intensity = ((G.pack_cards and not G.pack_cards.REMOVED) or (G.TAROT_INTERRUPT)) and 0 or math.max(0., math.log(G.ARGS.score_intensity.earned_score, 5)-2)
|
||||
_F.intensity = ((G.pack_cards and not G.pack_cards.REMOVED) or (G.TAROT_INTERRUPT)) and 0 or Cartomancer.get_flames_intensity()
|
||||
else
|
||||
_F.intensity = 0
|
||||
end
|
||||
|
||||
_F.timer = _F.timer + G.real_dt*(1 + _F.intensity*0.2)
|
||||
_F.timer = Cartomancer.handle_flames_timer(_F.timer, _F.intensity)
|
||||
if _F.intensity_vel < 0 then _F.intensity_vel = _F.intensity_vel*(1 - 10*G.real_dt) end
|
||||
_F.intensity_vel = (1-exptime)*(_F.intensity - _F.real_intensity)*G.real_dt*25 + exptime*_F.intensity_vel
|
||||
_F.real_intensity = math.max(0, _F.real_intensity + _F.intensity_vel)
|
||||
|
@ -2078,7 +2078,7 @@ end
|
|||
end
|
||||
|
||||
G.FUNCS.can_reroll = function(e)
|
||||
if ((to_big(G.GAME.dollars)-to_big(G.GAME.bankrupt_at)) - to_big(G.GAME.current_round.reroll_cost) < to_big(0)) and G.GAME.current_round.reroll_cost ~= 0 then
|
||||
if ((G.GAME.dollars-G.GAME.bankrupt_at) - G.GAME.current_round.reroll_cost < 0) and G.GAME.current_round.reroll_cost ~= 0 then
|
||||
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
|
||||
e.config.button = nil
|
||||
--e.children[1].children[1].config.shadow = false
|
||||
|
@ -2220,29 +2220,30 @@ end
|
|||
nc = obj:keep_on_use(card)
|
||||
end
|
||||
end
|
||||
if card.area and (not nc or card.area == G.pack_cards) then card.area:remove_card(card) end
|
||||
if not nc and card.area then card.area:remove_card(card) end
|
||||
|
||||
if booster_obj and booster_obj.select_card then
|
||||
local area = type(booster_obj.select_card) == 'table' and (booster_obj.select_card[e.config.ref_table.ability.set] or nil) or booster_obj.select_card
|
||||
G[area]:emplace(card)
|
||||
play_sound('card1', 0.8, 0.6)
|
||||
play_sound('generic1')
|
||||
dont_dissolve = true
|
||||
delay_fac = 0.2
|
||||
elseif card.ability.consumeable then
|
||||
if nc then
|
||||
if area then area:remove_from_highlighted(card) end
|
||||
play_sound('cardSlide2', nil, 0.3)
|
||||
dont_dissolve = true
|
||||
end
|
||||
if (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then
|
||||
if card.ability.consumeable then
|
||||
if G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED then
|
||||
card.T.x = G.hand.T.x + G.hand.T.w/2 - card.T.w/2
|
||||
card.T.y = G.hand.T.y + G.hand.T.h/2 - card.T.h/2 - 0.5
|
||||
discover_card(card.config.center)
|
||||
elseif not nc then draw_card(G.hand, G.play, 1, 'up', true, card, nil, mute) end
|
||||
elseif nc then
|
||||
area:remove_from_highlighted(card)
|
||||
play_sound('cardSlide2', nil, 0.3)
|
||||
dont_dissolve = true
|
||||
else draw_card(G.hand, G.play, 1, 'up', true, card, nil, mute) end
|
||||
delay(0.2)
|
||||
e.config.ref_table:use_consumeable(area)
|
||||
SMODS.calculate_context({using_consumeable = true, consumeable = card, area = card.from_area})
|
||||
if card.edition and card.edition.key then
|
||||
local ed = SMODS.Centers[card.edition.key]
|
||||
if ed.calculate and type(ed.calculate) == 'function' then
|
||||
ed:calculate(card, {from_consumable = true})
|
||||
end
|
||||
end
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'using_consumeable', consumeable = card})
|
||||
elseif card.ability.set == 'Enhanced' or card.ability.set == 'Default' then
|
||||
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
|
||||
G.deck:emplace(card)
|
||||
|
@ -2293,8 +2294,6 @@ end
|
|||
prev_state == G.STATES.SPECTRAL_PACK or prev_state == G.STATES.STANDARD_PACK or
|
||||
prev_state == G.STATES.SMODS_BOOSTER_OPENED or
|
||||
prev_state == G.STATES.BUFFOON_PACK) and G.booster_pack then
|
||||
if nc and area == G.pack_cards then G.pack_cards:remove_card(card); G.consumeables:emplace(card) end
|
||||
booster_obj = nil
|
||||
if area == G.consumeables or area == G.hand then
|
||||
G.booster_pack.alignment.offset.y = G.booster_pack.alignment.offset.py
|
||||
G.booster_pack.alignment.offset.py = nil
|
||||
|
@ -2346,7 +2345,12 @@ end
|
|||
G.FUNCS.sell_card = function(e)
|
||||
local card = e.config.ref_table
|
||||
card:sell_card()
|
||||
SMODS.calculate_context({selling_card = true, card = card})
|
||||
for i = 1, #G.jokers.cards do
|
||||
if G.jokers.cards[i] ~= card then
|
||||
G.jokers.cards[i]:calculate_joker({selling_card = true, card = card})
|
||||
end
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'selling_card', card = card})
|
||||
end
|
||||
|
||||
G.FUNCS.can_confirm_contest_name = function(e)
|
||||
|
@ -2438,7 +2442,6 @@ G.FUNCS.buy_from_shop = function(e)
|
|||
trigger = 'after',
|
||||
delay = 0.1,
|
||||
func = function()
|
||||
c1.from_area = c1.area
|
||||
c1.area:remove_card(c1)
|
||||
c1:add_to_deck()
|
||||
if c1.children.price then c1.children.price:remove() end
|
||||
|
@ -2459,11 +2462,7 @@ G.FUNCS.buy_from_shop = function(e)
|
|||
else
|
||||
G.jokers:emplace(c1)
|
||||
end
|
||||
G.E_MANAGER:add_event(Event({func = function()
|
||||
local eval, post = eval_card(c1, {buying_card = true, card = c1})
|
||||
SMODS.trigger_effects({eval, post}, c1)
|
||||
return true
|
||||
end}))
|
||||
G.E_MANAGER:add_event(Event({func = function() c1:calculate_joker({buying_card = true, card = c1}) return true end}))
|
||||
end
|
||||
--Tallies for unlocks
|
||||
G.GAME.round_scores.cards_purchased.amt = G.GAME.round_scores.cards_purchased.amt + 1
|
||||
|
@ -2477,7 +2476,10 @@ G.FUNCS.buy_from_shop = function(e)
|
|||
G.GAME.current_round.jokers_purchased = G.GAME.current_round.jokers_purchased + 1
|
||||
end
|
||||
|
||||
SMODS.calculate_context({buying_card = true, card = c1})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({buying_card = true, card = c1})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'buying_card', card = c1})
|
||||
|
||||
if G.GAME.modifiers.inflation then
|
||||
G.GAME.inflation = G.GAME.inflation + 1
|
||||
|
@ -2509,7 +2511,10 @@ end
|
|||
stop_use()
|
||||
G.CONTROLLER.locks.toggle_shop = true
|
||||
if G.shop then
|
||||
SMODS.calculate_context({ending_shop = true})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({ending_shop = true})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'ending_shop'})
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'immediate',
|
||||
func = function()
|
||||
|
@ -2582,8 +2587,10 @@ end
|
|||
end
|
||||
|
||||
G.FUNCS.skip_booster = function(e)
|
||||
booster_obj = nil
|
||||
SMODS.calculate_context({skipping_booster = true})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({skipping_booster = true})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'skipping_booster'})
|
||||
G.FUNCS.end_consumeable(e)
|
||||
end
|
||||
|
||||
|
@ -2614,7 +2621,11 @@ end
|
|||
delay(0.2*delayfac)
|
||||
G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2*delayfac,
|
||||
func = function()
|
||||
G.FUNCS.draw_from_hand_to_deck()
|
||||
if not G.GAME.USING_RUN then
|
||||
G.FUNCS.draw_from_hand_to_deck()
|
||||
else
|
||||
G.FUNCS.draw_from_hand_to_run()
|
||||
end
|
||||
G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2*delayfac,
|
||||
func = function()
|
||||
if G.shop and G.shop.alignment.offset.py then
|
||||
|
@ -2791,7 +2802,10 @@ end
|
|||
trigger = 'immediate',
|
||||
func = function()
|
||||
delay(0.3)
|
||||
SMODS.calculate_context({skip_blind = true})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({skip_blind = true})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'skip_blind'})
|
||||
save_run()
|
||||
for i = 1, #G.GAME.tags do
|
||||
G.GAME.tags[i]:apply_to_run({type = 'immediate'})
|
||||
|
@ -2806,7 +2820,7 @@ end
|
|||
end
|
||||
|
||||
G.FUNCS.reroll_boss_button = function(e)
|
||||
if ((G.GAME.dollars-G.GAME.bankrupt_at) - cry_cheapest_boss_reroll() >= 0) and
|
||||
if ((to_big(G.GAME.dollars)-to_big(G.GAME.bankrupt_at)) - to_big(cry_cheapest_boss_reroll()) >= to_big(0)) and
|
||||
(G.GAME.used_vouchers["v_retcon"] or
|
||||
(G.GAME.used_vouchers["v_directors_cut"] and not G.GAME.round_resets.boss_rerolled)) then
|
||||
e.config.colour = G.C.RED
|
||||
|
@ -2822,16 +2836,6 @@ end
|
|||
end
|
||||
|
||||
G.FUNCS.reroll_boss = function(e)
|
||||
if not G.blind_select_opts then
|
||||
G.GAME.round_resets.boss_rerolled = true
|
||||
if not G.from_boss_tag then ease_dollars(-cry_cheapest_boss_reroll()) end
|
||||
G.from_boss_tag = nil
|
||||
G.GAME.round_resets.blind_choices.Boss = get_new_boss()
|
||||
for i = 1, #G.GAME.tags do
|
||||
if G.GAME.tags[i]:apply_to_run({type = 'new_blind_choice'}) then break end
|
||||
end
|
||||
return true
|
||||
end
|
||||
stop_use()
|
||||
G.GAME.round_resets.boss_rerolled = true
|
||||
if not G.from_boss_tag then ease_dollars(-cry_cheapest_boss_reroll()) end
|
||||
|
@ -2931,7 +2935,10 @@ end
|
|||
G.CONTROLLER.interrupt.focus = false
|
||||
G.CONTROLLER.locks.shop_reroll = false
|
||||
G.CONTROLLER:recall_cardarea_focus('shop_jokers')
|
||||
SMODS.calculate_context({reroll_shop = true})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({reroll_shop = true})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'reroll_shop'})
|
||||
return true
|
||||
end
|
||||
}))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = 'ab00e08d711a2c3f72976a640fd09ce67bc5a2fbcc3a1d480a50325ec883c75e'
|
||||
LOVELY_INTEGRITY = 'ab88a2edb6834f9903a71e4d3886fdab11953bd163c30e4b5640c2bb3712b019'
|
||||
|
||||
function set_screen_positions()
|
||||
if G.STAGE == G.STAGES.RUN then
|
||||
|
@ -75,7 +75,7 @@ function ease_dollars(mod, instant)
|
|||
mod = mod or 0
|
||||
local text = '+'..localize('$')
|
||||
local col = G.C.MONEY
|
||||
if to_big(mod) < to_big(0) then
|
||||
if mod < 0 then
|
||||
text = '-'..localize('$')
|
||||
col = G.C.RED
|
||||
else
|
||||
|
@ -120,7 +120,7 @@ function ease_discard(mod, instant, silent)
|
|||
mod = math.max(-G.GAME.current_round.discards_left, mod)
|
||||
local text = '+'
|
||||
local col = G.C.GREEN
|
||||
if to_big(mod) < to_big(0) then
|
||||
if mod < 0 then
|
||||
text = ''
|
||||
col = G.C.RED
|
||||
end
|
||||
|
@ -159,7 +159,7 @@ function ease_hands_played(mod, instant)
|
|||
mod = mod or 0
|
||||
local text = '+'
|
||||
local col = G.C.GREEN
|
||||
if to_big(mod) < to_big(0) then
|
||||
if mod < 0 then
|
||||
text = ''
|
||||
col = G.C.RED
|
||||
end
|
||||
|
@ -200,7 +200,7 @@ function ease_ante(mod)
|
|||
mod = mod or 0
|
||||
local text = '+'
|
||||
local col = G.C.IMPORTANT
|
||||
if to_big(mod) < to_big(0) then
|
||||
if mod < 0 then
|
||||
text = '-'
|
||||
col = G.C.RED
|
||||
end
|
||||
|
@ -235,7 +235,7 @@ function ease_round(mod)
|
|||
mod = mod or 0
|
||||
local text = '+'
|
||||
local col = G.C.IMPORTANT
|
||||
if to_big(mod) < to_big(0) then
|
||||
if mod < 0 then
|
||||
text = ''
|
||||
col = G.C.RED
|
||||
end
|
||||
|
@ -405,6 +405,9 @@ function draw_card(from, to, percent, dir, sort, card, delay, mute, stay_flipped
|
|||
if card then
|
||||
if from then card = from:remove_card(card) end
|
||||
if card then drawn = true end
|
||||
if card and to == G.hand and not card.states.visible then
|
||||
card.states.visible = true
|
||||
end
|
||||
local stay_flipped = G.GAME and G.GAME.blind and G.GAME.blind:stay_flipped(to, card)
|
||||
if G.GAME.modifiers.flipped_cards and to == G.hand then
|
||||
if pseudorandom(pseudoseed('flipped_card')) < 1/G.GAME.modifiers.flipped_cards then
|
||||
|
@ -484,7 +487,7 @@ function level_up_hand(card, hand, instant, amount)
|
|||
G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult * (universum_mod)^amount, 1)
|
||||
G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips * (universum_mod)^amount, 1)
|
||||
end
|
||||
if not instant and not Talisman.config_file.disable_anims then
|
||||
if not instant then
|
||||
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function()
|
||||
play_sound('tarot1')
|
||||
if card and card.juice_up then card:juice_up(0.8, 0.5) end
|
||||
|
@ -574,8 +577,8 @@ function update_hand_text(config, vals)
|
|||
G.GAME.current_round.current_hand.hand_level = vals.level
|
||||
else
|
||||
G.GAME.current_round.current_hand.hand_level = ' '..localize('k_lvl')..tostring(vals.level)
|
||||
if is_number(vals.level) then
|
||||
G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[to_big(math.min(vals.level, 7)):to_number()]
|
||||
if type(vals.level) == 'number' then
|
||||
G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[math.min(vals.level, 7)]
|
||||
else
|
||||
G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[1]
|
||||
end
|
||||
|
@ -594,57 +597,39 @@ function update_hand_text(config, vals)
|
|||
end
|
||||
|
||||
function eval_card(card, context)
|
||||
if card.ability.set ~= 'Joker' and card.debuff then return {}, {} end
|
||||
local enhancement_calculated = false
|
||||
local center = card.config.center
|
||||
context = context or {}
|
||||
local ret = {}
|
||||
|
||||
if context.repetition_only then
|
||||
if card.ability.set == 'Enhanced' then
|
||||
local enhancement = card:calculate_enhancement(context)
|
||||
if enhancement then
|
||||
ret.enhancement = enhancement
|
||||
end
|
||||
if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then
|
||||
center:calculate(card, context, ret)
|
||||
enhancement_calculated = true
|
||||
end
|
||||
if card.edition then
|
||||
local edition = card:calculate_edition(context)
|
||||
if edition then
|
||||
ret.edition = edition
|
||||
end
|
||||
local seals = card:calculate_seal(context)
|
||||
if seals then
|
||||
ret.seals = seals
|
||||
end
|
||||
if card.seal then
|
||||
local seals = card:calculate_seal(context)
|
||||
if seals then
|
||||
ret.seals = seals
|
||||
end
|
||||
end
|
||||
for k,v in pairs(SMODS.Stickers) do
|
||||
local sticker = card:calculate_sticker(context, k)
|
||||
if sticker then
|
||||
ret[v] = sticker
|
||||
end
|
||||
end
|
||||
|
||||
-- TARGET: evaluate your own repetition effects
|
||||
return ret
|
||||
end
|
||||
|
||||
if context.cardarea == G.play and context.main_scoring then
|
||||
ret.playing_card = {}
|
||||
if context.cardarea == G.play then
|
||||
local chips = card:get_chip_bonus()
|
||||
if chips ~= 0 then
|
||||
ret.playing_card.chips = chips
|
||||
if chips > 0 then
|
||||
ret.chips = chips
|
||||
end
|
||||
|
||||
|
||||
local mult = card:get_chip_mult()
|
||||
if mult ~= 0 then
|
||||
ret.playing_card.mult = mult
|
||||
if mult > 0 then
|
||||
ret.mult = mult
|
||||
end
|
||||
|
||||
|
||||
local x_mult = card:get_chip_x_mult(context)
|
||||
if x_mult > 0 then
|
||||
ret.playing_card.x_mult = x_mult
|
||||
ret.x_mult = x_mult
|
||||
end
|
||||
|
||||
|
||||
local x_chips = card:get_chip_x_bonus()
|
||||
if x_chips > 0 then
|
||||
ret.x_chips = x_chips
|
||||
|
@ -691,95 +676,76 @@ function eval_card(card, context)
|
|||
end
|
||||
local p_dollars = card:get_p_dollars()
|
||||
if p_dollars > 0 then
|
||||
ret.playing_card.p_dollars = p_dollars
|
||||
ret.p_dollars = p_dollars
|
||||
end
|
||||
|
||||
if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then
|
||||
center:calculate(card, context, ret)
|
||||
enhancement_calculated = true
|
||||
end
|
||||
|
||||
-- TARGET: main scoring on played cards
|
||||
|
||||
local jokers = card:calculate_joker(context)
|
||||
if jokers then
|
||||
ret.jokers = jokers
|
||||
end
|
||||
|
||||
local edition = card:calculate_edition(context)
|
||||
|
||||
local edition = card:get_edition(context)
|
||||
if edition then
|
||||
ret.edition = edition
|
||||
end
|
||||
end
|
||||
if context.end_of_round and context.cardarea == G.hand and context.playing_card_end_of_round then
|
||||
local end_of_round = card:get_end_of_round_effect(context)
|
||||
if end_of_round then
|
||||
ret.end_of_round = end_of_round
|
||||
end
|
||||
end
|
||||
|
||||
if context.cardarea == G.hand and context.main_scoring then
|
||||
ret.playing_card = {}
|
||||
if context.cardarea == G.hand then
|
||||
local h_mult = card:get_chip_h_mult()
|
||||
if h_mult ~= 0 then
|
||||
ret.playing_card.h_mult = h_mult
|
||||
if h_mult > 0 then
|
||||
ret.h_mult = h_mult
|
||||
end
|
||||
|
||||
|
||||
local h_x_mult = card:get_chip_h_x_mult()
|
||||
if h_x_mult > 0 then
|
||||
ret.playing_card.x_mult = h_x_mult
|
||||
ret.x_mult = h_x_mult
|
||||
end
|
||||
|
||||
-- TARGET: main scoring on held cards
|
||||
|
||||
if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then
|
||||
center:calculate(card, context, ret)
|
||||
enhancement_calculated = true
|
||||
end
|
||||
local jokers = card:calculate_joker(context)
|
||||
if jokers then
|
||||
ret.jokers = jokers
|
||||
end
|
||||
end
|
||||
|
||||
if card.ability.set == 'Enhanced' then
|
||||
local enhancement = card:calculate_enhancement(context)
|
||||
if enhancement then
|
||||
ret.enhancement = enhancement
|
||||
if not card.ability.extra_enhancement and card.edition and card.edition.key then
|
||||
local ed = SMODS.Centers[card.edition.key]
|
||||
if ed.calculate and type(ed.calculate) == 'function' then
|
||||
context.from_playing_card = true
|
||||
ed:calculate(card, context)
|
||||
context.from_playing_card = nil
|
||||
end
|
||||
end
|
||||
if card.edition then
|
||||
local edition = card:calculate_edition(context)
|
||||
if edition then
|
||||
ret.edition = edition
|
||||
end
|
||||
if not enhancement_calculated and card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then
|
||||
center:calculate(card, context, ret)
|
||||
enhancement_calculated = true
|
||||
end
|
||||
if card.seal and not card.ability.extra_enhancement then
|
||||
local seals = card:calculate_seal(context)
|
||||
if seals then
|
||||
ret.seals = seals
|
||||
end
|
||||
local seals = not card.ability.extra_enhancement and card:calculate_seal(context)
|
||||
if seals then
|
||||
ret.seals = seals
|
||||
end
|
||||
for k,v in pairs(SMODS.Stickers) do
|
||||
local sticker = card:calculate_sticker(context, k)
|
||||
if sticker then
|
||||
ret[v] = sticker
|
||||
if context.cardarea == G.jokers or context.card == G.consumeables then
|
||||
local jokers = nil
|
||||
if context.edition then
|
||||
jokers = card:get_edition(context)
|
||||
elseif context.other_joker then
|
||||
jokers = context.other_joker:calculate_joker(context)
|
||||
else
|
||||
jokers = card:calculate_joker(context)
|
||||
end
|
||||
end
|
||||
|
||||
-- TARGET: evaluate your own general effects
|
||||
local post_trig = {}
|
||||
local areas = SMODS.get_card_areas('jokers')
|
||||
local area_set = {}
|
||||
for _,v in ipairs(areas) do area_set[v] = true end
|
||||
if card.area and area_set[card.area] then
|
||||
local jokers, triggered = card:calculate_joker(context)
|
||||
if jokers or triggered then
|
||||
if jokers then
|
||||
ret.jokers = jokers
|
||||
if not (context.retrigger_joker_check or context.retrigger_joker) then
|
||||
local retriggers = SMODS.calculate_retriggers(card, context, ret)
|
||||
if next(retriggers) then
|
||||
ret.retriggers = retriggers
|
||||
end
|
||||
end
|
||||
if not context.post_trigger and not context.retrigger_joker_check and SMODS.optional_features.post_trigger then
|
||||
SMODS.calculate_context({blueprint_card = context.blueprint_card, post_trigger = true, other_card = card, other_context = context, other_ret = ret}, post_trig)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ret, post_trig
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function set_alerts()
|
||||
|
@ -919,14 +885,7 @@ function card_eval_status_text(card, eval_type, amt, percent, dir, extra)
|
|||
local y_off = 0.15*G.CARD_H
|
||||
if card.area == G.jokers or card.area == G.consumeables then
|
||||
y_off = 0.05*card.T.h
|
||||
elseif card == G.deck then
|
||||
y_off = -0.05*G.CARD_H
|
||||
card_aligned = 'tm'
|
||||
elseif card.area == G.discard or card.area == G.vouchers then
|
||||
y_off = card.area == G.discard and -0.35*G.CARD_H or -0.65*G.CARD_H
|
||||
card = G.deck.cards[1] or G.deck
|
||||
card_aligned = 'tm'
|
||||
elseif card.area == G.hand or card.area == G.deck then
|
||||
elseif card.area == G.hand then
|
||||
y_off = -0.05*G.CARD_H
|
||||
card_aligned = 'tm'
|
||||
elseif card.area == G.play then
|
||||
|
@ -951,35 +910,27 @@ function card_eval_status_text(card, eval_type, amt, percent, dir, extra)
|
|||
sound = 'chips1'
|
||||
amt = amt
|
||||
colour = G.C.CHIPS
|
||||
text = localize{type='variable',key='a_chips'..(to_big(amt)<to_big(0) and '_minus' or ''),vars={math.abs(amt)}}
|
||||
text = localize{type='variable',key='a_chips',vars={amt}}
|
||||
delay = 0.6
|
||||
elseif eval_type == 'mult' then
|
||||
sound = 'multhit1'--'other1'
|
||||
amt = amt
|
||||
text = localize{type='variable',key='a_mult'..(to_big(amt)<to_big(0) and '_minus' or ''),vars={math.abs(amt)}}
|
||||
text = localize{type='variable',key='a_mult',vars={amt}}
|
||||
colour = G.C.MULT
|
||||
config.type = 'fade'
|
||||
config.scale = 0.7
|
||||
elseif eval_type == 'x_chips' then
|
||||
sound = 'xchips'
|
||||
volume = 0.7
|
||||
amt = amt
|
||||
text = localize{type='variable',key='a_xchips'..(amt<0 and '_minus' or ''),vars={math.abs(amt)}}
|
||||
colour = G.C.BLUE
|
||||
config.type = 'fade'
|
||||
config.scale = 0.7
|
||||
elseif (eval_type == 'x_mult') or (eval_type == 'h_x_mult') then
|
||||
sound = 'multhit2'
|
||||
volume = 0.7
|
||||
amt = amt
|
||||
text = localize{type='variable',key='a_xmult'..(to_big(amt)<to_big(0) and '_minus' or ''),vars={math.abs(amt)}}
|
||||
text = localize{type='variable',key='a_xmult',vars={amt}}
|
||||
colour = G.C.XMULT
|
||||
config.type = 'fade'
|
||||
config.scale = 0.7
|
||||
elseif eval_type == 'h_mult' then
|
||||
sound = 'multhit1'
|
||||
amt = amt
|
||||
text = localize{type='variable',key='a_mult'..(to_big(amt)<to_big(0) and '_minus' or ''),vars={math.abs(amt)}}
|
||||
text = localize{type='variable',key='a_mult',vars={amt}}
|
||||
colour = G.C.MULT
|
||||
config.type = 'fade'
|
||||
config.scale = 0.7
|
||||
|
@ -1062,9 +1013,6 @@ function card_eval_status_text(card, eval_type, amt, percent, dir, extra)
|
|||
colour = G.C.DARK_EDITION
|
||||
end
|
||||
volume = extra.edition and 0.3 or sound == 'multhit2' and 0.7 or 1
|
||||
sound = extra.sound or sound
|
||||
percent = extra.pitch or percent
|
||||
volume = extra.volume or volume
|
||||
delay = extra.delay or 0.75
|
||||
amt = 1
|
||||
text = extra.message or text
|
||||
|
@ -1072,7 +1020,7 @@ function card_eval_status_text(card, eval_type, amt, percent, dir, extra)
|
|||
if not extra.edition and (extra.mult_mod or extra.Xmult_mod) then
|
||||
colour = G.C.MULT
|
||||
end
|
||||
if extra.chip_mod or extra.Xchip_mod then
|
||||
if extra.chip_mod then
|
||||
config.type = 'fall'
|
||||
colour = G.C.CHIPS
|
||||
config.scale = 0.7
|
||||
|
@ -1238,14 +1186,16 @@ function add_round_eval_row(config)
|
|||
end
|
||||
}))
|
||||
local dollar_row = 0
|
||||
num_dollars = to_number(num_dollars); if math.abs(to_number(num_dollars)) > 60 then
|
||||
if num_dollars > 60 or num_dollars < -60 then
|
||||
local dollar_string
|
||||
if num_dollars < 0 then --if negative
|
||||
dollar_string = '-'..localize('$')..(num_dollars*-1)
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'before',delay = 0.38,
|
||||
func = function()
|
||||
G.round_eval:add_child(
|
||||
{n=G.UIT.R, config={align = "cm", id = 'dollar_row_'..(dollar_row+1)..'_'..config.name}, nodes={
|
||||
{n=G.UIT.O, config={object = DynaText({string = {'-'..localize('$')..format_ui_value(-num_dollars)}, colours = {G.C.MONEY}, shadow = true, pop_in = 0, scale = 0.65, float = true})}}
|
||||
{n=G.UIT.O, config={object = DynaText({string = {localize('$')..(num_dollars*-1)}, colours = {G.C.MONEY}, shadow = true, pop_in = 0, scale = 0.65, float = true})}}
|
||||
}},
|
||||
G.round_eval:get_UIE_by_ID('dollar_'..config.name))
|
||||
play_sound('coin3', 0.9+0.2*math.random(), 0.7)
|
||||
|
@ -1254,6 +1204,7 @@ function add_round_eval_row(config)
|
|||
end
|
||||
}))
|
||||
else --if positive
|
||||
dollar_string = localize('$')..num_dollars
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'before',delay = 0.38,
|
||||
func = function()
|
||||
|
@ -1342,7 +1293,7 @@ function change_shop_size(mod)
|
|||
if not G.GAME.shop then return end
|
||||
G.GAME.shop.joker_max = G.GAME.shop.joker_max + mod
|
||||
if G.shop_jokers and G.shop_jokers.cards then
|
||||
if to_big(mod) < to_big(0) then
|
||||
if mod < 0 then
|
||||
--Remove jokers in shop
|
||||
for i = #G.shop_jokers.cards, G.GAME.shop.joker_max+1, -1 do
|
||||
if G.shop_jokers.cards[i] then
|
||||
|
@ -1474,7 +1425,7 @@ function check_for_unlock(args)
|
|||
end
|
||||
end
|
||||
if args.type == 'money' then
|
||||
if to_big(G.GAME.dollars) >= to_big(400) then
|
||||
if G.GAME.dollars >= 400 then
|
||||
unlock_achievement('nest_egg')
|
||||
end
|
||||
end
|
||||
|
@ -1511,7 +1462,7 @@ function check_for_unlock(args)
|
|||
end
|
||||
end
|
||||
if args.type == 'upgrade_hand' then
|
||||
if to_big(args.level) >= to_big(10) then
|
||||
if args.level >= 10 then
|
||||
unlock_achievement('retrograde')
|
||||
end
|
||||
end
|
||||
|
@ -1748,7 +1699,7 @@ function check_for_unlock(args)
|
|||
end
|
||||
end
|
||||
if args.type == 'money' then
|
||||
if to_big(card.unlock_condition.extra) <= to_big(G.GAME.dollars) then
|
||||
if card.unlock_condition.extra <= G.GAME.dollars then
|
||||
ret = true
|
||||
unlock_card(card)
|
||||
end
|
||||
|
@ -1938,8 +1889,7 @@ function fetch_achievements()
|
|||
G.ACHIEVEMENTS[kk].earned = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if G.F_NO_ACHIEVEMENTS then return end
|
||||
end if G.F_NO_ACHIEVEMENTS then return end
|
||||
|
||||
--|FROM LOCAL SETTINGS FILE
|
||||
--|-------------------------------------------------------
|
||||
|
@ -1974,32 +1924,15 @@ function fetch_achievements()
|
|||
end
|
||||
|
||||
function unlock_achievement(achievement_name)
|
||||
if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return true end
|
||||
if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return end
|
||||
G.E_MANAGER:add_event(Event({
|
||||
no_delete = true,
|
||||
blockable = false,
|
||||
blocking = false,
|
||||
func = function()
|
||||
if G.STATE ~= G.STATES.HAND_PLAYED then
|
||||
if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return true end
|
||||
if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return end
|
||||
local achievement_set = false
|
||||
if not G.ACHIEVEMENTS then fetch_achievements() end
|
||||
G.SETTINGS.ACHIEVEMENTS_EARNED[achievement_name] = true
|
||||
G:save_progress()
|
||||
|
||||
if G.ACHIEVEMENTS[achievement_name] and G.ACHIEVEMENTS[achievement_name].mod then
|
||||
if not G.ACHIEVEMENTS[achievement_name].earned then
|
||||
--|THIS IS THE FIRST TIME THIS ACHIEVEMENT HAS BEEN EARNED
|
||||
achievement_set = true
|
||||
G.FILE_HANDLER.force = true
|
||||
end
|
||||
G.ACHIEVEMENTS[achievement_name].earned = true
|
||||
end
|
||||
|
||||
if achievement_set then
|
||||
notify_alert(achievement_name)
|
||||
return true
|
||||
end
|
||||
if G.F_NO_ACHIEVEMENTS and not (G.ACHIEVEMENTS[achievement_name] or {}).mod then return true end
|
||||
|
||||
--|LOCAL SETTINGS FILE
|
||||
|
@ -2253,7 +2186,6 @@ function get_current_pool(_type, _rarity, _legendary, _append)
|
|||
|
||||
if _type == 'Joker' then
|
||||
_rarity = (_legendary and 4) or (type(_rarity) == "number" and ((_rarity > 0.95 and 3) or (_rarity > 0.7 and 2) or 1)) or _rarity
|
||||
_rarity = ({Common = 1, Uncommon = 2, Rare = 3, Legendary = 4})[_rarity] or _rarity
|
||||
local rarity = _rarity or SMODS.poll_rarity("Joker", 'rarity'..G.GAME.round_resets.ante..(_append or ''))
|
||||
|
||||
_starting_pool, _pool_key = G.P_JOKER_RARITY_POOLS[rarity], 'Joker'..rarity..((not _legendary and _append) or '')
|
||||
|
@ -2353,6 +2285,7 @@ local rarity = _rarity or SMODS.poll_rarity("Joker", 'rarity'..G.GAME.round_rese
|
|||
|
||||
if v.no_pool_flag and G.GAME.pool_flags[v.no_pool_flag] then add = nil end
|
||||
if v.yes_pool_flag and not G.GAME.pool_flags[v.yes_pool_flag] then add = nil end
|
||||
if v.key == 'j_cry_filler' and _append == 'rta' then add = nil end
|
||||
|
||||
if v.in_pool and type(v.in_pool) == 'function' then
|
||||
add = in_pool and (add or pool_opts.override_base_checks)
|
||||
|
@ -2531,15 +2464,6 @@ function copy_card(other, new_card, card_scale, playing_card, strip_edition)
|
|||
end
|
||||
check_for_unlock({type = 'have_edition'})
|
||||
new_card:set_seal(other.seal, true)
|
||||
if other.seal then
|
||||
for k, v in pairs(other.ability.seal or {}) do
|
||||
if type(v) == 'table' then
|
||||
new_card.ability.seal[k] = copy_table(v)
|
||||
else
|
||||
new_card.ability.seal[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
if other.params then
|
||||
new_card.params = other.params
|
||||
new_card.params.playing_card = playing_card
|
||||
|
@ -2892,13 +2816,10 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
else
|
||||
local res = {}
|
||||
if _c.locked_loc_vars and type(_c.locked_loc_vars) == 'function' then
|
||||
local _card = _c.create_fake_card and _c:create_fake_card()
|
||||
res = _c:locked_loc_vars(info_queue, _card) or {}
|
||||
res = _c:locked_loc_vars(info_queue) or {}
|
||||
loc_vars = res.vars or {}
|
||||
specific_vars = specific_vars or {}
|
||||
specific_vars.not_hidden = res.not_hidden or specific_vars.not_hidden
|
||||
if res.main_start then desc_nodes[#desc_nodes+1] = res.main_start end
|
||||
main_end = res.main_end or main_end
|
||||
elseif _c.name == 'Golden Ticket' then
|
||||
elseif _c.name == 'Mr. Bones' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_losses}
|
||||
elseif _c.name == 'Acrobat' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_hands_played}
|
||||
|
@ -2974,16 +2895,6 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
elseif specific_vars and specific_vars.debuffed then
|
||||
localize{type = 'other', key = 'debuffed_'..(specific_vars.playing_card and 'playing_card' or 'default'), nodes = desc_nodes}
|
||||
elseif _c.set == 'Joker' then
|
||||
if not card then
|
||||
local ability = copy_table(cfg)
|
||||
ability.set = 'Joker'
|
||||
ability.name = _c.name
|
||||
local ret = {Card.generate_UIBox_ability_table({ ability = ability, config = { center = _c }, bypass_lock = true}, true)}
|
||||
specific_vars = ret[1]
|
||||
if ret[2] then desc_nodes[#desc_nodes+1] = ret[2] end
|
||||
main_end = ret[3]
|
||||
end
|
||||
|
||||
if _c.name == 'Stone Joker' or _c.name == 'Marble Joker' then info_queue[#info_queue+1] = G.P_CENTERS.m_stone
|
||||
elseif _c.name == 'Steel Joker' then info_queue[#info_queue+1] = G.P_CENTERS.m_steel
|
||||
elseif _c.name == 'Glass Joker' then info_queue[#info_queue+1] = G.P_CENTERS.m_glass
|
||||
|
@ -3008,7 +2919,6 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
if specific_vars and specific_vars.sticker then info_queue[#info_queue+1] = {key = string.lower(specific_vars.sticker)..'_sticker', set = 'Other'} end
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = specific_vars or {}}
|
||||
elseif _c.set == 'Tag' then
|
||||
specific_vars = specific_vars or Tag.get_uibox_table({ name = _c.name, config = _c.config, ability = { orbital_hand = '['..localize('k_poker_hand')..']' }}, nil, true)
|
||||
if _c.name == 'Negative Tag' then info_queue[#info_queue+1] = G.P_CENTERS.e_negative
|
||||
elseif _c.name == 'Foil Tag' then info_queue[#info_queue+1] = G.P_CENTERS.e_foil
|
||||
elseif _c.name == 'Holographic Tag' then info_queue[#info_queue+1] = G.P_CENTERS.e_holo
|
||||
|
@ -3036,10 +2946,10 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
elseif _c.name == "Telescope" or _c.name == "Observatory" then loc_vars = {cfg.extra}
|
||||
elseif _c.name == "Clearance Sale" or _c.name == "Liquidation" then loc_vars = {cfg.extra}
|
||||
end
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = _c.vars or loc_vars}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}
|
||||
elseif _c.set == 'Edition' then
|
||||
loc_vars = {cfg.extra}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = _c.vars or loc_vars}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}
|
||||
elseif _c.set == 'Default' and specific_vars then
|
||||
if specific_vars.nominal_chips then
|
||||
localize{type = 'other', key = 'card_chips', nodes = desc_nodes, vars = {specific_vars.nominal_chips}}
|
||||
|
@ -3053,13 +2963,13 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
end
|
||||
if _c.effect == 'Mult Card' then loc_vars = {cfg.mult}
|
||||
elseif _c.effect == 'Wild Card' then
|
||||
elseif _c.effect == 'Glass Card' then loc_vars = {cfg.Xmult, G.GAME.probabilities.normal, cfg.extra}
|
||||
elseif _c.effect == 'Glass Card' then loc_vars = {cfg.Xmult, cfg.cry_prob and cry_prob(cfg.cry_prob, cfg.extra, cfg.cry_rigged) or G.GAME.probabilities.normal, cfg.extra}
|
||||
elseif _c.effect == 'Steel Card' then loc_vars = {cfg.h_x_mult}
|
||||
elseif _c.effect == 'Stone Card' then loc_vars = {((specific_vars and specific_vars.bonus_chips) or cfg.bonus)}
|
||||
elseif _c.effect == 'Gold Card' then loc_vars = {cfg.h_dollars}
|
||||
elseif _c.effect == 'Lucky Card' then loc_vars = {G.GAME.probabilities.normal, cfg.mult, 5, cfg.p_dollars, 15}
|
||||
elseif _c.effect == 'Lucky Card' then loc_vars = {cfg.cry_prob and cry_prob(cfg.cry_prob, 15, cfg.cry_rigged) or G.GAME.probabilities.normal, cfg.mult, 5, cfg.p_dollars, 15}
|
||||
end
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = _c.vars or loc_vars}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}
|
||||
if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or (cfg.bonus ~= 0 and cfg.bonus)) then
|
||||
localize{type = 'other', key = 'card_extra_chips', nodes = desc_nodes, vars = {((specific_vars and specific_vars.bonus_chips) or cfg.bonus)}}
|
||||
end
|
||||
|
@ -3092,10 +3002,10 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
if _c.name == 'Familiar' or _c.name == 'Grim' or _c.name == 'Incantation' then loc_vars = {cfg.extra}
|
||||
elseif _c.name == 'Immolate' then loc_vars = {cfg.extra.destroy, cfg.extra.dollars}
|
||||
elseif _c.name == 'Hex' then info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome
|
||||
elseif _c.name == 'Talisman' then info_queue[#info_queue+1] = G.P_SEALS['gold_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['gold_seal'] or '']
|
||||
elseif _c.name == 'Deja Vu' then info_queue[#info_queue+1] = G.P_SEALS['red_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['red_seal'] or '']
|
||||
elseif _c.name == 'Trance' then info_queue[#info_queue+1] = G.P_SEALS['blue_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['blue_seal'] or '']
|
||||
elseif _c.name == 'Medium' then info_queue[#info_queue+1] = G.P_SEALS['purple_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['purple_seal'] or '']
|
||||
elseif _c.name == 'Talisman' then info_queue[#info_queue+1] = {key = 'gold_seal', set = 'Other'}
|
||||
elseif _c.name == 'Deja Vu' then info_queue[#info_queue+1] = {key = 'red_seal', set = 'Other'}
|
||||
elseif _c.name == 'Trance' then info_queue[#info_queue+1] = {key = 'blue_seal', set = 'Other'}
|
||||
elseif _c.name == 'Medium' then info_queue[#info_queue+1] = {key = 'purple_seal', set = 'Other'}
|
||||
elseif _c.name == 'Ankh' then
|
||||
if G.jokers and G.jokers.cards then
|
||||
for k, v in ipairs(G.jokers.cards) do
|
||||
|
@ -3116,13 +3026,13 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
info_queue[#info_queue+1] = G.P_CENTERS.e_holo
|
||||
info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome
|
||||
end
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = _c.vars or loc_vars}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}
|
||||
elseif _c.set == 'Planet' then
|
||||
loc_vars = {
|
||||
G.GAME.hands[cfg.hand_type].level,localize(cfg.hand_type, 'poker_hands'), G.GAME.hands[cfg.hand_type].l_mult, G.GAME.hands[cfg.hand_type].l_chips,
|
||||
colours = {(to_big(G.GAME.hands[cfg.hand_type].level)==to_big(1) and G.C.UI.TEXT_DARK or G.C.HAND_LEVELS[to_big(math.min(7, G.GAME.hands[cfg.hand_type].level)):to_number()])}
|
||||
colours = {(G.GAME.hands[cfg.hand_type].level==1 and G.C.UI.TEXT_DARK or G.C.HAND_LEVELS[math.min(7, G.GAME.hands[cfg.hand_type].level)])}
|
||||
}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = _c.vars or loc_vars}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}
|
||||
elseif _c.set == 'Tarot' then
|
||||
if _c.name == "The Fool" then
|
||||
local fool_c = G.GAME.last_tarot_planet and G.P_CENTERS[G.GAME.last_tarot_planet] or nil
|
||||
|
@ -3148,7 +3058,7 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
elseif _c.name == "The Chariot" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv]
|
||||
elseif _c.name == "Justice" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv]
|
||||
elseif _c.name == "The Hermit" then loc_vars = {cfg.extra}
|
||||
elseif _c.name == "The Wheel of Fortune" then loc_vars = {G.GAME.probabilities.normal, cfg.extra}; info_queue[#info_queue+1] = G.P_CENTERS.e_foil; info_queue[#info_queue+1] = G.P_CENTERS.e_holo; info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome;
|
||||
elseif _c.name == "The Wheel of Fortune" then loc_vars = {cfg.cry_prob and cry_prob(cfg.cry_prob, cfg.extra, cfg.cry_rigged) or G.GAME.probabilities.normal, cfg.extra}; info_queue[#info_queue+1] = G.P_CENTERS.e_foil; info_queue[#info_queue+1] = G.P_CENTERS.e_holo; info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome;
|
||||
elseif _c.name == "Strength" then loc_vars = {cfg.max_highlighted}
|
||||
elseif _c.name == "The Hanged Man" then loc_vars = {cfg.max_highlighted}
|
||||
elseif _c.name == "Death" then loc_vars = {cfg.max_highlighted}
|
||||
|
@ -3170,7 +3080,7 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
elseif _c.name == "Judgement" then
|
||||
elseif _c.name == "The World" then loc_vars = {cfg.max_highlighted, localize(cfg.suit_conv, 'suits_plural'), colours = {G.C.SUITS[cfg.suit_conv]}}
|
||||
end
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = _c.vars or loc_vars}
|
||||
localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}
|
||||
end
|
||||
|
||||
if main_end then
|
||||
|
@ -3212,23 +3122,23 @@ function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, h
|
|||
info_queue[#info_queue + 1] = G.P_CENTERS[v]
|
||||
end
|
||||
if G.P_CENTERS['e_'..v] and G.P_CENTERS['e_'..v].set == 'Edition' then
|
||||
local t = {key = 'e_'..v, set = 'Edition', config = {}}
|
||||
info_queue[#info_queue + 1] = t
|
||||
if G.P_CENTERS['e_'..v].loc_vars and type(G.P_CENTERS['e_'..v].loc_vars) == 'function' then
|
||||
local res = G.P_CENTERS['e_'..v]:loc_vars(info_queue, card) or {}
|
||||
t.vars = res.vars
|
||||
t.key = res.key or t.key
|
||||
t.set = res.set or t.set
|
||||
end
|
||||
info_queue[#info_queue + 1] = G.P_CENTERS['e_'..v]
|
||||
end
|
||||
local seal = G.P_SEALS[v] or G.P_SEALS[SMODS.Seal.badge_to_key[v] or '']
|
||||
if seal then
|
||||
info_queue[#info_queue+1] = seal
|
||||
local seal = SMODS.Seals[v] or SMODS.Seal.badge_to_key[v] and SMODS.Seals[SMODS.Seal.badge_to_key[v]]
|
||||
if seal and seal.generate_ui ~= 0 then
|
||||
local t = { key = v, set = 'Other' }
|
||||
info_queue[#info_queue+1] = t
|
||||
if seal.loc_vars and type(seal.loc_vars) == 'function' then
|
||||
local res = seal:loc_vars(info_queue, card) or {}
|
||||
t.vars = res.vars
|
||||
t.key = res.key or t.key
|
||||
t.set = res.set or t.set
|
||||
end
|
||||
else
|
||||
if v == 'gold_seal' then info_queue[#info_queue+1] = G.P_SEALS['gold_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['gold_seal'] or ''] end
|
||||
if v == 'blue_seal' then info_queue[#info_queue+1] = G.P_SEALS['blue_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['blue_seal'] or ''] end
|
||||
if v == 'red_seal' then info_queue[#info_queue+1] = G.P_SEALS['red_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['red_seal'] or ''] end
|
||||
if v == 'purple_seal' then info_queue[#info_queue+1] = G.P_SEALS['purple_seal'] or G.P_SEALS[SMODS.Seal.badge_to_key['purple_seal'] or ''] end
|
||||
if v == 'gold_seal' then info_queue[#info_queue+1] = {key = 'gold_seal', set = 'Other'} end
|
||||
if v == 'blue_seal' then info_queue[#info_queue+1] = {key = 'blue_seal', set = 'Other'} end
|
||||
if v == 'red_seal' then info_queue[#info_queue+1] = {key = 'red_seal', set = 'Other'} end
|
||||
if v == 'purple_seal' then info_queue[#info_queue+1] = {key = 'purple_seal', set = 'Other'} end
|
||||
end
|
||||
local sticker = SMODS.Stickers[v]
|
||||
if sticker then
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = '947c501831366deb5e5f19e43dac710ed1309c3fcf0da962c8524122a4e9a142'
|
||||
LOVELY_INTEGRITY = '3062e877afa10107b5e3bbd8ec9b301a451c24f4ae132c36908811e861037ba4'
|
||||
|
||||
--Updates all display information for all displays for a given screenmode. Returns the key for the resolution option cycle
|
||||
--
|
||||
|
@ -818,7 +818,7 @@ function modulate_sound(dt)
|
|||
for k, v in pairs(G.ARGS.ambient_sounds) do
|
||||
AC[k] = AC[k] or {}
|
||||
AC[k].per = (k == 'ambientOrgan1') and 0.7 or (k == 'ambientFire1' and 1.1) or (k == 'ambientFire2' and 1.05) or 1
|
||||
AC[k].vol = (not G.video_organ and G.STATE == G.STATES.SPLASH) and 0 or AC[k].vol and v.volfunc(AC[k].vol) or 0
|
||||
AC[k].vol = Cartomancer.handle_flames_volume((not G.video_organ and G.STATE == G.STATES.SPLASH) and 0 or AC[k].vol and v.volfunc(AC[k].vol) or 0)
|
||||
end
|
||||
|
||||
G.ARGS.push = G.ARGS.push or {}
|
||||
|
@ -1577,6 +1577,14 @@ function save_with_action(action)
|
|||
G.action = nil
|
||||
end
|
||||
|
||||
function cry_prob(owned, den, rigged)
|
||||
prob = G.GAME and G.GAME.probabilities.normal or 1
|
||||
if rigged then
|
||||
return den
|
||||
else
|
||||
return prob*owned
|
||||
end
|
||||
end
|
||||
function save_run()
|
||||
if G.F_NO_SAVING == true then return end
|
||||
local cardAreas = {}
|
||||
|
@ -1984,73 +1992,24 @@ end
|
|||
function get_front_spriteinfo(_front)
|
||||
if _front and _front.suit and G.SETTINGS.CUSTOM_DECK and G.SETTINGS.CUSTOM_DECK.Collabs then
|
||||
local collab = G.SETTINGS.CUSTOM_DECK.Collabs[_front.suit]
|
||||
if collab then
|
||||
if collab and collab ~= 'default' then
|
||||
local deckSkin = SMODS.DeckSkins[collab]
|
||||
if deckSkin then
|
||||
if deckSkin.outdated then
|
||||
local hasRank = false
|
||||
for i = 1, #deckSkin.ranks do
|
||||
if deckSkin.ranks[i] == _front.value then hasRank = true break end
|
||||
end
|
||||
if hasRank then
|
||||
local atlas = G.ASSET_ATLAS[G.SETTINGS.colour_palettes[_front.suit] == 'hc' and deckSkin.hc_atlas or deckSkin.lc_atlas]
|
||||
if atlas then
|
||||
if deckSkin.pos_style == 'collab' then
|
||||
return atlas, G.COLLABS.pos[_front.value]
|
||||
elseif deckSkin.pos_style == 'suit' then
|
||||
return atlas, { x = _front.pos.x, y = 0}
|
||||
elseif deckSkin.pos_style == 'deck' then
|
||||
return atlas, _front.pos
|
||||
elseif deckSkin.pos_style == 'ranks' or nil then
|
||||
for i, rank in ipairs(deckSkin.ranks) do
|
||||
if rank == _front.value then
|
||||
return atlas, { x = i - 1, y = 0}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return G.ASSET_ATLAS[G.SETTINGS.colour_palettes[_front.suit] == 'hc' and _front.hc_atlas or _front.lc_atlas or {}] or G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(G.SETTINGS.colour_palettes[_front.suit] == 'hc' and 2 or 1)], _front.pos
|
||||
else
|
||||
local palette = deckSkin.palette_map and deckSkin.palette_map[G.SETTINGS.colour_palettes[_front.suit] or ''] or (deckSkin.palettes or {})[1]
|
||||
local hasRank = false
|
||||
for i = 1, #palette.ranks do
|
||||
if palette.ranks[i] == _front.value then hasRank = true break end
|
||||
end
|
||||
if hasRank then
|
||||
local atlas = G.ASSET_ATLAS[palette.atlas]
|
||||
if type(palette.pos_style) == "table" then
|
||||
if palette.pos_style[_front.value] then
|
||||
if palette.pos_style[_front.value].atlas then
|
||||
atlas = G.ASSET_ATLAS[palette.pos_style[_front.value].atlas]
|
||||
end
|
||||
if palette.pos_style[_front.value].pos then
|
||||
return atlas, palette.pos_style[_front.value].pos
|
||||
end
|
||||
elseif palette.pos_style.fallback_style then
|
||||
if palette.pos_style.fallback_style == 'collab' then
|
||||
return atlas, G.COLLABS.pos[_front.value]
|
||||
elseif palette.pos_style.fallback_style == 'suit' then
|
||||
return atlas, { x = _front.pos.x, y = 0}
|
||||
elseif palette.pos_style.fallback_style == 'deck' then
|
||||
return atlas, _front.pos
|
||||
end
|
||||
end
|
||||
elseif palette.pos_style == 'collab' then
|
||||
local hasRank = false
|
||||
for i = 1, #deckSkin.ranks do
|
||||
if deckSkin.ranks[i] == _front.value then hasRank = true break end
|
||||
end
|
||||
if hasRank then
|
||||
local atlas = G.ASSET_ATLAS[G.SETTINGS.colourblind_option and deckSkin.hc_atlas or deckSkin.lc_atlas]
|
||||
if atlas then
|
||||
if deckSkin.posStyle == 'collab' then
|
||||
return atlas, G.COLLABS.pos[_front.value]
|
||||
elseif palette.pos_style == 'suit' then
|
||||
elseif deckSkin.posStyle == 'suit' then
|
||||
return atlas, { x = _front.pos.x, y = 0}
|
||||
elseif palette.pos_style == 'deck' then
|
||||
elseif deckSkin.posStyle == 'deck' then
|
||||
return atlas, _front.pos
|
||||
elseif palette.pos_style == 'ranks' or nil then
|
||||
for i, rank in ipairs(palette.ranks) do
|
||||
if rank == _front.value then
|
||||
return atlas, { x = i - 1, y = 0}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return G.ASSET_ATLAS[palette.hc_default and _front.hc_atlas or _front.lc_atlas or {}] or G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(palette.hc_default and 2 or 1)], _front.pos
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = 'fff73090942942422c5293599c9a5f94fd18419e9275888a510770e6688dd01c'
|
||||
LOVELY_INTEGRITY = '6ce2fb7d4e93be5cc027ce62976490e8adadb9539ab9eecba2f579bc9b8dc2e5'
|
||||
|
||||
--Class
|
||||
Game = Object:extend()
|
||||
|
@ -109,7 +109,109 @@ function Game:start_up()
|
|||
boot_timer('window init', 'savemanager')
|
||||
--call the save manager to wait for any save requests
|
||||
G.SAVE_MANAGER = {
|
||||
thread = love.thread.newThread('engine/save_manager.lua'),
|
||||
thread = love.thread.newThread([[require "love.system"
|
||||
|
||||
if (love.system.getOS() == 'OS X' ) and (jit.arch == 'arm64' or jit.arch == 'arm') then jit.off() end
|
||||
|
||||
require "love.timer"
|
||||
require "love.thread"
|
||||
require 'love.filesystem'
|
||||
require "engine/object"
|
||||
require "engine/string_packer"
|
||||
|
||||
--vars needed for sound manager thread
|
||||
CHANNEL = love.thread.getChannel("save_request")
|
||||
|
||||
talisman = "]] .. Talisman.config_file.break_infinity .. [["
|
||||
|
||||
--untested
|
||||
function tal_compress_and_save(_file, _data)
|
||||
local save_string = type(_data) == 'table' and STR_PACK(_data) or _data
|
||||
local fallback_save = STR_PACK({GAME = {won = true}}) --just bare minimum to not crash, maybe eventually display some info?
|
||||
if talisman == 'bignumber' then
|
||||
fallback_save = "if not BigMeta then " .. fallback_save
|
||||
elseif talisman == 'omeganum' then
|
||||
fallback_save = "if not OmegaMeta then " .. fallback_save
|
||||
else
|
||||
fallback_save = "if BigMeta or OmegaMeta then " .. fallback_save
|
||||
end
|
||||
fallback_save = fallback_save .. " end"
|
||||
save_string = fallback_save .. " " .. save_string
|
||||
save_string = love.data.compress('string', 'deflate', save_string, 1)
|
||||
love.filesystem.write(_file,save_string)
|
||||
end
|
||||
|
||||
while true do
|
||||
--Monitor the channel for any new requests
|
||||
local request = CHANNEL:demand() -- Value from channel
|
||||
if request then
|
||||
--Saves progress for settings, unlocks, alerts and discoveries
|
||||
if request.type == 'save_progress' then
|
||||
local prefix_profile = (request.save_progress.SETTINGS.profile or 1)..''
|
||||
if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end
|
||||
prefix_profile = prefix_profile..'/'
|
||||
|
||||
if not love.filesystem.getInfo(prefix_profile..'meta.jkr') then
|
||||
love.filesystem.append( prefix_profile..'meta.jkr', 'return {}' )
|
||||
end
|
||||
|
||||
local meta = STR_UNPACK(get_compressed(prefix_profile..'meta.jkr') or 'return {}')
|
||||
meta.unlocked = meta.unlocked or {}
|
||||
meta.discovered = meta.discovered or {}
|
||||
meta.alerted = meta.alerted or {}
|
||||
|
||||
local _append = false
|
||||
|
||||
for k, v in pairs(request.save_progress.UDA) do
|
||||
if string.find(v, 'u') and not meta.unlocked[k] then
|
||||
meta.unlocked[k] = true
|
||||
_append = true
|
||||
end
|
||||
if string.find(v, 'd') and not meta.discovered[k] then
|
||||
meta.discovered[k] = true
|
||||
_append = true
|
||||
end
|
||||
if string.find(v, 'a') and not meta.alerted[k] then
|
||||
meta.alerted[k] = true
|
||||
_append = true
|
||||
end
|
||||
end
|
||||
if _append then compress_and_save( prefix_profile..'meta.jkr', STR_PACK(meta)) end
|
||||
|
||||
compress_and_save('settings.jkr', request.save_progress.SETTINGS)
|
||||
compress_and_save(prefix_profile..'profile.jkr', request.save_progress.PROFILE)
|
||||
|
||||
CHANNEL:push('done')
|
||||
--Saves the settings file
|
||||
elseif request.type == 'save_settings' then
|
||||
compress_and_save('settings.jkr', request.save_settings)
|
||||
compress_and_save(request.profile_num..'/profile.jkr', request.save_profile)
|
||||
--Saves the metrics file
|
||||
elseif request.type == 'save_metrics' then
|
||||
compress_and_save('metrics.jkr', request.save_metrics)
|
||||
--Saves any notifications
|
||||
elseif request.type == 'save_notify' then
|
||||
local prefix_profile = (request.profile_num or 1)..''
|
||||
if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end
|
||||
prefix_profile = prefix_profile..'/'
|
||||
|
||||
if not love.filesystem.getInfo(prefix_profile..'unlock_notify.jkr') then love.filesystem.append( prefix_profile..'unlock_notify.jkr', '') end
|
||||
local unlock_notify = get_compressed(prefix_profile..'unlock_notify.jkr') or ''
|
||||
|
||||
if request.save_notify and not string.find(unlock_notify, request.save_notify) then
|
||||
compress_and_save( prefix_profile..'unlock_notify.jkr', unlock_notify..request.save_notify..'\n')
|
||||
end
|
||||
|
||||
--Saves the run
|
||||
elseif request.type == 'save_run' then
|
||||
local prefix_profile = (request.profile_num or 1)..''
|
||||
if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end
|
||||
prefix_profile = prefix_profile..'/'
|
||||
|
||||
tal_compress_and_save(prefix_profile..'save.jkr', request.save_table)
|
||||
end
|
||||
end
|
||||
end]]),
|
||||
channel = love.thread.getChannel('save_request')
|
||||
}
|
||||
G.SAVE_MANAGER.thread:start(2)
|
||||
|
@ -220,6 +322,7 @@ function Game:start_up()
|
|||
end
|
||||
end
|
||||
set_profile_progress()
|
||||
Cartomancer.load_mod_file('internal/localization.lua', 'localization')
|
||||
boot_timer('prep stage', 'splash prep',1)
|
||||
self:splash_screen()
|
||||
boot_timer('splash prep', 'end',1)
|
||||
|
@ -1252,7 +1355,7 @@ function Game:prep_stage(new_stage, new_state, new_game_obj)
|
|||
self.CONTROLLER.locks[k] = nil
|
||||
end
|
||||
if new_game_obj then self.GAME = self:init_game_object() end
|
||||
if Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end
|
||||
if new_game_obj and Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end
|
||||
self.STAGE = new_stage or self.STAGES.MAIN_MENU
|
||||
self.STATE = new_state or self.STATES.MENU
|
||||
self.STATE_COMPLETE = false
|
||||
|
@ -1449,12 +1552,12 @@ function Game:splash_screen()
|
|||
local option = math.random(#mcard)
|
||||
local chosenoption = mcard[option]
|
||||
if chosenoption == "j_cry_biggestm" or chosenoption == "j_cry_reverse" then --These don't render properly; replace these with loopy instead
|
||||
SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_cry_loopy'])
|
||||
SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_cry_loopy'],{bypass_discovery_center = true, bypass_discovery_ui = true})
|
||||
else
|
||||
SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[chosenoption])
|
||||
SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[chosenoption],{bypass_discovery_center = true, bypass_discovery_ui = true})
|
||||
end
|
||||
else
|
||||
SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_jolly'])
|
||||
SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_jolly'],{bypass_discovery_center = true, bypass_discovery_ui = true})
|
||||
end
|
||||
end
|
||||
SC.T.y = G.ROOM.T.h/2 - SC_scale*G.CARD_H/2
|
||||
|
@ -1948,6 +2051,10 @@ function Game:init_game_object()
|
|||
pseudorandom = {},
|
||||
starting_deck_size = 52,
|
||||
ecto_minus = 1,
|
||||
cry_bonusvouchercount = 0,
|
||||
cry_bonusvouchersused = {},
|
||||
voucher_edition_index = {},
|
||||
voucher_sticker_index = {eternal = {}, perishable = {}, rental = {}, pinned = {}, banana = {}}, -- might as well
|
||||
pack_size = 2,
|
||||
skips = 0,
|
||||
STOP_USE = 0,
|
||||
|
@ -2008,6 +2115,9 @@ function Game:init_game_object()
|
|||
hand_level = ''
|
||||
},
|
||||
used_packs = {},
|
||||
cry_bonusvouchers = {},
|
||||
cry_voucher_stickers = {eternal = false, perishable = false, rental = false, pinned = false, banana = false},
|
||||
cry_voucher_edition = {},
|
||||
cards_flipped = 0,
|
||||
round_text = 'Round ',
|
||||
idol_card = {suit = 'Spades', rank = 'Ace'},
|
||||
|
@ -2090,13 +2200,20 @@ function Game:start_run(args)
|
|||
local selected_back = saveTable and saveTable.BACK.name or (args.challenge and args.challenge.deck and args.challenge.deck.type) or (self.GAME.viewed_back and self.GAME.viewed_back.name) or self.GAME.selected_back and self.GAME.selected_back.name or 'Red Deck'
|
||||
selected_back = get_deck_from_name(selected_back)
|
||||
self.GAME = saveTable and saveTable.GAME or self:init_game_object()
|
||||
if Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end
|
||||
if (not saveTable or not saveTable.GAME) and Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end
|
||||
Handy.UI.init()
|
||||
self.GAME.modifiers = self.GAME.modifiers or {}
|
||||
self.GAME.stake = args.stake or self.GAME.stake or 1
|
||||
self.GAME.STOP_USE = 0
|
||||
self.GAME.selected_back = Back(selected_back)
|
||||
self.GAME.selected_back_key = selected_back
|
||||
G.GAME.cry_voucher_centers = {}
|
||||
for k, v in pairs(G.P_CENTERS) do
|
||||
if v.set == 'Voucher' then
|
||||
G.GAME.cry_voucher_centers[k] = {config = {}}
|
||||
G.GAME.cry_voucher_centers[k].config = copy_table(v.config)
|
||||
end
|
||||
end
|
||||
|
||||
G.C.UI_CHIPS[1], G.C.UI_CHIPS[2], G.C.UI_CHIPS[3], G.C.UI_CHIPS[4] = G.C.BLUE[1], G.C.BLUE[2], G.C.BLUE[3], G.C.BLUE[4]
|
||||
G.C.UI_MULT[1], G.C.UI_MULT[2], G.C.UI_MULT[3], G.C.UI_MULT[4] = G.C.RED[1], G.C.RED[2], G.C.RED[3], G.C.RED[4]
|
||||
|
@ -2216,13 +2333,6 @@ function Game:start_run(args)
|
|||
end
|
||||
|
||||
G.GAME.chips_text = ''
|
||||
G.GAME.cry_voucher_centers = {}
|
||||
for k, v in pairs(G.P_CENTERS) do
|
||||
if v.set == 'Voucher' then
|
||||
G.GAME.cry_voucher_centers[k] = {config = {}}
|
||||
G.GAME.cry_voucher_centers[k].config = copy_table(v.config)
|
||||
end
|
||||
end
|
||||
|
||||
if not saveTable then
|
||||
if args.seed then self.GAME.seeded = true end
|
||||
|
@ -2261,6 +2371,9 @@ function Game:start_run(args)
|
|||
if not self.GAME.modifiers.cry_no_vouchers then
|
||||
if not G.GAME.modifiers.cry_voucher_restock_antes or G.GAME.round_resets.ante % G.GAME.modifiers.cry_voucher_restock_antes == 0 then
|
||||
self.GAME.current_round.voucher = G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_voucher or get_next_voucher_key()
|
||||
for i = 1, self.GAME.cry_bonusvouchercount do
|
||||
self.GAME.current_round.cry_bonusvouchers[i] = get_next_voucher_key()
|
||||
end
|
||||
end
|
||||
else
|
||||
very_fair_quip = pseudorandom_element(G.localization.misc.very_fair_quips, pseudoseed("cry_very_fair"))
|
||||
|
@ -2348,11 +2461,6 @@ function Game:start_run(args)
|
|||
0, 0,
|
||||
CAI.discard_W,CAI.discard_H,
|
||||
{card_limit = 1e308, type = 'discard'})
|
||||
self.vouchers = CardArea(
|
||||
G.discard.T.x, G.discard.T.y,
|
||||
G.discard.T.w, G.discard.T.h,
|
||||
{ type = "discard", card_limit = 1e308 }
|
||||
)
|
||||
self.deck = CardArea(
|
||||
0, 0,
|
||||
CAI.deck_W,CAI.deck_H,
|
||||
|
@ -2553,6 +2661,7 @@ function Game:start_run(args)
|
|||
reset_blinds()
|
||||
end
|
||||
|
||||
Cartomancer.update_tags_visibility()
|
||||
G.FUNCS.blind_chip_UI_scale(G.hand_text_area.blind_chips)
|
||||
|
||||
self.HUD:recalculate()
|
||||
|
@ -2589,7 +2698,7 @@ function Game:update(dt)
|
|||
self.TIMERS.BACKGROUND = self.TIMERS.BACKGROUND + dt*(G.ARGS.spin and G.ARGS.spin.amount or 0)
|
||||
self.real_dt = dt
|
||||
|
||||
if self.real_dt > 0.05 then print('LONG DT @ '..math.floor(G.TIMERS.REAL)..': '..self.real_dt) end
|
||||
if require('debugplus.config').getValue('enableLongDT') and self.real_dt > 0.05 then print('LONG DT @ '..math.floor(G.TIMERS.REAL)..': '..self.real_dt) end
|
||||
if not G.fbf or G.new_frame then
|
||||
G.new_frame = false
|
||||
|
||||
|
@ -2607,7 +2716,7 @@ function Game:update(dt)
|
|||
if G.STATE ~= G.ACC_state then G.ACC = 0 end
|
||||
G.ACC_state = G.STATE
|
||||
|
||||
if (G.STATE == G.STATES.HAND_PLAYED) or (G.STATE == G.STATES.NEW_ROUND) then
|
||||
if (G.STATE == G.STATES.HAND_PLAYED) or (G.STATE == G.STATES.NEW_ROUND) or Incantation and Incantation.accelerate then
|
||||
G.ACC = math.min((G.ACC or 0) + dt*0.2*self.SETTINGS.GAMESPEED, 16)
|
||||
elseif Handy.insta_cash_out.is_skipped then G.ACC = 999
|
||||
else
|
||||
|
@ -2667,7 +2776,23 @@ function Game:update(dt)
|
|||
end
|
||||
|
||||
|
||||
if G.GAME.USING_RUN then self.STATE = self.STATES.SHOP end
|
||||
if G.GAME.USING_RUN then
|
||||
if not (self.STATE == self.STATES.STANDARD_PACK or self.STATE == self.STATES.BUFFOON_PACK or self.STATE == self.STATES.PLANET_PACK or self.STATE == self.STATES.TAROT_PACK or self.STATE == self.STATES.SPECTRAL_PACK or self.STATE == self.STATES.SMODS_BOOSTER_OPENED) then -- do you are have stupid
|
||||
self.STATE = self.STATES.SHOP
|
||||
end
|
||||
if G.GAME.blind then G.GAME.blind:change_colour() end -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
if G.load_cry_runarea then
|
||||
G.cry_runarea = CardArea(
|
||||
G.discard.T.x,
|
||||
G.discard.T.y,
|
||||
G.discard.T.w,
|
||||
G.discard.T.h,
|
||||
{ type = "discard", card_limit = 1e100 }
|
||||
)
|
||||
G.cry_runarea:load(G.load_cry_runarea)
|
||||
G.load_cry_runarea = nil
|
||||
end
|
||||
end
|
||||
if self.STATE == self.STATES.SELECTING_HAND then
|
||||
if (not G.hand.cards[1]) and G.deck.cards[1] then
|
||||
G.STATE = G.STATES.DRAW_TO_HAND
|
||||
|
@ -2851,7 +2976,6 @@ function Game:update(dt)
|
|||
if G.FILE_HANDLER.run then
|
||||
G.SAVE_MANAGER.channel:push({
|
||||
type = 'save_run',
|
||||
talisman = Talisman.config_file.break_infinity,
|
||||
save_table = G.ARGS.save_run,
|
||||
profile_num = G.SETTINGS.profile})
|
||||
G.SAVED_GAME = nil
|
||||
|
@ -3107,11 +3231,23 @@ love.graphics.pop()
|
|||
|
||||
timer_checkpoint('canvas', 'draw')
|
||||
|
||||
if not _RELEASE_MODE and G.DEBUG and not G.video_control and G.F_VERBOSE then
|
||||
if require("debugplus.config").getValue("showHUD") and not G.video_control and G.F_VERBOSE then
|
||||
love.graphics.push()
|
||||
love.graphics.setColor(0, 1, 1,1)
|
||||
local fps = love.timer.getFPS( )
|
||||
love.graphics.print("Current FPS: "..fps, 10, 10)
|
||||
do
|
||||
local otherSize = 0
|
||||
for k,v in pairs(G.E_MANAGER.queues or {}) do
|
||||
if k ~= 'base' then
|
||||
otherSize = otherSize + #v
|
||||
end
|
||||
end
|
||||
if otherSize ~= 0 then
|
||||
love.graphics.print(string.format("Current FPS: %d\nBase event queue: %d\nOther event queues: %d", fps, #(G.E_MANAGER.queues and G.E_MANAGER.queues.base or {}), otherSize), 10, 10)
|
||||
else
|
||||
love.graphics.print(string.format("Current FPS: %d\nBase event queue: %d", fps, #(G.E_MANAGER.queues and G.E_MANAGER.queues.base or {})), 10, 10)
|
||||
end
|
||||
end
|
||||
|
||||
if G.check and G.SETTINGS.perf_mode then
|
||||
local section_h = 30
|
||||
|
@ -3130,6 +3266,10 @@ love.graphics.pop()
|
|||
end
|
||||
love.graphics.rectangle('fill', 10+poll_w*kk, 20 + v_off, 5*poll_w, -(vv)*resolution)
|
||||
end
|
||||
v_off = v_off + section_h
|
||||
end
|
||||
local v_off = v_off - section_h * #b.checkpoint_list
|
||||
for k, v in ipairs(b.checkpoint_list) do
|
||||
love.graphics.setColor(a == 2 and 0.5 or 1, a == 2 and 1 or 0.5, 1,1)
|
||||
love.graphics.print(v.label..': '..(string.format("%.2f",1000*(v.average or 0)))..'\n', 10, -section_h + 30 + v_off)
|
||||
v_off = v_off + section_h
|
||||
|
@ -3299,20 +3439,48 @@ function Game:update_shop(dt)
|
|||
end
|
||||
end
|
||||
G.shop_vouchers:emplace(card)
|
||||
end
|
||||
end for i = 1, #G.GAME.current_round.cry_bonusvouchers do
|
||||
if not G.GAME.cry_bonusvouchersused[i] then
|
||||
local card = Card(G.shop_vouchers.T.x + G.shop_vouchers.T.w/2,
|
||||
G.shop_vouchers.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[G.GAME.current_round.cry_bonusvouchers[i]],{bypass_discovery_center = true, bypass_discovery_ui = true})
|
||||
card.shop_cry_bonusvoucher = i
|
||||
cry_misprintize(card)
|
||||
if G.GAME.events.ev_cry_choco2 then
|
||||
card.misprint_cost_fac = (card.misprint_cost_fac or 1) * 2
|
||||
card:set_cost()
|
||||
end
|
||||
if G.GAME.modifiers.cry_enable_flipped_in_shop and pseudorandom('cry_flip_vouch'..G.GAME.round_resets.ante) > 0.7 then
|
||||
card.cry_flipped = true
|
||||
end
|
||||
create_shop_card_ui(card, 'Voucher', G.shop_vouchers)
|
||||
card:start_materialize()
|
||||
if G.GAME.current_round.cry_voucher_edition then -- eh why not
|
||||
card:set_edition(G.GAME.current_round.cry_voucher_edition, true, true)
|
||||
end
|
||||
G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1 -- does this actually matter/even get reset??? i'm confused but whatever
|
||||
G.shop_vouchers:emplace(card)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if G.GAME.events.ev_cry_choco10 and not G.load_shop_vouchers then
|
||||
local card = create_card('Joker', G.jokers, true, nil, nil, nil, nil, 'cry_antique')
|
||||
cry_misprintize(card)
|
||||
card.misprint_cost_fac = 50/card.cost
|
||||
card:set_cost()
|
||||
create_shop_card_ui(card, 'Voucher', G.shop_vouchers)
|
||||
card:start_materialize()
|
||||
card.ability.cry_antique = true
|
||||
G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1
|
||||
G.shop_vouchers:emplace(card)
|
||||
if G.GAME.events.ev_cry_choco10 then
|
||||
local add = true
|
||||
for k, v in pairs(G.shop_vouchers.cards) do -- G.load_shop_vouchers is already set to nil here, just do a normal check
|
||||
if v.ability.cry_antique then add = false end
|
||||
end
|
||||
if add then
|
||||
local card = create_card('Joker', G.jokers, true, nil, nil, nil, nil, 'cry_antique')
|
||||
cry_misprintize(card)
|
||||
card.misprint_cost_fac = 50/card.cost
|
||||
card:set_cost()
|
||||
create_shop_card_ui(card, 'Voucher', G.shop_vouchers)
|
||||
card:start_materialize()
|
||||
card.ability.cry_antique = true
|
||||
G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1
|
||||
G.shop_vouchers:emplace(card)
|
||||
end
|
||||
end
|
||||
if G.load_shop_booster then
|
||||
nosave_shop = true
|
||||
|
@ -3461,9 +3629,14 @@ end
|
|||
|
||||
if G.GAME.current_round.hands_played == 0 and
|
||||
G.GAME.current_round.discards_used == 0 and G.GAME.facing_blind then
|
||||
SMODS.calculate_context({first_hand_drawn = true})
|
||||
for i = 1, #G.hand.cards do
|
||||
eval_card(G.hand.cards[i], {first_hand_drawn = true})
|
||||
end
|
||||
SMODS.calculate_context({hand_drawn = true})
|
||||
for i = 1, #G.jokers.cards do
|
||||
G.jokers.cards[i]:calculate_joker({first_hand_drawn = true})
|
||||
end
|
||||
G.GAME.selected_back:trigger_effect({context = 'first_hand_drawn'})
|
||||
end
|
||||
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'immediate',
|
||||
|
|
|
@ -1,4 +1,148 @@
|
|||
LOVELY_INTEGRITY = '7a80102588959caa0bf8986947ac508ff87fd856221043d85aeb54a472571ab0'
|
||||
LOVELY_INTEGRITY = 'b2c04153038fd7e4525be3be1bc642bcab91ec38fb988d0c5db268f85955e1dd'
|
||||
|
||||
|
||||
local Cartomancer_replacements = {
|
||||
{
|
||||
find = [[
|
||||
for k, v in ipairs%(G%.playing_cards%) do
|
||||
if v%.base%.suit then table%.insert%(SUITS%[v%.base%.suit%], v%) end]],
|
||||
-- Steamodded<0917b
|
||||
find_alt = [[
|
||||
for k, v in ipairs%(G%.playing_cards%) do
|
||||
table%.insert%(SUITS%[v%.base%.suit%], v%)]],
|
||||
place = [[
|
||||
local SUITS_SORTED = Cartomancer.tablecopy(SUITS)
|
||||
for k, v in ipairs(G.playing_cards) do
|
||||
if v.base.suit then
|
||||
local greyed
|
||||
if unplayed_only and not ((v.area and v.area == G.deck) or v.ability.wheel_flipped) then
|
||||
greyed = true
|
||||
end
|
||||
local card_string = v:cart_to_string()
|
||||
if greyed then
|
||||
card_string = card_string .. "Greyed" -- for some reason format doesn't work and final string is `sGreyed`
|
||||
end
|
||||
if greyed and Cartomancer.SETTINGS.deck_view_hide_drawn_cards then
|
||||
-- Ignore this card.
|
||||
elseif not Cartomancer.SETTINGS.deck_view_stack_enabled then
|
||||
-- Don't stack cards
|
||||
local _scale = 0.7
|
||||
local copy = copy_card(v, nil, _scale)
|
||||
|
||||
copy.greyed = greyed
|
||||
copy.stacked_quantity = 1
|
||||
table.insert(SUITS_SORTED[v.base.suit], copy)
|
||||
|
||||
elseif not SUITS[v.base.suit][card_string] then
|
||||
-- Initiate stack
|
||||
table.insert(SUITS_SORTED[v.base.suit], card_string)
|
||||
|
||||
local _scale = 0.7
|
||||
local copy = copy_card(v, nil, _scale)
|
||||
|
||||
copy.greyed = greyed
|
||||
copy.stacked_quantity = 1
|
||||
|
||||
SUITS[v.base.suit][card_string] = copy
|
||||
else
|
||||
-- Stack cards
|
||||
local stacked_card = SUITS[v.base.suit][card_string]
|
||||
stacked_card.stacked_quantity = stacked_card.stacked_quantity + 1
|
||||
end
|
||||
end]]
|
||||
},
|
||||
|
||||
{
|
||||
find = "card_limit = #SUITS%[suit_map%[j%]%],",
|
||||
place = "card_limit = #SUITS_SORTED[suit_map[j]],"
|
||||
},
|
||||
|
||||
{
|
||||
find = [[
|
||||
for i = 1%, %#SUITS%[suit_map%[j%]%] do
|
||||
if SUITS%[suit_map%[j%]%]%[i%] then
|
||||
local greyed%, _scale = nil%, 0%.7
|
||||
if unplayed_only and not %(%(SUITS%[suit_map%[j%]%]%[i%]%.area and SUITS%[suit_map%[j%]%]%[i%]%.area == G%.deck%) or SUITS%[suit_map%[j%]%]%[i%]%.ability%.wheel_flipped%) then
|
||||
greyed = true
|
||||
end
|
||||
local copy = copy_card%(SUITS%[suit_map%[j%]%]%[i%]%, nil%, _scale%)
|
||||
copy%.greyed = greyed
|
||||
copy%.T%.x = view_deck%.T%.x %+ view_deck%.T%.w %/ 2
|
||||
copy%.T%.y = view_deck%.T%.y
|
||||
|
||||
copy:hard_set_T%(%)
|
||||
view_deck:emplace%(copy%)
|
||||
end
|
||||
end]],
|
||||
place = [[
|
||||
for i = 1%, %#SUITS_SORTED%[suit_map%[j%]%] do
|
||||
local card
|
||||
if not Cartomancer.SETTINGS.deck_view_stack_enabled then
|
||||
card = SUITS_SORTED%[suit_map%[j%]%]%[i%]
|
||||
else
|
||||
local card_string = SUITS_SORTED%[suit_map%[j%]%]%[i%]
|
||||
card = SUITS%[suit_map%[j%]%]%[card_string%]
|
||||
end
|
||||
|
||||
card%.T%.x = view_deck%.T%.x %+ view_deck%.T%.w%/2
|
||||
card%.T%.y = view_deck%.T%.y
|
||||
card:create_quantity_display%(%)
|
||||
|
||||
card:hard_set_T%(%)
|
||||
view_deck:emplace%(card%)
|
||||
end]]
|
||||
},
|
||||
|
||||
{
|
||||
find = ' modded and {n = G.UIT.R, config = {align = "cm"}, nodes = {',
|
||||
place = [=[
|
||||
not unplayed_only and Cartomancer.add_unique_count() or nil,
|
||||
modded and {n = G.UIT.R, config = {align = "cm"}, nodes = {]=]
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
-- Mom, can we have lovely patches for overrides.lua?
|
||||
-- No, we have lovely patches at home
|
||||
|
||||
-- Lovely patches at home:
|
||||
|
||||
local Cartomancer_nfs_read
|
||||
local Cartomancer_nfs_read_override = function (containerOrName, nameOrSize, sizeOrNil)
|
||||
local data, size = Cartomancer_nfs_read(containerOrName, nameOrSize, sizeOrNil)
|
||||
|
||||
if type(containerOrName) ~= "string" then
|
||||
return data, size
|
||||
end
|
||||
local overrides = '/overrides.lua'
|
||||
if containerOrName:sub(-#overrides) ~= overrides then
|
||||
return data, size
|
||||
end
|
||||
|
||||
local replaced = 0
|
||||
local total_replaced = 0
|
||||
for _, v in ipairs(Cartomancer_replacements) do
|
||||
data, replaced = string.gsub(data, v.find, v.place)
|
||||
|
||||
if replaced == 0 and v.find_alt then
|
||||
data, replaced = string.gsub(data, v.find_alt, v.place)
|
||||
end
|
||||
|
||||
if replaced == 0 then
|
||||
print("Failed to replace " .. v.find .. " for overrides.lua")
|
||||
else
|
||||
total_replaced = total_replaced + 1
|
||||
end
|
||||
end
|
||||
|
||||
print("Totally applied " .. total_replaced .. " replacements to overrides.lua")
|
||||
|
||||
-- We no longer need this override
|
||||
NFS.read = Cartomancer_nfs_read
|
||||
|
||||
return data, size
|
||||
end
|
||||
|
||||
--- STEAMODDED CORE
|
||||
--- MODULE STACKTRACE
|
||||
|
@ -500,7 +644,7 @@ function getDebugInfoForCrash()
|
|||
local versionFile = love.filesystem.read("version.jkr")
|
||||
if versionFile then
|
||||
version = versionFile:match("[^\n]*") .. " (best guess)"
|
||||
else
|
||||
else
|
||||
version = "???"
|
||||
end
|
||||
end
|
||||
|
@ -511,18 +655,18 @@ function getDebugInfoForCrash()
|
|||
modded_version = reqVersion
|
||||
else
|
||||
modded_version = "???"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local info = "Additional Context:\nBalatro Version: " .. version .. "\nModded Version: " ..
|
||||
(modded_version)
|
||||
local major, minor, revision, codename = love.getVersion()
|
||||
info = info .. string.format("\nLÖVE Version: %d.%d.%d", major, minor, revision)
|
||||
|
||||
local lovely_success, lovely = pcall(require, "lovely")
|
||||
if lovely_success then
|
||||
info = info .. "\nLovely Version: " .. lovely.version
|
||||
end
|
||||
info = info .. "\nPlatform: " .. (love.system.getOS() or "???")
|
||||
if SMODS and SMODS.Mods then
|
||||
local mod_strings = ""
|
||||
local lovely_strings = ""
|
||||
|
@ -857,6 +1001,10 @@ injectStackTrace()
|
|||
-- --------MOD CORE API STACKTRACE END-----------
|
||||
|
||||
if (love.system.getOS() == 'OS X' ) and (jit.arch == 'arm64' or jit.arch == 'arm') then jit.off() end
|
||||
do
|
||||
local logger = require("debugplus.logger")
|
||||
logger.registerLogHandler()
|
||||
end
|
||||
require "engine/object"
|
||||
require "bit"
|
||||
require "engine/string_packer"
|
||||
|
@ -942,11 +1090,15 @@ function love.run()
|
|||
end
|
||||
|
||||
Cryptid = {}
|
||||
Cryptid.enabled = {}
|
||||
Cryptid.memepack = {}
|
||||
Cryptid.aliases = {}
|
||||
Cryptid.food = {}
|
||||
Cryptid.M_jokers = {}
|
||||
Cryptid.Megavouchers = {}
|
||||
function cry_format(...)
|
||||
return ...
|
||||
end
|
||||
function love.load()
|
||||
G:start_up()
|
||||
--Steam integration
|
||||
|
@ -1002,10 +1154,17 @@ function love.draw()
|
|||
--Perf monitoring checkpoint
|
||||
timer_checkpoint(nil, 'draw', true)
|
||||
G:draw()
|
||||
do
|
||||
local console = require("debugplus.console")
|
||||
console.doConsoleRender()
|
||||
timer_checkpoint('DebugPlus Console', 'draw')
|
||||
end
|
||||
end
|
||||
|
||||
function love.keypressed(key)
|
||||
if Handy.controller.process_key(key, false) then return end
|
||||
local console = require("debugplus.console")
|
||||
if not console.consoleHandleKey(key) then return end
|
||||
if not _RELEASE_MODE and G.keybind_mapping[key] then love.gamepadpressed(G.CONTROLLER.keyboard_controller, G.keybind_mapping[key])
|
||||
else
|
||||
G.CONTROLLER:set_HID_flags('mouse')
|
||||
|
@ -1269,7 +1428,6 @@ SMODS.id = 'Steamodded'
|
|||
SMODS.version = MODDED_VERSION:gsub('%-STEAMODDED', '')
|
||||
SMODS.can_load = true
|
||||
SMODS.meta_mod = true
|
||||
SMODS.config_file = 'config.lua'
|
||||
|
||||
-- Include lovely and nativefs modules
|
||||
local nativefs = require "nativefs"
|
||||
|
@ -1325,6 +1483,10 @@ end
|
|||
|
||||
SMODS.path = find_self(SMODS.MODS_DIR, 'core.lua', '--- STEAMODDED CORE')
|
||||
|
||||
Cartomancer_nfs_read = NFS.read
|
||||
NFS.read = Cartomancer_nfs_read_override
|
||||
|
||||
|
||||
for _, path in ipairs {
|
||||
"src/ui.lua",
|
||||
"src/index.lua",
|
||||
|
@ -3612,9 +3774,7 @@ if Talisman.config_file.break_infinity then
|
|||
v.s_mult = to_big(v.s_mult)
|
||||
v.l_chips = to_big(v.l_chips)
|
||||
v.l_mult = to_big(v.l_mult)
|
||||
v.level = to_big(v.level)
|
||||
end
|
||||
obj.starting_params.dollars = to_big(obj.starting_params.dollars)
|
||||
return obj
|
||||
end
|
||||
|
||||
|
@ -3636,11 +3796,6 @@ if Talisman.config_file.break_infinity then
|
|||
if type(x) == 'table' then return x:floor() end
|
||||
return mf(x)
|
||||
end
|
||||
local mc = math.ceil
|
||||
function math.ceil(x)
|
||||
if type(x) == 'table' then return x:ceil() end
|
||||
return mc(x)
|
||||
end
|
||||
|
||||
local l10 = math.log10
|
||||
function math.log10(x)
|
||||
|
@ -3756,25 +3911,6 @@ if Talisman.config_file.break_infinity then
|
|||
end--]] --going to hold off on modifying this until proper save loading exists
|
||||
end
|
||||
|
||||
local ics = inc_career_stat
|
||||
-- This is used often for unlocks, so we can't just prevent big money from being added
|
||||
-- Also, I'm completely overriding this, since I don't think any mods would want to change it
|
||||
function inc_career_stat(stat, mod)
|
||||
if G.GAME.seeded or G.GAME.challenge then return end
|
||||
if not G.PROFILES[G.SETTINGS.profile].career_stats[stat] then G.PROFILES[G.SETTINGS.profile].career_stats[stat] = 0 end
|
||||
G.PROFILES[G.SETTINGS.profile].career_stats[stat] = G.PROFILES[G.SETTINGS.profile].career_stats[stat] + (mod or 0)
|
||||
-- Make sure this isn't ever a talisman number
|
||||
if type(G.PROFILES[G.SETTINGS.profile].career_stats[stat]) == 'table' then
|
||||
if G.PROFILES[G.SETTINGS.profile].career_stats[stat] > to_big(1e300) then
|
||||
G.PROFILES[G.SETTINGS.profile].career_stats[stat] = to_big(1e300)
|
||||
elseif G.PROFILES[G.SETTINGS.profile].career_stats[stat] < to_big(-1e300) then
|
||||
G.PROFILES[G.SETTINGS.profile].career_stats[stat] = to_big(-1e300)
|
||||
end
|
||||
G.PROFILES[G.SETTINGS.profile].career_stats[stat] = G.PROFILES[G.SETTINGS.profile].career_stats[stat]:to_number()
|
||||
end
|
||||
G:save_settings()
|
||||
end
|
||||
|
||||
local sn = scale_number
|
||||
function scale_number(number, scale, max, e_switch_point)
|
||||
if not Big then return sn(number, scale, max, e_switch_point) end
|
||||
|
@ -3934,8 +4070,8 @@ function tal_uht(config, vals)
|
|||
G.GAME.current_round.current_hand.hand_level = vals.level
|
||||
else
|
||||
G.GAME.current_round.current_hand.hand_level = ' '..localize('k_lvl')..tostring(vals.level)
|
||||
if is_number(vals.level) then
|
||||
G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[to_big(math.min(vals.level, 7)):to_number()]
|
||||
if type(vals.level) == 'number' then
|
||||
G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[math.min(vals.level, 7)]
|
||||
else
|
||||
G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[1]
|
||||
end
|
||||
|
@ -3969,145 +4105,135 @@ function Game:update(dt)
|
|||
Talisman.dollar_update = false
|
||||
end
|
||||
end
|
||||
Talisman.F_NO_COROUTINE = false --easy disabling for bugfixing, since the coroutine can make it hard to see where errors are
|
||||
if not Talisman.F_NO_COROUTINE then
|
||||
--scoring coroutine
|
||||
local oldplay = G.FUNCS.evaluate_play
|
||||
--scoring coroutine
|
||||
local oldplay = G.FUNCS.evaluate_play
|
||||
|
||||
function G.FUNCS.evaluate_play()
|
||||
G.SCORING_COROUTINE = coroutine.create(oldplay)
|
||||
G.LAST_SCORING_YIELD = love.timer.getTime()
|
||||
G.CARD_CALC_COUNTS = {} -- keys = cards, values = table containing numbers
|
||||
local success, err = coroutine.resume(G.SCORING_COROUTINE)
|
||||
if not success then
|
||||
error(err)
|
||||
function G.FUNCS.evaluate_play()
|
||||
G.SCORING_COROUTINE = coroutine.create(oldplay)
|
||||
G.LAST_SCORING_YIELD = love.timer.getTime()
|
||||
G.CARD_CALC_COUNTS = {} -- keys = cards, values = table containing numbers
|
||||
local success, err = coroutine.resume(G.SCORING_COROUTINE)
|
||||
if not success then
|
||||
error(err)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local oldupd = love.update
|
||||
function love.update(dt, ...)
|
||||
oldupd(dt, ...)
|
||||
if G.SCORING_COROUTINE then
|
||||
if collectgarbage("count") > 1024*1024 then
|
||||
collectgarbage("collect")
|
||||
end
|
||||
end
|
||||
if coroutine.status(G.SCORING_COROUTINE) == "dead" then
|
||||
G.SCORING_COROUTINE = nil
|
||||
G.FUNCS.exit_overlay_menu()
|
||||
local totalCalcs = 0
|
||||
for i, v in pairs(G.CARD_CALC_COUNTS) do
|
||||
totalCalcs = totalCalcs + v[1]
|
||||
end
|
||||
G.GAME.LAST_CALCS = totalCalcs
|
||||
else
|
||||
G.SCORING_TEXT = nil
|
||||
if not G.OVERLAY_MENU then
|
||||
G.scoring_text = {"Calculating...", "", "", ""}
|
||||
G.SCORING_TEXT = {
|
||||
{n = G.UIT.C, nodes = {
|
||||
{n = G.UIT.R, config = {padding = 0.1, align = "cm"}, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 1}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 1, silent = true})}},
|
||||
}},{n = G.UIT.R, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 2}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}},
|
||||
}},{n = G.UIT.R, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 3}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}},
|
||||
}},{n = G.UIT.R, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 4}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}},
|
||||
}}}}}
|
||||
G.FUNCS.overlay_menu({
|
||||
definition =
|
||||
{n=G.UIT.ROOT, minw = G.ROOM.T.w*5, minh = G.ROOM.T.h*5, config={align = "cm", padding = 9999, offset = {x = 0, y = -3}, r = 0.1, colour = {G.C.GREY[1], G.C.GREY[2], G.C.GREY[3],0.7}}, nodes= G.SCORING_TEXT},
|
||||
config = {align="cm", offset = {x=0,y=0}, major = G.ROOM_ATTACH, bond = 'Weak'}
|
||||
})
|
||||
else
|
||||
|
||||
|
||||
local oldupd = love.update
|
||||
function love.update(dt, ...)
|
||||
oldupd(dt, ...)
|
||||
if G.SCORING_COROUTINE then
|
||||
if collectgarbage("count") > 1024*1024 then
|
||||
collectgarbage("collect")
|
||||
end
|
||||
if coroutine.status(G.SCORING_COROUTINE) == "dead" then
|
||||
G.SCORING_COROUTINE = nil
|
||||
G.FUNCS.exit_overlay_menu()
|
||||
local totalCalcs = 0
|
||||
for i, v in pairs(G.CARD_CALC_COUNTS) do
|
||||
totalCalcs = totalCalcs + v[1]
|
||||
end
|
||||
G.GAME.LAST_CALCS = totalCalcs
|
||||
G.GAME.LAST_CALC_TIME = G.CURRENT_CALC_TIME
|
||||
else
|
||||
G.SCORING_TEXT = nil
|
||||
if not G.OVERLAY_MENU then
|
||||
G.scoring_text = {"Calculating...", "", "", ""}
|
||||
G.SCORING_TEXT = {
|
||||
{n = G.UIT.C, nodes = {
|
||||
{n = G.UIT.R, config = {padding = 0.1, align = "cm"}, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 1}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 1, silent = true})}},
|
||||
}},{n = G.UIT.R, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 2}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}},
|
||||
}},{n = G.UIT.R, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 3}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}},
|
||||
}},{n = G.UIT.R, nodes = {
|
||||
{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 4}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}},
|
||||
}}}}}
|
||||
G.FUNCS.overlay_menu({
|
||||
definition =
|
||||
{n=G.UIT.ROOT, minw = G.ROOM.T.w*5, minh = G.ROOM.T.h*5, config={align = "cm", padding = 9999, offset = {x = 0, y = -3}, r = 0.1, colour = {G.C.GREY[1], G.C.GREY[2], G.C.GREY[3],0.7}}, nodes= G.SCORING_TEXT},
|
||||
config = {align="cm", offset = {x=0,y=0}, major = G.ROOM_ATTACH, bond = 'Weak'}
|
||||
})
|
||||
else
|
||||
|
||||
if G.OVERLAY_MENU and G.scoring_text then
|
||||
local totalCalcs = 0
|
||||
for i, v in pairs(G.CARD_CALC_COUNTS) do
|
||||
totalCalcs = totalCalcs + v[1]
|
||||
end
|
||||
local jokersYetToScore = #G.jokers.cards + #G.play.cards - #G.CARD_CALC_COUNTS
|
||||
G.CURRENT_CALC_TIME = (G.CURRENT_CALC_TIME or 0) + dt
|
||||
G.scoring_text[1] = "Calculating..."
|
||||
G.scoring_text[2] = "Elapsed calculations: "..tostring(totalCalcs).." ("..tostring(number_format(G.CURRENT_CALC_TIME)).."s)"
|
||||
G.scoring_text[3] = "Cards yet to score: "..tostring(jokersYetToScore)
|
||||
G.scoring_text[4] = "Calculations last played hand: " .. tostring(G.GAME.LAST_CALCS or "Unknown") .." ("..tostring(G.GAME.LAST_CALC_TIME and number_format(G.GAME.LAST_CALC_TIME) or "???").."s)"
|
||||
if G.OVERLAY_MENU and G.scoring_text then
|
||||
local totalCalcs = 0
|
||||
for i, v in pairs(G.CARD_CALC_COUNTS) do
|
||||
totalCalcs = totalCalcs + v[1]
|
||||
end
|
||||
local jokersYetToScore = #G.jokers.cards + #G.play.cards - #G.CARD_CALC_COUNTS
|
||||
G.scoring_text[1] = "Calculating..."
|
||||
G.scoring_text[2] = "Elapsed calculations: "..tostring(totalCalcs)
|
||||
G.scoring_text[3] = "Cards yet to score: "..tostring(jokersYetToScore)
|
||||
G.scoring_text[4] = "Calculations last played hand: " .. tostring(G.GAME.LAST_CALCS or "Unknown")
|
||||
end
|
||||
|
||||
end
|
||||
--this coroutine allows us to stagger GC cycles through
|
||||
--the main source of waste in terms of memory (especially w joker retriggers) is through local variables that become garbage
|
||||
--this practically eliminates the memory overhead of scoring
|
||||
--event queue overhead seems to not exist if Talismans Disable Scoring Animations is off.
|
||||
--event manager has to wait for scoring to finish until it can keep processing events anyways.
|
||||
end
|
||||
--this coroutine allows us to stagger GC cycles through
|
||||
--the main source of waste in terms of memory (especially w joker retriggers) is through local variables that become garbage
|
||||
--this practically eliminates the memory overhead of scoring
|
||||
--event queue overhead seems to not exist if Talismans Disable Scoring Animations is off.
|
||||
--event manager has to wait for scoring to finish until it can keep processing events anyways.
|
||||
|
||||
|
||||
G.LAST_SCORING_YIELD = love.timer.getTime()
|
||||
|
||||
local success, msg = coroutine.resume(G.SCORING_COROUTINE)
|
||||
if not success then
|
||||
error(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
G.LAST_SCORING_YIELD = love.timer.getTime()
|
||||
|
||||
local success, msg = coroutine.resume(G.SCORING_COROUTINE)
|
||||
if not success then
|
||||
error(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
TIME_BETWEEN_SCORING_FRAMES = 0.03 -- 30 fps during scoring
|
||||
-- we dont want overhead from updates making scoring much slower
|
||||
-- originally 10 fps, I think 30 fps is a good way to balance it while making it look smooth, too
|
||||
--wrap everything in calculating contexts so we can do more things with it
|
||||
Talisman.calculating_joker = false
|
||||
Talisman.calculating_score = false
|
||||
Talisman.calculating_card = false
|
||||
Talisman.dollar_update = false
|
||||
local ccj = Card.calculate_joker
|
||||
function Card:calculate_joker(context)
|
||||
--scoring coroutine
|
||||
G.CURRENT_SCORING_CARD = self
|
||||
G.CARD_CALC_COUNTS = G.CARD_CALC_COUNTS or {}
|
||||
if G.CARD_CALC_COUNTS[self] then
|
||||
G.CARD_CALC_COUNTS[self][1] = G.CARD_CALC_COUNTS[self][1] + 1
|
||||
else
|
||||
G.CARD_CALC_COUNTS[self] = {1, 1}
|
||||
end
|
||||
|
||||
|
||||
if G.LAST_SCORING_YIELD and ((love.timer.getTime() - G.LAST_SCORING_YIELD) > TIME_BETWEEN_SCORING_FRAMES) and coroutine.running() then
|
||||
coroutine.yield()
|
||||
end
|
||||
Talisman.calculating_joker = true
|
||||
local ret = ccj(self, context)
|
||||
|
||||
TIME_BETWEEN_SCORING_FRAMES = 0.03 -- 30 fps during scoring
|
||||
-- we dont want overhead from updates making scoring much slower
|
||||
-- originally 10 fps, I think 30 fps is a good way to balance it while making it look smooth, too
|
||||
--wrap everything in calculating contexts so we can do more things with it
|
||||
if ret and type(ret) == "table" and ret.repetitions then
|
||||
G.CARD_CALC_COUNTS[ret.card] = G.CARD_CALC_COUNTS[ret.card] or {1,1}
|
||||
G.CARD_CALC_COUNTS[ret.card][2] = G.CARD_CALC_COUNTS[ret.card][2] + ret.repetitions
|
||||
end
|
||||
Talisman.calculating_joker = false
|
||||
return ret
|
||||
end
|
||||
local cuc = Card.use_consumable
|
||||
function Card:use_consumable(x,y)
|
||||
Talisman.calculating_score = true
|
||||
local ret = cuc(self, x,y)
|
||||
Talisman.calculating_score = false
|
||||
Talisman.calculating_card = false
|
||||
Talisman.dollar_update = false
|
||||
local ccj = Card.calculate_joker
|
||||
function Card:calculate_joker(context)
|
||||
--scoring coroutine
|
||||
G.CURRENT_SCORING_CARD = self
|
||||
G.CARD_CALC_COUNTS = G.CARD_CALC_COUNTS or {}
|
||||
if G.CARD_CALC_COUNTS[self] then
|
||||
G.CARD_CALC_COUNTS[self][1] = G.CARD_CALC_COUNTS[self][1] + 1
|
||||
else
|
||||
G.CARD_CALC_COUNTS[self] = {1, 1}
|
||||
end
|
||||
|
||||
|
||||
if G.LAST_SCORING_YIELD and ((love.timer.getTime() - G.LAST_SCORING_YIELD) > TIME_BETWEEN_SCORING_FRAMES) and coroutine.running() then
|
||||
coroutine.yield()
|
||||
end
|
||||
Talisman.calculating_joker = true
|
||||
local ret, trig = ccj(self, context)
|
||||
|
||||
if ret and type(ret) == "table" and ret.repetitions then
|
||||
if not ret.card then
|
||||
G.CARD_CALC_COUNTS.other = G.CARD_CALC_COUNTS.other or {1,1}
|
||||
G.CARD_CALC_COUNTS.other[2] = G.CARD_CALC_COUNTS.other[2] + ret.repetitions
|
||||
else
|
||||
G.CARD_CALC_COUNTS[ret.card] = G.CARD_CALC_COUNTS[ret.card] or {1,1}
|
||||
G.CARD_CALC_COUNTS[ret.card][2] = G.CARD_CALC_COUNTS[ret.card][2] + ret.repetitions
|
||||
end
|
||||
end
|
||||
Talisman.calculating_joker = false
|
||||
return ret, trig
|
||||
end
|
||||
local cuc = Card.use_consumable
|
||||
function Card:use_consumable(x,y)
|
||||
Talisman.calculating_score = true
|
||||
local ret = cuc(self, x,y)
|
||||
Talisman.calculating_score = false
|
||||
return ret
|
||||
end
|
||||
local gfep = G.FUNCS.evaluate_play
|
||||
G.FUNCS.evaluate_play = function(e)
|
||||
Talisman.calculating_score = true
|
||||
local ret = gfep(e)
|
||||
Talisman.calculating_score = false
|
||||
return ret
|
||||
end
|
||||
return ret
|
||||
end
|
||||
local gfep = G.FUNCS.evaluate_play
|
||||
G.FUNCS.evaluate_play = function(e)
|
||||
Talisman.calculating_score = true
|
||||
local ret = gfep(e)
|
||||
Talisman.calculating_score = false
|
||||
return ret
|
||||
end
|
||||
--[[local ec = eval_card
|
||||
function eval_card()
|
||||
|
@ -4202,7 +4328,7 @@ local edo = ease_dollars
|
|||
function ease_dollars(mod, instant)
|
||||
if Talisman.config_file.disable_anims then--and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then
|
||||
mod = mod or 0
|
||||
if to_big(mod) < to_big(0) then inc_career_stat('c_dollars_earned', mod) end
|
||||
if mod < 0 then inc_career_stat('c_dollars_earned', mod) end
|
||||
G.GAME.dollars = G.GAME.dollars + mod
|
||||
Talisman.dollar_update = true
|
||||
else return edo(mod, instant) end
|
||||
|
@ -4217,13 +4343,11 @@ function safe_str_unpack(str)
|
|||
if success then
|
||||
return result
|
||||
else
|
||||
print("[Talisman] Error unpacking string: " .. result)
|
||||
print(tostring(str))
|
||||
print("Error unpacking string: " .. result)
|
||||
return nil
|
||||
end
|
||||
else
|
||||
print("[Talisman] Error loading string: " .. err)
|
||||
print(tostring(str))
|
||||
print("Error loading string: " .. err)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
@ -4252,207 +4376,6 @@ function G.FUNCS.evaluate_round()
|
|||
end
|
||||
end
|
||||
|
||||
local g_start_run = Game.start_run
|
||||
function Game:start_run(args)
|
||||
local ret = g_start_run(self, args)
|
||||
self.GAME.round_resets.ante_disp = self.GAME.round_resets.ante_disp or number_format(self.GAME.round_resets.ante)
|
||||
return ret
|
||||
end
|
||||
|
||||
-- Steamodded calculation API: add extra operations
|
||||
if SMODS and SMODS.calculate_individual_effect then
|
||||
local smods_xchips = false
|
||||
for _, v in pairs(SMODS.calculation_keys) do
|
||||
if v == 'x_chips' then
|
||||
smods_xchips = true
|
||||
break
|
||||
end
|
||||
end
|
||||
local scie = SMODS.calculate_individual_effect
|
||||
function SMODS.calculate_individual_effect(effect, scored_card, key, amount, from_edition)
|
||||
-- For some reason, some keys' animations are completely removed
|
||||
-- I think this is caused by a lovely patch conflict
|
||||
--if key == 'chip_mod' then key = 'chips' end
|
||||
--if key == 'mult_mod' then key = 'mult' end
|
||||
--if key == 'Xmult_mod' then key = 'x_mult' end
|
||||
local ret = scie(effect, scored_card, key, amount, from_edition)
|
||||
if ret then
|
||||
return ret
|
||||
end
|
||||
if not smods_xchips and (key == 'x_chips' or key == 'xchips' or key == 'Xchip_mod') and amount ~= 1 then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
hand_chips = mod_chips(hand_chips * amount)
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = "X"..amount, colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'Xchip_mod' then
|
||||
if effect.xchip_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.xchip_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'x_chips', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'e_chips' or key == 'echips' or key == 'Echip_mod') and amount ~= 1 then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
hand_chips = mod_chips(hand_chips ^ amount)
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = "^"..amount, colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'Echip_mod' then
|
||||
if effect.echip_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.echip_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'e_chips', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'ee_chips' or key == 'eechips' or key == 'EEchip_mod') and amount ~= 1 then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
hand_chips = mod_chips(hand_chips:arrow(2, amount))
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = "^^"..amount, colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'EEchip_mod' then
|
||||
if effect.eechip_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.eechip_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'ee_chips', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'eee_chips' or key == 'eeechips' or key == 'EEEchip_mod') and amount ~= 1 then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
hand_chips = mod_chips(hand_chips:arrow(3, amount))
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = "^^^"..amount, colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'EEEchip_mod' then
|
||||
if effect.eeechip_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.eeechip_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'eee_chips', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'hyper_chips' or key == 'hyperchips' or key == 'hyperchip_mod') and type(amount) == 'table' then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
hand_chips = mod_chips(hand_chips:arrow(amount[1], amount[2]))
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = (amount[1] > 5 and ('{' .. amount[1] .. '}') or string.rep('^', amount[1])) .. amount[2], colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'hyperchip_mod' then
|
||||
if effect.hyperchip_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.hyperchip_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'hyper_chips', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'e_mult' or key == 'emult' or key == 'Emult_mod') and amount ~= 1 then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
mult = mod_chips(mult ^ amount)
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = "^"..amount.." Mult", colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'Emult_mod' then
|
||||
if effect.emult_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.emult_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'e_mult', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'ee_mult' or key == 'eemult' or key == 'EEmult_mod') and amount ~= 1 then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
mult = mod_chips(mult:arrow(2, amount))
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = "^^"..amount.." Mult", colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'EEmult_mod' then
|
||||
if effect.eemult_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.eemult_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'ee_mult', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'eee_mult' or key == 'eeemult' or key == 'EEEmult_mod') and amount ~= 1 then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
mult = mod_chips(mult:arrow(3, amount))
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = "^^^"..amount.." Mult", colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'EEEmult_mod' then
|
||||
if effect.eeemult_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.eeemult_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'eee_mult', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if (key == 'hyper_mult' or key == 'hypermult' or key == 'hypermult_mod') and type(amount) == 'table' then
|
||||
if effect.card then juice_card(effect.card) end
|
||||
mult = mod_chips(mult:arrow(amount[1], amount[2]))
|
||||
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
|
||||
if not effect.remove_default_message then
|
||||
if from_edition then
|
||||
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = ((amount[1] > 5 and ('{' .. amount[1] .. '}') or string.rep('^', amount[1])) .. amount[2]).." Mult", colour = G.C.EDITION, edition = true})
|
||||
elseif key ~= 'hypermult_mod' then
|
||||
if effect.hypermult_message then
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.hypermult_message)
|
||||
else
|
||||
card_eval_status_text(scored_card or effect.card or effect.focus, 'hyper_mult', amount, percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
for _, v in ipairs({'e_mult', 'e_chips', 'ee_mult', 'ee_chips', 'eee_mult', 'eee_chips', 'hyper_mult', 'hyper_chips',
|
||||
'emult', 'echips', 'eemult', 'eechips', 'eeemult', 'eeechips', 'hypermult', 'hyperchips',
|
||||
'Emult_mod', 'Echip_mod', 'EEmult_mod', 'EEchip_mod', 'EEEmult_mod', 'EEEchip_mod', 'hypermult_mod', 'hyperchip_mod'}) do
|
||||
table.insert(SMODS.calculation_keys, v)
|
||||
end
|
||||
if not smods_xchips then
|
||||
for _, v in ipairs({'x_chips', 'xchips', 'Xchip_mod'}) do
|
||||
table.insert(SMODS.calculation_keys, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--some debugging functions
|
||||
--[[local callstep=0
|
||||
function printCallerInfo()
|
||||
|
@ -4470,3 +4393,79 @@ function EventManager:add_event(x,y,z)
|
|||
printCallerInfo()
|
||||
return emae(self,x,y,z)
|
||||
end--]]
|
||||
|
||||
require 'cartomancer.init'
|
||||
|
||||
Cartomancer.path = assert(
|
||||
Cartomancer.find_self('cartomancer.lua'),
|
||||
"Failed to find mod folder. Make sure that `Cartomancer` folder has `cartomancer.lua` file!"
|
||||
)
|
||||
|
||||
Cartomancer.load_mod_file('internal/config.lua', 'internal.config')
|
||||
Cartomancer.load_mod_file('internal/atlas.lua', 'internal.atlas')
|
||||
Cartomancer.load_mod_file('internal/ui.lua', 'internal.ui')
|
||||
Cartomancer.load_mod_file('internal/keybinds.lua', 'internal.keybinds')
|
||||
|
||||
Cartomancer.load_mod_file('core/view-deck.lua', 'core.view-deck')
|
||||
Cartomancer.load_mod_file('core/flames.lua', 'core.flames')
|
||||
Cartomancer.load_mod_file('core/optimizations.lua', 'core.optimizations')
|
||||
Cartomancer.load_mod_file('core/jokers.lua', 'core.jokers')
|
||||
Cartomancer.load_mod_file('core/hand.lua', 'core.hand')
|
||||
|
||||
Cartomancer.load_config()
|
||||
|
||||
Cartomancer.INTERNAL_jokers_menu = false
|
||||
|
||||
-- TODO dedicated keybinds file? keybinds need to load after config
|
||||
Cartomancer.register_keybind {
|
||||
name = 'hide_joker',
|
||||
func = function (controller)
|
||||
Cartomancer.hide_hovered_joker(controller)
|
||||
end
|
||||
}
|
||||
|
||||
Cartomancer.register_keybind {
|
||||
name = 'toggle_tags',
|
||||
func = function (controller)
|
||||
Cartomancer.SETTINGS.hide_tags = not Cartomancer.SETTINGS.hide_tags
|
||||
Cartomancer.update_tags_visibility()
|
||||
end
|
||||
}
|
||||
|
||||
Cartomancer.register_keybind {
|
||||
name = 'toggle_consumables',
|
||||
func = function (controller)
|
||||
Cartomancer.SETTINGS.hide_consumables = not Cartomancer.SETTINGS.hide_consumables
|
||||
end
|
||||
}
|
||||
|
||||
Cartomancer.register_keybind {
|
||||
name = 'toggle_deck',
|
||||
func = function (controller)
|
||||
Cartomancer.SETTINGS.hide_deck = not Cartomancer.SETTINGS.hide_deck
|
||||
end
|
||||
}
|
||||
|
||||
Cartomancer.register_keybind {
|
||||
name = 'toggle_jokers',
|
||||
func = function (controller)
|
||||
if not (G and G.jokers) then
|
||||
return
|
||||
end
|
||||
G.jokers.cart_hide_all = not G.jokers.cart_hide_all
|
||||
|
||||
if G.jokers.cart_hide_all then
|
||||
Cartomancer.hide_all_jokers()
|
||||
else
|
||||
Cartomancer.show_all_jokers()
|
||||
end
|
||||
Cartomancer.align_G_jokers()
|
||||
end
|
||||
}
|
||||
|
||||
Cartomancer.register_keybind {
|
||||
name = 'toggle_jokers_buttons',
|
||||
func = function (controller)
|
||||
Cartomancer.SETTINGS.jokers_controls_buttons = not Cartomancer.SETTINGS.jokers_controls_buttons
|
||||
end
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
LOVELY_INTEGRITY = '1051baf1f570241c507e107aff5fffefe3df969645f0fe53ec0856cd5ac0a760'
|
||||
LOVELY_INTEGRITY = '28c37f9c0f5cf94954059dedc38a88cb0f1dbceb28b6a40c9df225d84bb10fe5'
|
||||
|
||||
--Class
|
||||
Tag = Object:extend()
|
||||
|
@ -203,6 +203,14 @@ function Tag:apply_to_run(_context)
|
|||
return true
|
||||
end
|
||||
if self.name == 'Orbital Tag' then
|
||||
if (not self.ability.orbital_hand) or (not G.GAME.hands[self.ability.orbital_hand]) then
|
||||
local _poker_hands = {}
|
||||
for k, v in pairs(G.GAME.hands) do
|
||||
if v.visible then _poker_hands[#_poker_hands+1] = k end
|
||||
end
|
||||
|
||||
self.ability.orbital_hand = pseudorandom_element(_poker_hands, pseudoseed('orbital'))
|
||||
end
|
||||
update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {
|
||||
handname= self.ability.orbital_hand,
|
||||
chips = G.GAME.hands[self.ability.orbital_hand].chips,
|
||||
|
@ -723,7 +731,7 @@ function Tag:generate_UI(_size)
|
|||
return tag_sprite_tab, tag_sprite
|
||||
end
|
||||
|
||||
function Tag:get_uibox_table(tag_sprite, vars_only)
|
||||
function Tag:get_uibox_table(tag_sprite)
|
||||
tag_sprite = tag_sprite or self.tag_sprite
|
||||
local name_to_check, loc_vars = self.name, {}
|
||||
if name_to_check == 'Uncommon Tag' then
|
||||
|
@ -742,7 +750,6 @@ function Tag:get_uibox_table(tag_sprite, vars_only)
|
|||
}
|
||||
elseif name_to_check == "cry-Cat Tag" then loc_vars = {self.ability.level or 1}
|
||||
end
|
||||
if vars_only then return loc_vars end
|
||||
tag_sprite.ability_UIBox_table = generate_card_ui(G.P_TAGS[self.key], nil, loc_vars, (self.hide_ability) and 'Undiscovered' or 'Tag', nil, (self.hide_ability), nil, nil, self)
|
||||
return tag_sprite
|
||||
end
|
||||
|
|
1
smods-main/.gitattributes
vendored
1
smods-main/.gitattributes
vendored
|
@ -1 +0,0 @@
|
|||
* text=auto eol=lf
|
0
smods-main/.github/.keep
vendored
0
smods-main/.github/.keep
vendored
70
smods-main/.github/workflows/version-bumper.yml
vendored
70
smods-main/.github/workflows/version-bumper.yml
vendored
|
@ -1,70 +0,0 @@
|
|||
name: Version Bumper and Tagger
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
check-version-change:
|
||||
if: github.repository_owner == 'Steamopollys'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version_changed: ${{ steps.detect-version-change.outputs.version_changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2 # Fetch the current and previous commit for comparison
|
||||
|
||||
- name: Detect version change
|
||||
id: detect-version-change
|
||||
run: |
|
||||
echo ::set-output name=version_changed::$(git diff HEAD^ HEAD -- manifest.json | grep -q '"version_number":' && echo "true" || echo "false")
|
||||
|
||||
bump-and-tag-version:
|
||||
needs: check-version-change
|
||||
if: needs.check-version-change.outputs.version_changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Necessary for tagging to include all history
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
- name: Bump version and create tag
|
||||
run: |
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
# Load and parse manifest.json
|
||||
manifest_path = Path('manifest.json')
|
||||
manifest = json.loads(manifest_path.read_text())
|
||||
version_number = manifest['version_number']
|
||||
|
||||
# Update core/core.lua
|
||||
core_lua_path = Path('core/core.lua')
|
||||
core_lua_content = core_lua_path.read_text()
|
||||
core_lua_content = re.sub(r'MODDED_VERSION = "\d+\.\d+\.\d+-STEAMODDED"', f'MODDED_VERSION = "{version_number}-STEAMODDED"', core_lua_content)
|
||||
core_lua_path.write_text(core_lua_content)
|
||||
|
||||
# Output version number for later steps
|
||||
print(f"::set-output name=version_number::{version_number}")
|
||||
id: bump-version
|
||||
shell: python
|
||||
|
||||
- name: Commit and push if changed
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: "Automatically bump version to ${{ needs.bump-and-tag-version.outputs.version_number }}"
|
||||
file_pattern: core/core.lua injector.ps1
|
||||
|
||||
- name: Create and push tag
|
||||
if: steps.bump-version.outputs.version_number
|
||||
env:
|
||||
VERSION: ${{ steps.bump-version.outputs.version_number }}
|
||||
run: |
|
||||
git tag $VERSION
|
||||
git push origin $VERSION
|
||||
|
199
smods-main/.gitignore
vendored
199
smods-main/.gitignore
vendored
|
@ -1,199 +0,0 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||
|
||||
*~
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Python Patch ###
|
||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||
poetry.toml
|
||||
|
||||
# ruff
|
||||
.ruff_cache/
|
||||
|
||||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
## IntelliJ
|
||||
/.idea/
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
/httpRequests/
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
|
||||
/injector/steamodded_injector.dist/
|
||||
/injector/steamodded_injector.exe
|
||||
/steamodded_injector.build/
|
||||
/steamodded_injector.dist/
|
||||
/steamodded_injector.onefile-build/
|
||||
/steamodded_injector.exe
|
||||
|
||||
*.Identifier
|
||||
core/EditionAPI.lua
|
|
@ -1,674 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
@ -1,29 +0,0 @@
|
|||
# Steamodded - A Balatro ModLoader
|
||||
|
||||
## Introduction
|
||||
|
||||
Steamodded is a mod loader and injector for the game Balatro. Much like the [LÖVE framework](https://love2d.org/wiki/Main_Page) itself, it is built using [Lua](https://www.lua.org/). It is made with modularity and extensibility in mind, providing a large selection of APIs and other features to facilitate bringing your ideas to life.
|
||||
|
||||
## Installation
|
||||
|
||||
### How to Install Steamodded
|
||||
|
||||
Click [here](https://github.com/Steamopollys/Steamodded/wiki).
|
||||
|
||||
## How to Install a Mod
|
||||
|
||||
- Navigate to your Mods directory (see the installation instructions).
|
||||
- Put the mod into that directory. (The mod can be a single file if there is only one file provided, or it can be a whole folder.)
|
||||
- Launch the game and enjoy!
|
||||
|
||||
## Features
|
||||
|
||||
Documentation for this project is currently incomplete. A collection of documentation pages and guides that are currently available can be found [here](https://github.com/Steamopollys/Steamodded/wiki).
|
||||
|
||||
## Contributing
|
||||
|
||||
This project is open for contribution; feel free to open a pull request. If you are adding new features, providing documentation is highly appreciated.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the GNU General Public License. This ensures that the software is free to use, modify, and distribute. For more details, click [here](https://github.com/Steamopollys/Steamodded/actions?tab=GPL-3.0-1-ov-file)
|
|
@ -1,29 +0,0 @@
|
|||
# Documentation
|
||||
## To do
|
||||
|
||||
|
||||
- Challenge
|
||||
- DeckSkin
|
||||
|
||||
## In progress
|
||||
- Enhancement
|
||||
|
||||
## Done
|
||||
|
||||
- Achievement
|
||||
- Atlas
|
||||
- Center (Joker, Consumable, Voucher, Back, Booster)
|
||||
- Blind
|
||||
- ObjectType
|
||||
- UndiscoveredSprite
|
||||
- Edition
|
||||
- Language
|
||||
- Sound
|
||||
- Stake
|
||||
- PokerHand
|
||||
- Suit
|
||||
- Rank
|
||||
- Seal
|
||||
- Sticker
|
||||
- Rarity
|
||||
- Tag
|
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
return {
|
||||
["no_mod_badges"] = false,
|
||||
["graphics_mipmap_level"] = 3,
|
||||
["graphics_mipmap_level_options"] = {0, 2, 4, 8},
|
||||
["achievements"] = 1,
|
||||
["seeded_unlocks"] = false,
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 54 KiB |
|
@ -1 +0,0 @@
|
|||
return require((...) .. '.json')
|
|
@ -1,388 +0,0 @@
|
|||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
|
@ -1,7 +0,0 @@
|
|||
Copyright 2020 megagrump@pm.me
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,94 +0,0 @@
|
|||
# This is a Thunderstore package mirror of EngineerSmith's `nativefs` repository with an added lovely patch.
|
||||
|
||||
...
|
||||
|
||||
This is a re-host as previously it was taken down by the original developer. Pull requests for fixes are welcome!
|
||||
|
||||
# Native filesystem for LÖVE
|
||||
|
||||
nativefs replicates a subset of the [love.filesystem](https://love2d.org/wiki/love.filesystem) API, but without LÖVE's path restrictions.
|
||||
|
||||
## Available functions
|
||||
|
||||
### nativefs
|
||||
|
||||
Links in this list point to their `love.filesystem` counterparts. All functions are designed to behave the same way as `love.filesystem`, but without the path restrictions that LÖVE imposes; i.e., they allow full access to the filesystem.
|
||||
|
||||
* [nativefs.newFile](https://love2d.org/wiki/love.filesystem.newFile)
|
||||
* [nativefs.newFileData](https://love2d.org/wiki/love.filesystem.newFileData)
|
||||
* [nativefs.mount](https://love2d.org/wiki/love.filesystem.mount)
|
||||
* [nativefs.unmount](https://love2d.org/wiki/love.filesystem.unmount)
|
||||
* [nativefs.read](https://love2d.org/wiki/love.filesystem.read)
|
||||
* [nativefs.write](https://love2d.org/wiki/love.filesystem.write)
|
||||
* [nativefs.append](https://love2d.org/wiki/love.filesystem.append)
|
||||
* [nativefs.lines](https://love2d.org/wiki/love.filesystem.lines)
|
||||
* [nativefs.load](https://love2d.org/wiki/love.filesystem.load)
|
||||
* [nativefs.getWorkingDirectory](https://love2d.org/wiki/love.filesystem.getWorkingDirectory)
|
||||
* [nativefs.getDirectoryItems](https://love2d.org/wiki/love.filesystem.getDirectoryItems)
|
||||
* [nativefs.getInfo](https://love2d.org/wiki/love.filesystem.getInfo)
|
||||
* [nativefs.createDirectory](https://love2d.org/wiki/love.filesystem.createDirectory)
|
||||
* [nativefs.remove](https://love2d.org/wiki/love.filesystem.remove)
|
||||
* nativefs.getDirectoryItemsInfo
|
||||
* nativefs.getDriveList
|
||||
* nativefs.setWorkingDirectory
|
||||
|
||||
#### Additional `nativefs` functions
|
||||
|
||||
Functions that are not available in `love.filesystem`:
|
||||
|
||||
* `nativefs.getDirectoryItemsInfo(path)`
|
||||
Returns a list of items in a directory that contains not only the names, but also the information returned by `getInfo` for each item. The return value is a list of files and directories, in which each entry is a table as returned by [getInfo](https://love2d.org/wiki/love.filesystem.getInfo), with an additional `name` field for each entry. Using this function is faster than calling `getInfo` separately for each item.
|
||||
|
||||
* `nativefs.getDriveList()`
|
||||
Returns a table with all populated drive letters on Windows (`{ 'C:/', 'D:/', ...}`). On systems that don't use drive letters, a table with the single entry `/` is returned.
|
||||
|
||||
* `nativefs.setWorkingDirectory(directory)`
|
||||
Changes the working directory.
|
||||
|
||||
### File
|
||||
|
||||
`nativefs.newFile` returns a `File` object that provides these functions:
|
||||
|
||||
* [File:open](https://love2d.org/wiki/\(File\):open)
|
||||
* [File:close](https://love2d.org/wiki/\(File\):close)
|
||||
* [File:read](https://love2d.org/wiki/\(File\):read)
|
||||
* [File:write](https://love2d.org/wiki/\(File\):write)
|
||||
* [File:lines](https://love2d.org/wiki/\(File\):lines)
|
||||
* [File:isOpen](https://love2d.org/wiki/\(File\):isOpen)
|
||||
* [File:isEOF](https://love2d.org/wiki/\(File\):isEOF)
|
||||
* [File:getFilename](https://love2d.org/wiki/\(File\):getFilename)
|
||||
* [File:getMode](https://love2d.org/wiki/\(File\):getMode)
|
||||
* [File:getBuffer](https://love2d.org/wiki/\(File\):getBuffer)
|
||||
* [File:setBuffer](https://love2d.org/wiki/\(File\):setBuffer)
|
||||
* [File:getSize](https://love2d.org/wiki/\(File\):getSize)
|
||||
* [File:seek](https://love2d.org/wiki/\(File\):seek)
|
||||
* [File:tell](https://love2d.org/wiki/\(File\):tell)
|
||||
* [File:flush](https://love2d.org/wiki/\(File\):flush)
|
||||
* [File:type](https://love2d.org/wiki/Object:type)
|
||||
* [File:typeOf](https://love2d.org/wiki/Object:typeOf)
|
||||
* [File:release](https://love2d.org/wiki/Object:release)
|
||||
|
||||
Function names in this list are links to their LÖVE `File` counterparts. `File` is designed to work the same way as LÖVE's `File` objects.
|
||||
|
||||
## Example
|
||||
|
||||
```lua
|
||||
local nativefs = require("nativefs")
|
||||
|
||||
-- Prints all information on files in C:\Windows
|
||||
local files = nativefs.getDirectoryItemsInfo("C:/Windows")
|
||||
for i = 1, #files do
|
||||
if files[i].type == "file" then
|
||||
local info = nativefs.getInfo("C:/Windows/" .. files[i].name)
|
||||
print(i .. ": " .. files[i] .. " : Type:" .. info.type .. ", Size:" .. tostring(info.size) .. ", last modified:" .. tostring(info.modtime))
|
||||
end
|
||||
end
|
||||
```
|
||||
## License
|
||||
Copyright 2020 megagrump@pm.me
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
Binary file not shown.
Before Width: | Height: | Size: 138 KiB |
|
@ -1 +0,0 @@
|
|||
return require((...) .. '.nativefs')
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"name": "nativefs",
|
||||
"version_number": "1.0.0",
|
||||
"website_url": "https://github.com/EngineerSmith/nativefs",
|
||||
"description": "nativefs replicates a subset of the love.filesystem API, but without LÖVE's path restrictions.",
|
||||
"dependencies": ["Thunderstore-lovely-0.3.0"]
|
||||
}
|
|
@ -1,495 +0,0 @@
|
|||
--[[
|
||||
Copyright 2020 megagrump@pm.me
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]--
|
||||
|
||||
-- module("nativefs", package.seeall)
|
||||
|
||||
local ffi, bit = require('ffi'), require('bit')
|
||||
local C = ffi.C
|
||||
|
||||
local fopen, getcwd, chdir, unlink, mkdir, rmdir
|
||||
local BUFFERMODE, MODEMAP
|
||||
local ByteArray = ffi.typeof('unsigned char[?]')
|
||||
local function _ptr(p) return p ~= nil and p or nil end -- NULL pointer to nil
|
||||
|
||||
local File = {
|
||||
getBuffer = function(self) return self._bufferMode, self._bufferSize end,
|
||||
getFilename = function(self) return self._name end,
|
||||
getMode = function(self) return self._mode end,
|
||||
isOpen = function(self) return self._mode ~= 'c' and self._handle ~= nil end,
|
||||
}
|
||||
|
||||
function File:open(mode)
|
||||
if self._mode ~= 'c' then return false, "File " .. self._name .. " is already open" end
|
||||
if not MODEMAP[mode] then return false, "Invalid open mode for " .. self._name .. ": " .. mode end
|
||||
|
||||
local handle = _ptr(fopen(self._name, MODEMAP[mode]))
|
||||
if not handle then return false, "Could not open " .. self._name .. " in mode " .. mode end
|
||||
|
||||
self._handle, self._mode = ffi.gc(handle, C.fclose), mode
|
||||
self:setBuffer(self._bufferMode, self._bufferSize)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function File:close()
|
||||
if self._mode == 'c' then return false, "File is not open" end
|
||||
C.fclose(ffi.gc(self._handle, nil))
|
||||
self._handle, self._mode = nil, 'c'
|
||||
return true
|
||||
end
|
||||
|
||||
function File:setBuffer(mode, size)
|
||||
local bufferMode = BUFFERMODE[mode]
|
||||
if not bufferMode then
|
||||
return false, "Invalid buffer mode " .. mode .. " (expected 'none', 'full', or 'line')"
|
||||
end
|
||||
|
||||
if mode == 'none' then
|
||||
size = math.max(0, size or 0)
|
||||
else
|
||||
size = math.max(2, size or 2) -- Windows requires buffer to be at least 2 bytes
|
||||
end
|
||||
|
||||
local success = self._mode == 'c' or C.setvbuf(self._handle, nil, bufferMode, size) == 0
|
||||
if not success then
|
||||
self._bufferMode, self._bufferSize = 'none', 0
|
||||
return false, "Could not set buffer mode"
|
||||
end
|
||||
|
||||
self._bufferMode, self._bufferSize = mode, size
|
||||
return true
|
||||
end
|
||||
|
||||
function File:getSize()
|
||||
-- NOTE: The correct way to do this would be a stat() call, which requires a
|
||||
-- lot more (system-specific) code. This is a shortcut that requires the file
|
||||
-- to be readable.
|
||||
local mustOpen = not self:isOpen()
|
||||
if mustOpen and not self:open('r') then return 0 end
|
||||
|
||||
local pos = mustOpen and 0 or self:tell()
|
||||
C.fseek(self._handle, 0, 2)
|
||||
local size = self:tell()
|
||||
if mustOpen then
|
||||
self:close()
|
||||
else
|
||||
self:seek(pos)
|
||||
end
|
||||
return size
|
||||
end
|
||||
|
||||
function File:read(containerOrBytes, bytes)
|
||||
if self._mode ~= 'r' then return nil, 0 end
|
||||
|
||||
local container = bytes ~= nil and containerOrBytes or 'string'
|
||||
if container ~= 'string' and container ~= 'data' then
|
||||
error("Invalid container type: " .. container)
|
||||
end
|
||||
|
||||
bytes = not bytes and containerOrBytes or 'all'
|
||||
bytes = bytes == 'all' and self:getSize() - self:tell() or math.min(self:getSize() - self:tell(), bytes)
|
||||
|
||||
if bytes <= 0 then
|
||||
local data = container == 'string' and '' or love.data.newFileData('', self._name)
|
||||
return data, 0
|
||||
end
|
||||
|
||||
local data = love.data.newByteData(bytes)
|
||||
local r = tonumber(C.fread(data:getFFIPointer(), 1, bytes, self._handle))
|
||||
|
||||
if container == 'data' then
|
||||
-- FileData from ByteData requires LÖVE 11.4+
|
||||
local ok, fd = pcall(love.filesystem.newFileData, data, self._name)
|
||||
if ok then return fd end
|
||||
end
|
||||
|
||||
local str = data:getString()
|
||||
data:release()
|
||||
data = container == 'data' and love.filesystem.newFileData(str, self._name) or str
|
||||
return data, r
|
||||
end
|
||||
|
||||
local function lines(file, autoclose)
|
||||
local BUFFERSIZE = 4096
|
||||
local buffer, bufferPos = ByteArray(BUFFERSIZE), 0
|
||||
local bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle))
|
||||
|
||||
local offset = file:tell()
|
||||
return function()
|
||||
file:seek(offset)
|
||||
|
||||
local line = {}
|
||||
while bytesRead > 0 do
|
||||
for i = bufferPos, bytesRead - 1 do
|
||||
if buffer[i] == 10 then -- end of line
|
||||
bufferPos = i + 1
|
||||
return table.concat(line)
|
||||
end
|
||||
|
||||
if buffer[i] ~= 13 then -- ignore CR
|
||||
table.insert(line, string.char(buffer[i]))
|
||||
end
|
||||
end
|
||||
|
||||
bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle))
|
||||
offset, bufferPos = offset + bytesRead, 0
|
||||
end
|
||||
|
||||
if not line[1] then
|
||||
if autoclose then file:close() end
|
||||
return nil
|
||||
end
|
||||
return table.concat(line)
|
||||
end
|
||||
end
|
||||
|
||||
function File:lines()
|
||||
if self._mode ~= 'r' then error("File is not opened for reading") end
|
||||
return lines(self)
|
||||
end
|
||||
|
||||
function File:write(data, size)
|
||||
if self._mode ~= 'w' and self._mode ~= 'a' then
|
||||
return false, "File " .. self._name .. " not opened for writing"
|
||||
end
|
||||
|
||||
local toWrite, writeSize
|
||||
if type(data) == 'string' then
|
||||
writeSize = (size == nil or size == 'all') and #data or size
|
||||
toWrite = data
|
||||
else
|
||||
writeSize = (size == nil or size == 'all') and data:getSize() or size
|
||||
toWrite = data:getFFIPointer()
|
||||
end
|
||||
|
||||
if tonumber(C.fwrite(toWrite, 1, writeSize, self._handle)) ~= writeSize then
|
||||
return false, "Could not write data"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function File:seek(pos)
|
||||
return self._handle and C.fseek(self._handle, pos, 0) == 0
|
||||
end
|
||||
|
||||
function File:tell()
|
||||
if not self._handle then return nil, "Invalid position" end
|
||||
return tonumber(C.ftell(self._handle))
|
||||
end
|
||||
|
||||
function File:flush()
|
||||
if self._mode ~= 'w' and self._mode ~= 'a' then
|
||||
return nil, "File is not opened for writing"
|
||||
end
|
||||
return C.fflush(self._handle) == 0
|
||||
end
|
||||
|
||||
function File:isEOF()
|
||||
return not self:isOpen() or C.feof(self._handle) ~= 0 or self:tell() == self:getSize()
|
||||
end
|
||||
|
||||
function File:release()
|
||||
if self._mode ~= 'c' then self:close() end
|
||||
self._handle = nil
|
||||
end
|
||||
|
||||
function File:type() return 'File' end
|
||||
|
||||
function File:typeOf(t) return t == 'File' end
|
||||
|
||||
File.__index = File
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
local nativefs = {}
|
||||
local loveC = ffi.os == 'Windows' and ffi.load('love') or C
|
||||
|
||||
function nativefs.newFile(name)
|
||||
if type(name) ~= 'string' then
|
||||
error("bad argument #1 to 'newFile' (string expected, got " .. type(name) .. ")")
|
||||
end
|
||||
return setmetatable({
|
||||
_name = name,
|
||||
_mode = 'c',
|
||||
_handle = nil,
|
||||
_bufferSize = 0,
|
||||
_bufferMode = 'none'
|
||||
}, File)
|
||||
end
|
||||
|
||||
function nativefs.newFileData(filepath)
|
||||
local f = nativefs.newFile(filepath)
|
||||
local ok, err = f:open('r')
|
||||
if not ok then return nil, err end
|
||||
|
||||
local data, err = f:read('data', 'all')
|
||||
f:close()
|
||||
return data, err
|
||||
end
|
||||
|
||||
function nativefs.mount(archive, mountPoint, appendToPath)
|
||||
return loveC.PHYSFS_mount(archive, mountPoint, appendToPath and 1 or 0) ~= 0
|
||||
end
|
||||
|
||||
function nativefs.unmount(archive)
|
||||
return loveC.PHYSFS_unmount(archive) ~= 0
|
||||
end
|
||||
|
||||
function nativefs.read(containerOrName, nameOrSize, sizeOrNil)
|
||||
local container, name, size
|
||||
if sizeOrNil then
|
||||
container, name, size = containerOrName, nameOrSize, sizeOrNil
|
||||
elseif not nameOrSize then
|
||||
container, name, size = 'string', containerOrName, 'all'
|
||||
else
|
||||
if type(nameOrSize) == 'number' or nameOrSize == 'all' then
|
||||
container, name, size = 'string', containerOrName, nameOrSize
|
||||
else
|
||||
container, name, size = containerOrName, nameOrSize, 'all'
|
||||
end
|
||||
end
|
||||
|
||||
local file = nativefs.newFile(name)
|
||||
local ok, err = file:open('r')
|
||||
if not ok then return nil, err end
|
||||
|
||||
local data, size = file:read(container, size)
|
||||
file:close()
|
||||
return data, size
|
||||
end
|
||||
|
||||
local function writeFile(mode, name, data, size)
|
||||
local file = nativefs.newFile(name)
|
||||
local ok, err = file:open(mode)
|
||||
if not ok then return nil, err end
|
||||
|
||||
ok, err = file:write(data, size or 'all')
|
||||
file:close()
|
||||
return ok, err
|
||||
end
|
||||
|
||||
function nativefs.write(name, data, size)
|
||||
return writeFile('w', name, data, size)
|
||||
end
|
||||
|
||||
function nativefs.append(name, data, size)
|
||||
return writeFile('a', name, data, size)
|
||||
end
|
||||
|
||||
function nativefs.lines(name)
|
||||
local f = nativefs.newFile(name)
|
||||
local ok, err = f:open('r')
|
||||
if not ok then return nil, err end
|
||||
return lines(f, true)
|
||||
end
|
||||
|
||||
function nativefs.load(name)
|
||||
local chunk, err = nativefs.read(name)
|
||||
if not chunk then return nil, err end
|
||||
return loadstring(chunk, name)
|
||||
end
|
||||
|
||||
function nativefs.getWorkingDirectory()
|
||||
return getcwd()
|
||||
end
|
||||
|
||||
function nativefs.setWorkingDirectory(path)
|
||||
if not chdir(path) then return false, "Could not set working directory" end
|
||||
return true
|
||||
end
|
||||
|
||||
function nativefs.getDriveList()
|
||||
if ffi.os ~= 'Windows' then return { '/' } end
|
||||
local drives, bits = {}, C.GetLogicalDrives()
|
||||
for i = 0, 25 do
|
||||
if bit.band(bits, 2 ^ i) > 0 then
|
||||
table.insert(drives, string.char(65 + i) .. ':/')
|
||||
end
|
||||
end
|
||||
return drives
|
||||
end
|
||||
|
||||
function nativefs.createDirectory(path)
|
||||
local current = path:sub(1, 1) == '/' and '/' or ''
|
||||
for dir in path:gmatch('[^/\\]+') do
|
||||
current = current .. dir .. '/'
|
||||
local info = nativefs.getInfo(current, 'directory')
|
||||
if not info and not mkdir(current) then return false, "Could not create directory " .. current end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function nativefs.remove(name)
|
||||
local info = nativefs.getInfo(name)
|
||||
if not info then return false, "Could not remove " .. name end
|
||||
if info.type == 'directory' then
|
||||
if not rmdir(name) then return false, "Could not remove directory " .. name end
|
||||
return true
|
||||
end
|
||||
if not unlink(name) then return false, "Could not remove file " .. name end
|
||||
return true
|
||||
end
|
||||
|
||||
local function withTempMount(dir, fn, ...)
|
||||
local mountPoint = _ptr(loveC.PHYSFS_getMountPoint(dir))
|
||||
if mountPoint then return fn(ffi.string(mountPoint), ...) end
|
||||
if not nativefs.mount(dir, '__nativefs__temp__') then return false, "Could not mount " .. dir end
|
||||
local a, b = fn('__nativefs__temp__', ...)
|
||||
nativefs.unmount(dir)
|
||||
return a, b
|
||||
end
|
||||
|
||||
function nativefs.getDirectoryItems(dir)
|
||||
local result, err = withTempMount(dir, love.filesystem.getDirectoryItems)
|
||||
return result or {}
|
||||
end
|
||||
|
||||
local function getDirectoryItemsInfo(path, filtertype)
|
||||
local items = {}
|
||||
local files = love.filesystem.getDirectoryItems(path)
|
||||
for i = 1, #files do
|
||||
local filepath = string.format('%s/%s', path, files[i])
|
||||
local info = love.filesystem.getInfo(filepath, filtertype)
|
||||
if info then
|
||||
info.name = files[i]
|
||||
table.insert(items, info)
|
||||
end
|
||||
end
|
||||
return items
|
||||
end
|
||||
|
||||
function nativefs.getDirectoryItemsInfo(path, filtertype)
|
||||
local result, err = withTempMount(path, getDirectoryItemsInfo, filtertype)
|
||||
return result or {}
|
||||
end
|
||||
|
||||
local function getInfo(path, file, filtertype)
|
||||
local filepath = string.format('%s/%s', path, file)
|
||||
return love.filesystem.getInfo(filepath, filtertype)
|
||||
end
|
||||
|
||||
local function leaf(p)
|
||||
p = p:gsub('\\', '/')
|
||||
local last, a = p, 1
|
||||
while a do
|
||||
a = p:find('/', a + 1)
|
||||
if a then
|
||||
last = p:sub(a + 1)
|
||||
end
|
||||
end
|
||||
return last
|
||||
end
|
||||
|
||||
function nativefs.getInfo(path, filtertype)
|
||||
local dir = path:match("(.*[\\/]).*$") or './'
|
||||
local file = leaf(path)
|
||||
local result, err = withTempMount(dir, getInfo, file, filtertype)
|
||||
return result or nil
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
MODEMAP = { r = 'rb', w = 'wb', a = 'ab' }
|
||||
local MAX_PATH = 4096
|
||||
|
||||
ffi.cdef([[
|
||||
int PHYSFS_mount(const char* dir, const char* mountPoint, int appendToPath);
|
||||
int PHYSFS_unmount(const char* dir);
|
||||
const char* PHYSFS_getMountPoint(const char* dir);
|
||||
|
||||
typedef struct FILE FILE;
|
||||
|
||||
FILE* fopen(const char* path, const char* mode);
|
||||
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
|
||||
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
|
||||
int fclose(FILE* stream);
|
||||
int fflush(FILE* stream);
|
||||
size_t fseek(FILE* stream, size_t offset, int whence);
|
||||
size_t ftell(FILE* stream);
|
||||
int setvbuf(FILE* stream, char* buffer, int mode, size_t size);
|
||||
int feof(FILE* stream);
|
||||
]])
|
||||
|
||||
if ffi.os == 'Windows' then
|
||||
ffi.cdef([[
|
||||
int MultiByteToWideChar(unsigned int cp, uint32_t flags, const char* mb, int cmb, const wchar_t* wc, int cwc);
|
||||
int WideCharToMultiByte(unsigned int cp, uint32_t flags, const wchar_t* wc, int cwc, const char* mb,
|
||||
int cmb, const char* def, int* used);
|
||||
int GetLogicalDrives(void);
|
||||
int CreateDirectoryW(const wchar_t* path, void*);
|
||||
int _wchdir(const wchar_t* path);
|
||||
wchar_t* _wgetcwd(wchar_t* buffer, int maxlen);
|
||||
FILE* _wfopen(const wchar_t* path, const wchar_t* mode);
|
||||
int _wunlink(const wchar_t* path);
|
||||
int _wrmdir(const wchar_t* path);
|
||||
]])
|
||||
|
||||
BUFFERMODE = { full = 0, line = 64, none = 4 }
|
||||
|
||||
local function towidestring(str)
|
||||
local size = C.MultiByteToWideChar(65001, 0, str, #str, nil, 0)
|
||||
local buf = ffi.new('wchar_t[?]', size + 1)
|
||||
C.MultiByteToWideChar(65001, 0, str, #str, buf, size)
|
||||
return buf
|
||||
end
|
||||
|
||||
local function toutf8string(wstr)
|
||||
local size = C.WideCharToMultiByte(65001, 0, wstr, -1, nil, 0, nil, nil)
|
||||
local buf = ffi.new('char[?]', size + 1)
|
||||
C.WideCharToMultiByte(65001, 0, wstr, -1, buf, size, nil, nil)
|
||||
return ffi.string(buf)
|
||||
end
|
||||
|
||||
local nameBuffer = ffi.new('wchar_t[?]', MAX_PATH + 1)
|
||||
|
||||
fopen = function(path, mode) return C._wfopen(towidestring(path), towidestring(mode)) end
|
||||
getcwd = function() return toutf8string(C._wgetcwd(nameBuffer, MAX_PATH)) end
|
||||
chdir = function(path) return C._wchdir(towidestring(path)) == 0 end
|
||||
unlink = function(path) return C._wunlink(towidestring(path)) == 0 end
|
||||
mkdir = function(path) return C.CreateDirectoryW(towidestring(path), nil) ~= 0 end
|
||||
rmdir = function(path) return C._wrmdir(towidestring(path)) == 0 end
|
||||
else
|
||||
BUFFERMODE = { full = 0, line = 1, none = 2 }
|
||||
|
||||
ffi.cdef([[
|
||||
char* getcwd(char *buffer, int maxlen);
|
||||
int chdir(const char* path);
|
||||
int unlink(const char* path);
|
||||
int mkdir(const char* path, int mode);
|
||||
int rmdir(const char* path);
|
||||
]])
|
||||
|
||||
local nameBuffer = ByteArray(MAX_PATH)
|
||||
|
||||
fopen = C.fopen
|
||||
unlink = function(path) return ffi.C.unlink(path) == 0 end
|
||||
chdir = function(path) return ffi.C.chdir(path) == 0 end
|
||||
mkdir = function(path) return ffi.C.mkdir(path, 0x1ed) == 0 end
|
||||
rmdir = function(path) return ffi.C.rmdir(path) == 0 end
|
||||
|
||||
getcwd = function()
|
||||
local cwd = _ptr(C.getcwd(nameBuffer, MAX_PATH))
|
||||
return cwd and ffi.string(cwd) or nil
|
||||
end
|
||||
end
|
||||
|
||||
return nativefs
|
|
@ -1,132 +0,0 @@
|
|||
return {
|
||||
descriptions = {
|
||||
Other = {
|
||||
load_success = {
|
||||
text = {
|
||||
'Mod loaded',
|
||||
'{C:green}successfully!'
|
||||
}
|
||||
},
|
||||
load_failure_d = {
|
||||
text = {
|
||||
'Missing {C:attention}dependencies!',
|
||||
'#1#',
|
||||
}
|
||||
},
|
||||
load_failure_c = {
|
||||
text = {
|
||||
'Unresolved {C:attention}conflicts!',
|
||||
'#1#'
|
||||
}
|
||||
},
|
||||
load_failure_d_c = {
|
||||
text = {
|
||||
'Missing {C:attention}dependencies!',
|
||||
'#1#',
|
||||
'Unresolved {C:attention}conflicts!',
|
||||
'#2#'
|
||||
}
|
||||
},
|
||||
load_failure_o = {
|
||||
text = {
|
||||
'{C:attention}Outdated!{} Steamodded',
|
||||
'versions {C:money}0.9.8{} and below',
|
||||
'are no longer supported.'
|
||||
}
|
||||
},
|
||||
load_failure_i = {
|
||||
text = {
|
||||
'{C:attention}Incompatible!{} Needs version',
|
||||
'#1# of Steamodded,',
|
||||
'but #2# is installed.'
|
||||
}
|
||||
},
|
||||
load_failure_p = {
|
||||
text = {
|
||||
'{C:attention}Prefix Conflict!{}',
|
||||
'This mod\'s prefix is',
|
||||
'the same as another mod\'s.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_failure_m = {
|
||||
text = {
|
||||
'{C:attention}Main File Not Found!{}',
|
||||
'This mod\'s main file',
|
||||
'could not be found.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_disabled = {
|
||||
text = {
|
||||
'This mod has been',
|
||||
'{C:attention}disabled!{}'
|
||||
}
|
||||
}
|
||||
},
|
||||
Edition = {
|
||||
e_negative_playing_card = {
|
||||
name = "Negative",
|
||||
text = {
|
||||
"{C:dark_edition}+#1#{} hand size"
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
misc = {
|
||||
achievement_names = {
|
||||
hidden_achievement = "???",
|
||||
},
|
||||
achievement_descriptions = {
|
||||
hidden_achievement = "Play more to find out!",
|
||||
},
|
||||
dictionary = {
|
||||
b_mods = 'Mods',
|
||||
b_mods_cap = 'MODS',
|
||||
b_modded_version = 'Modded Version!',
|
||||
b_steamodded = 'Steamodded',
|
||||
b_credits = 'Credits',
|
||||
b_open_mods_dir = 'Open Mods directory',
|
||||
b_no_mods = 'No mods have been detected...',
|
||||
b_mod_list = 'List of Activated Mods',
|
||||
b_mod_loader = 'Mod Loader',
|
||||
b_developed_by = 'developed by ',
|
||||
b_rewrite_by = 'Rewrite by ',
|
||||
b_github_project = 'Github Project',
|
||||
b_github_bugs_1 = 'You can report bugs and',
|
||||
b_github_bugs_2 = 'submit contributions there.',
|
||||
b_disable_mod_badges = 'Disable Mod Badges',
|
||||
b_author = 'Author',
|
||||
b_authors = 'Authors',
|
||||
b_unknown = 'Unknown',
|
||||
b_lovely_mod = '(Lovely Mod) ',
|
||||
b_by = ' By: ',
|
||||
b_config = "Config",
|
||||
b_additions = 'Additions',
|
||||
b_stickers = 'Stickers',
|
||||
b_achievements = "Achievements",
|
||||
b_applies_stakes_1 = 'Applies ',
|
||||
b_applies_stakes_2 = '',
|
||||
b_graphics_mipmap_level = "Mipmap level",
|
||||
b_browse = 'Browse',
|
||||
b_search_prompt = 'Search for mods',
|
||||
b_search_button = 'Search',
|
||||
b_seeded_unlocks = 'Seeded unlocks',
|
||||
b_seeded_unlocks_info = 'Enable unlocks and discoveries in seeded runs',
|
||||
ml_achievement_settings = {
|
||||
'Disabled',
|
||||
'Enabled',
|
||||
'Bypass Restrictions'
|
||||
},
|
||||
b_deckskins_lc = 'Low Contrast Colors',
|
||||
b_deckskins_hc = 'High Contrast Colors',
|
||||
b_deckskins_def = 'Default Colors',
|
||||
},
|
||||
v_dictionary = {
|
||||
c_types = '#1# Types',
|
||||
cashout_hidden = '...and #1# more',
|
||||
a_xchips = "X#1# Chips",
|
||||
a_xchips_minus = "-X#1# Chips",
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
return {
|
||||
descriptions = {
|
||||
Other = {
|
||||
load_success = {
|
||||
text = {
|
||||
'¡Mod cargado',
|
||||
'{C:green}con éxito{}!'
|
||||
}
|
||||
},
|
||||
load_failure_d = {
|
||||
text = {
|
||||
'¡Faltan {C:attention}dependencias{}!',
|
||||
'#1#',
|
||||
}
|
||||
},
|
||||
load_failure_c = {
|
||||
text = {
|
||||
'¡Hay {C:attention}conflictos{} sin resolver!',
|
||||
'#1#'
|
||||
}
|
||||
},
|
||||
load_failure_d_c = {
|
||||
text = {
|
||||
'¡Faltan {C:attention}dependencias!',
|
||||
'#1#',
|
||||
'¡Hay {C:attention}conflictos{} sin resolver!',
|
||||
'#2#'
|
||||
}
|
||||
},
|
||||
load_failure_o = {
|
||||
text = {
|
||||
'¡Steamodded {C:attention}obsoleto{}!',
|
||||
'Las versiones por debajo de {C:money}0.9.8{}',
|
||||
'ya no tienen soporte.'
|
||||
}
|
||||
},
|
||||
load_failure_i = {
|
||||
text = {
|
||||
'{C:attention}¡Incompatible!{} Necesita la versión',
|
||||
'#1# de Steamodded,',
|
||||
'pero la #2# está instalada.'
|
||||
}
|
||||
},
|
||||
load_failure_p = { -- To be translated
|
||||
text = {
|
||||
'{C:attention}Prefix Conflict!{}',
|
||||
'This mod\'s prefix is',
|
||||
'the same as another mod\'s.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_failure_m = { -- To be translated
|
||||
text = {
|
||||
'{C:attention}Main File Not Found!{}',
|
||||
'This mod\'s main file',
|
||||
'could not be found.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_disabled = {
|
||||
text = {
|
||||
'¡Este mod ha sido',
|
||||
'{C:attention}desactivado{}!'
|
||||
}
|
||||
}
|
||||
},
|
||||
Edition = {
|
||||
e_negative_playing_card = {
|
||||
name = "Negativa",
|
||||
text = {
|
||||
"{C:dark_edition}+#1#{} de tamaño de mano"
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
misc = {
|
||||
achievement_names = {
|
||||
hidden_achievement = "???",
|
||||
},
|
||||
achievement_descriptions = {
|
||||
hidden_achievement = "¡Juega más para descubirlo!",
|
||||
},
|
||||
dictionary = {
|
||||
b_mods = 'Mods',
|
||||
b_mods_cap = 'MODS',
|
||||
b_modded_version = 'Modded Version!', -- To be translated
|
||||
b_steamodded = 'Steamodded',
|
||||
b_credits = 'Créditos',
|
||||
b_open_mods_dir = 'Abrir directorio de Mods',
|
||||
b_no_mods = 'No se han detectado mods...',
|
||||
b_mod_list = 'Lista de Mods activos',
|
||||
b_mod_loader = 'Cargador de Mods',
|
||||
b_developed_by = 'desarrollado por ',
|
||||
b_rewrite_by = 'Reescrito por ',
|
||||
b_github_project = 'Proyecto de Github',
|
||||
b_github_bugs_1 = 'Puedes reportar errores',
|
||||
b_github_bugs_2 = 'y contribuir allí.',
|
||||
b_disable_mod_badges = 'Desactivar insignias de mods',
|
||||
b_author = 'Autor/a',
|
||||
b_authors = 'Autores',
|
||||
b_unknown = 'Desconocido',
|
||||
b_lovely_mod = '(Lovely Mod) ', -- TODO
|
||||
b_by = ' Por: ',
|
||||
b_config = "Configuración",
|
||||
b_additions = 'Adiciones',
|
||||
b_stickers = 'Stickers', -- TODO
|
||||
b_achievements = "Logros",
|
||||
b_applies_stakes_1 = 'Aplica ',
|
||||
b_applies_stakes_2 = '',
|
||||
b_graphics_mipmap_level = "Mipmap level", -- TODO
|
||||
},
|
||||
v_dictionary = {
|
||||
c_types = '#1# Tipos',
|
||||
cashout_hidden = '...y #1# más',
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
return {
|
||||
descriptions = {
|
||||
Other = {
|
||||
load_success = {
|
||||
text = {
|
||||
'¡Mod cargado',
|
||||
'{C:green}con éxito{}!'
|
||||
}
|
||||
},
|
||||
load_failure_d = {
|
||||
text = {
|
||||
'¡Faltan {C:attention}dependencias{}!',
|
||||
'#1#',
|
||||
}
|
||||
},
|
||||
load_failure_c = {
|
||||
text = {
|
||||
'¡Hay {C:attention}conflictos{} sin resolver!',
|
||||
'#1#'
|
||||
}
|
||||
},
|
||||
load_failure_d_c = {
|
||||
text = {
|
||||
'¡Faltan {C:attention}dependencias!',
|
||||
'#1#',
|
||||
'¡Hay {C:attention}conflictos{} sin resolver!',
|
||||
'#2#'
|
||||
}
|
||||
},
|
||||
load_failure_o = {
|
||||
text = {
|
||||
'¡Steamodded {C:attention}obsoleto{}!',
|
||||
'Las versiones por debajo de {C:money}0.9.8{}',
|
||||
'ya no tienen soporte.'
|
||||
}
|
||||
},
|
||||
load_failure_i = {
|
||||
text = {
|
||||
'{C:attention}¡Incompatible!{} Necesita la versión',
|
||||
'#1# de Steamodded,',
|
||||
'pero la #2# está instalada.'
|
||||
}
|
||||
},
|
||||
load_failure_p = { -- To be translated
|
||||
text = {
|
||||
'{C:attention}Prefix Conflict!{}',
|
||||
'This mod\'s prefix is',
|
||||
'the same as another mod\'s.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_failure_m = { -- To be translated
|
||||
text = {
|
||||
'{C:attention}Main File Not Found!{}',
|
||||
'This mod\'s main file',
|
||||
'could not be found.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_disabled = {
|
||||
text = {
|
||||
'¡Este mod ha sido',
|
||||
'{C:attention}desactivado{}!'
|
||||
}
|
||||
}
|
||||
},
|
||||
Edition = {
|
||||
e_negative_playing_card = {
|
||||
name = "Negativa",
|
||||
text = {
|
||||
"{C:dark_edition}+#1#{} de tamaño de mano"
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
misc = {
|
||||
achievement_names = {
|
||||
hidden_achievement = "???",
|
||||
},
|
||||
achievement_descriptions = {
|
||||
hidden_achievement = "¡Juega más para descubirlo!",
|
||||
},
|
||||
dictionary = {
|
||||
b_mods = 'Mods',
|
||||
b_mods_cap = 'MODS',
|
||||
b_modded_version = 'Modded Version!', -- To be translated
|
||||
b_steamodded = 'Steamodded',
|
||||
b_credits = 'Créditos',
|
||||
b_open_mods_dir = 'Abrir directorio de Mods',
|
||||
b_no_mods = 'No se han detectado mods...',
|
||||
b_mod_list = 'Lista de Mods activos',
|
||||
b_mod_loader = 'Cargador de Mods',
|
||||
b_developed_by = 'desarrollado por ',
|
||||
b_rewrite_by = 'Reescrito por ',
|
||||
b_github_project = 'Proyecto de Github',
|
||||
b_github_bugs_1 = 'Puedes reportar errores',
|
||||
b_github_bugs_2 = 'y contribuir allí.',
|
||||
b_disable_mod_badges = 'Desactivar insignias de mods',
|
||||
b_author = 'Autor/a',
|
||||
b_authors = 'Autores',
|
||||
b_unknown = 'Desconocido',
|
||||
b_lovely_mod = '(Lovely Mod) ', --TODO
|
||||
b_by = ' Por: ',
|
||||
b_config = "Configuración",
|
||||
b_additions = 'Adiciones',
|
||||
b_stickers = 'Stickers', -- TODO
|
||||
b_achievements = "Logros",
|
||||
b_applies_stakes_1 = 'Aplica ',
|
||||
b_applies_stakes_2 = '',
|
||||
b_graphics_mipmap_level = "Mipmap level", -- TODO
|
||||
},
|
||||
v_dictionary = {
|
||||
c_types = '#1# Tipos',
|
||||
cashout_hidden = '...y #1# más',
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
return {
|
||||
descriptions = {
|
||||
Other = {
|
||||
load_success = {
|
||||
text = {
|
||||
'Mod carregado',
|
||||
'{C:green}com sucesso!'
|
||||
}
|
||||
},
|
||||
load_failure_d = {
|
||||
text = {
|
||||
'Faltam {C:attention}dependências!',
|
||||
'#1#',
|
||||
}
|
||||
},
|
||||
load_failure_c = {
|
||||
text = {
|
||||
'{C:attention}Conflitos{} não resolvidos!',
|
||||
'#1#'
|
||||
}
|
||||
},
|
||||
load_failure_d_c = {
|
||||
text = {
|
||||
'Faltam {C:attention}dependências!',
|
||||
'#1#',
|
||||
'{C:attention}Conflitos{} não resolvidos!',
|
||||
'#2#'
|
||||
}
|
||||
},
|
||||
load_failure_o = {
|
||||
text = {
|
||||
'{C:attention}Desatualizado!{} As versões',
|
||||
'{C:money}0.9.8{} e abaxido do Steamodded',
|
||||
'não são mais suportadas.'
|
||||
}
|
||||
},
|
||||
load_failure_i = {
|
||||
text = {
|
||||
'{C:attention}Incompatível!{} Precisa da versão',
|
||||
'#1# do Steamodded,',
|
||||
'mas a #2# está instalada.'
|
||||
}
|
||||
},
|
||||
load_failure_p = {
|
||||
text = {
|
||||
'{C:attention}Conflito de Prefixo!{}',
|
||||
'O prefixo deste mod é',
|
||||
'igual ao de outro mod.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_failure_m = {
|
||||
text = {
|
||||
'{C:attention}Arquivo Principal Não Encontrado!{}',
|
||||
'O arquivo principal deste mod',
|
||||
'não pôde ser encontrado.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_disabled = {
|
||||
text = {
|
||||
'Este mod foi',
|
||||
'{C:attention}desabilitado!{}'
|
||||
}
|
||||
}
|
||||
},
|
||||
Edition = {
|
||||
e_negative_playing_card = {
|
||||
name = "Negativo",
|
||||
text = {
|
||||
"{C:dark_edition}+#1#{} tamanho de mão"
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
misc = {
|
||||
achievement_names = {
|
||||
hidden_achievement = "???",
|
||||
},
|
||||
achievement_descriptions = {
|
||||
hidden_achievement = "Jogue mais para descobrir!",
|
||||
},
|
||||
dictionary = {
|
||||
b_mods = 'Mods',
|
||||
b_mods_cap = 'MODS',
|
||||
b_modded_version = 'Versão Modificada!',
|
||||
b_steamodded = 'Steamodded',
|
||||
b_credits = 'Créditos',
|
||||
b_open_mods_dir = 'Abrir pasta Mods',
|
||||
b_no_mods = 'Nenhum mod foi detectado...',
|
||||
b_mod_list = 'Lista de Mods Ativos',
|
||||
b_mod_loader = 'Mod Loader',
|
||||
b_developed_by = 'desenvolvido por ',
|
||||
b_rewrite_by = 'Reescrito por ',
|
||||
b_github_project = 'Projeto no Github',
|
||||
b_github_bugs_1 = 'Você pode reportar bugs e',
|
||||
b_github_bugs_2 = 'submeter contribuições por lá.',
|
||||
b_disable_mod_badges = 'Desabilitar Ícones de mods',
|
||||
b_author = 'Autor',
|
||||
b_authors = 'Autores',
|
||||
b_unknown = 'Desconhecido',
|
||||
b_lovely_mod = '(Mod do Lovely) ',
|
||||
b_by = ' Por: ',
|
||||
b_config = "Config.",
|
||||
b_additions = 'Adições',
|
||||
b_stickers = 'Adesivos',
|
||||
b_achievements = "Conquistas",
|
||||
b_applies_stakes_1 = 'Aplica ',
|
||||
b_applies_stakes_2 = '',
|
||||
b_graphics_mipmap_level = "Nível de Mipmap",
|
||||
b_browse = 'Navegar',
|
||||
b_search_prompt = 'Procurar por mods',
|
||||
b_search_button = 'Procurar',
|
||||
b_seeded_unlocks = 'Desbloquear com código',
|
||||
b_seeded_unlocks_info = 'Permite descobertas e desbloqueios em tentativas com código',
|
||||
ml_achievement_settings = {
|
||||
'Desabilitado',
|
||||
'Habilitado',
|
||||
'Ignorar Restrições'
|
||||
}
|
||||
},
|
||||
v_dictionary = {
|
||||
c_types = '#1# Tipos',
|
||||
cashout_hidden = '...e mais #1#',
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
return {
|
||||
descriptions = {
|
||||
Other = {
|
||||
load_success = {
|
||||
text = {
|
||||
'模组加载{C:green}成功!'
|
||||
}
|
||||
},
|
||||
load_failure_d = {
|
||||
text = {
|
||||
'{C:attention}依赖项{}缺失!',
|
||||
'#1#'
|
||||
}
|
||||
},
|
||||
load_failure_c = {
|
||||
text = {
|
||||
'存在{C:attention}冲突项{}!',
|
||||
'#1#'
|
||||
}
|
||||
},
|
||||
load_failure_d_c = {
|
||||
text = {
|
||||
'{C:attention}依赖项{}缺失!',
|
||||
'#1#',
|
||||
'存在{C:attention}冲突项{}!',
|
||||
'#2#'
|
||||
}
|
||||
},
|
||||
load_failure_o = {
|
||||
text = {
|
||||
'Steamodded版本{C:attention}过旧{}!',
|
||||
'已不再支持',
|
||||
'{C:money}0.9.8{}及以下版本'
|
||||
}
|
||||
},
|
||||
load_failure_i = {
|
||||
text = {
|
||||
'{C:attention}不兼容!',
|
||||
'所需Steamodded版本为#1#',
|
||||
'但当前为#2#'
|
||||
}
|
||||
},
|
||||
load_failure_p = {
|
||||
text = {
|
||||
'{C:attention}前缀冲突!{}',
|
||||
'此模组的前缀和',
|
||||
'另外一个模组相同!',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_failure_m = { -- To be translated
|
||||
text = {
|
||||
'{C:attention}Main File Not Found!{}',
|
||||
'This mod\'s main file',
|
||||
'could not be found.',
|
||||
'({C:attention}#1#{})'
|
||||
}
|
||||
},
|
||||
load_disabled = {
|
||||
text = {
|
||||
'该模组',
|
||||
'已被{C:attention}禁用{}!'
|
||||
}
|
||||
}
|
||||
},
|
||||
Edition = {
|
||||
e_negative_playing_card = {
|
||||
name = "负片",
|
||||
text = {
|
||||
"手牌上限{C:dark_edition}+#1#"
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
misc = {
|
||||
achievement_names = {
|
||||
hidden_achievement = "???",
|
||||
},
|
||||
achievement_descriptions = {
|
||||
hidden_achievement = "未发现",
|
||||
},
|
||||
dictionary = {
|
||||
b_mods = '模组',
|
||||
b_mods_cap = '模组',
|
||||
b_modded_version = '模组环境!',
|
||||
b_steamodded = 'Steamodded',
|
||||
b_credits = '鸣谢',
|
||||
b_open_mods_dir = '打开模组目录',
|
||||
b_no_mods = '未检测到任何模组……',
|
||||
b_mod_list = '已启用模组列表',
|
||||
b_mod_loader = '模组加载器',
|
||||
b_developed_by = '作者:',
|
||||
b_rewrite_by = '重写者:',
|
||||
b_github_project = 'Github项目',
|
||||
b_github_bugs_1 = '你可以在此汇报漏洞',
|
||||
b_github_bugs_2 = '和提交贡献',
|
||||
b_disable_mod_badges = '禁用模组横标',
|
||||
b_author = '作者',
|
||||
b_authors = '作者',
|
||||
b_unknown = '未知',
|
||||
b_lovely_mod = '(依赖Lovely加载器的补丁模组)',
|
||||
b_by = ' 作者:',
|
||||
b_config = "配置",
|
||||
b_additions = '新增项目',
|
||||
b_stickers = '贴纸',
|
||||
b_achievements = "成就",
|
||||
b_applies_stakes_1 = '',
|
||||
b_applies_stakes_2 = '的限制也都起效',
|
||||
b_graphics_mipmap_level = "多级渐远纹理层级",
|
||||
},
|
||||
v_dictionary = {
|
||||
c_types = '共有#1#种',
|
||||
cashout_hidden = '……还有#1#',
|
||||
},
|
||||
},
|
||||
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
## Achievement API
|
||||
|
||||
# fetch_achievements()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if G\.F_NO_ACHIEVEMENTS then return end[\n\s]*?--\|FROM LOCAL SETTINGS FILE'''
|
||||
position = 'before'
|
||||
# match_indent = true
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
G.SETTINGS.ACHIEVEMENTS_EARNED = G.SETTINGS.ACHIEVEMENTS_EARNED or {}
|
||||
for k, v in pairs(G.ACHIEVEMENTS) do
|
||||
if not v.key then v.key = k end
|
||||
for kk, vv in pairs(G.SETTINGS.ACHIEVEMENTS_EARNED) do
|
||||
if G.ACHIEVEMENTS[kk] and G.ACHIEVEMENTS[kk].mod then
|
||||
G.ACHIEVEMENTS[kk].earned = true
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
||||
|
||||
# check_for_unlock
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''if G.GAME.challenge then return end'''
|
||||
position = "after"
|
||||
payload = '''
|
||||
fetch_achievements() -- Refreshes achievements
|
||||
for k, v in pairs(G.ACHIEVEMENTS) do
|
||||
if (not v.earned) and (v.unlock_condition and type(v.unlock_condition) == 'function') and v:unlock_condition(args) then
|
||||
unlock_achievement(k)
|
||||
end
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# unlock_achievement()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''if G.PROFILES[G.SETTINGS.profile].all_unlocked then return end'''
|
||||
position = "at"
|
||||
payload = '''if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return true end'''
|
||||
match_indent = true
|
||||
|
||||
# unlock_achievement() - fix event queue leaking
|
||||
# fixed smods achievements not unlocking due to above comment's memory leak fix
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''local achievement_set = false
|
||||
if G.F_NO_ACHIEVEMENTS then return end'''
|
||||
position = "at"
|
||||
payload = '''local achievement_set = false
|
||||
if not G.ACHIEVEMENTS then fetch_achievements() end
|
||||
G.SETTINGS.ACHIEVEMENTS_EARNED[achievement_name] = true
|
||||
G:save_progress()
|
||||
|
||||
if G.ACHIEVEMENTS[achievement_name] and G.ACHIEVEMENTS[achievement_name].mod then
|
||||
if not G.ACHIEVEMENTS[achievement_name].earned then
|
||||
--|THIS IS THE FIRST TIME THIS ACHIEVEMENT HAS BEEN EARNED
|
||||
achievement_set = true
|
||||
G.FILE_HANDLER.force = true
|
||||
end
|
||||
G.ACHIEVEMENTS[achievement_name].earned = true
|
||||
end
|
||||
|
||||
if achievement_set then
|
||||
notify_alert(achievement_name)
|
||||
return true
|
||||
end
|
||||
if G.F_NO_ACHIEVEMENTS and not (G.ACHIEVEMENTS[achievement_name] or {}).mod then return true end'''
|
||||
match_indent = true
|
||||
|
||||
# create_UIBox_notify_alert
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''local t_s = Sprite(0,0,1.5*(_atlas.px/_atlas.py),1.5,_atlas, _c and _c.pos or {x=3, y=0})'''
|
||||
position = "before"
|
||||
payload = '''if SMODS.Achievements[_achievement] then _c = SMODS.Achievements[_achievement]; _atlas = G.ASSET_ATLAS[_c.atlas] end'''
|
||||
match_indent = true
|
||||
|
||||
# option to allow unlocks and discoveries in seeded runs
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = 'if G\.GAME\.seeded or G\.GAME\.challenge then return end'
|
||||
position = 'at'
|
||||
payload = 'if not SMODS.config.seeded_unlocks and (G.GAME.seeded or G.GAME.challenge) then return end'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/state_events.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = 'if not G.GAME.seeded and not G.GAME.challenge then'
|
||||
payload = 'if (not G.GAME.seeded and not G.GAME.challenge) or SMODS.config.seeded_unlocks then'
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = 'if G\.GAME\.seeded then'
|
||||
position = 'at'
|
||||
payload = 'if false then'
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = 'if G\.GAME\.challenge then'
|
||||
position = 'at'
|
||||
payload = 'if false then'
|
|
@ -1,105 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Sprite API
|
||||
|
||||
# Card:set_sprites()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = 'G.ASSET_ATLAS\["centers"\]'
|
||||
position = 'at'
|
||||
payload = "G.ASSET_ATLAS[(G.GAME.viewed_back or G.GAME.selected_back) and ((G.GAME.viewed_back or G.GAME.selected_back)[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or (G.GAME.viewed_back or G.GAME.selected_back).atlas) or 'centers']"
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = 'G.ASSET_ATLAS\[_center.atlas or _center.set\]'
|
||||
position = 'at'
|
||||
payload = '''
|
||||
G.ASSET_ATLAS[(_center.undiscovered and (_center.undiscovered[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.undiscovered.atlas))
|
||||
or (SMODS.UndiscoveredSprites[_center.set] and (SMODS.UndiscoveredSprites[_center.set][G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or SMODS.UndiscoveredSprites[_center.set].atlas))
|
||||
or _center.set] or G.ASSET_ATLAS["Joker"]'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "G.ASSET_ATLAS\\['Joker'\\]"
|
||||
position = 'at'
|
||||
payload = "G.ASSET_ATLAS[_center[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.atlas or _center.set]"
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = 'G.ASSET_ATLAS\[_center.set\]'
|
||||
position = 'at'
|
||||
payload = "G.ASSET_ATLAS[_center[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.atlas or _center.set]"
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "(_center.set == 'Joker' and G.j_undiscovered.pos) or"
|
||||
position = 'before'
|
||||
payload = '(_center.undiscovered and _center.undiscovered.pos) or (SMODS.UndiscoveredSprites[_center.set] and SMODS.UndiscoveredSprites[_center.set].pos) or'
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = '''(_center.set == 'Booster' and G.booster_undiscovered.pos))'''
|
||||
position = 'at'
|
||||
payload = '''(_center.set == 'Booster' and G.booster_undiscovered.pos) or G.j_undiscovered.pos)'''
|
||||
match_indent = true
|
||||
|
||||
# get_front_spriteinfo()
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = 'return G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(G.SETTINGS.colourblind_option and 2 or 1)], _front.pos'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = 'return G.ASSET_ATLAS[G.SETTINGS.colourblind_option and _front.hc_atlas or _front.lc_atlas or {}] or G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(G.SETTINGS.colourblind_option and 2 or 1)], _front.pos'
|
||||
|
||||
|
||||
# Game:set_render_settings()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = "G:set_render_settings()"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = "SMODS.injectObjects(SMODS.Atlas)"
|
||||
|
||||
|
||||
# create_UIBox_notify_alert()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'G.ASSET_ATLAS["icons"]'
|
||||
position = 'after'
|
||||
match_indent = false
|
||||
payload = '''
|
||||
local _smods_atlas = _c and ((G.SETTINGS.colourblind_option and _c.hc_atlas or _c.lc_atlas) or _c.atlas)
|
||||
if _smods_atlas then
|
||||
_atlas = G.ASSET_ATLAS[_smods_atlas] or _atlas
|
||||
end'''
|
||||
|
||||
## Hide floating ? from undiscovered types
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = '''shared_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)'''
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if (self.config.center.undiscovered and not self.config.center.undiscovered.no_overlay) or not( SMODS.UndiscoveredSprites[self.ability.set] and SMODS.UndiscoveredSprites[self.ability.set].no_overlay) then
|
||||
shared_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)
|
||||
else
|
||||
if SMODS.UndiscoveredSprites[self.ability.set] and SMODS.UndiscoveredSprites[self.ability.set].overlay_sprite then
|
||||
SMODS.UndiscoveredSprites[self.ability.set].overlay_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)
|
||||
end
|
||||
end'''
|
|
@ -1,173 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Back API
|
||||
|
||||
# Back:init()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = "if not selected_back then selected_back = G.P_CENTERS.b_red end"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = "self.atlas = selected_back.unlocked and selected_back.atlas or nil"
|
||||
|
||||
# Back:change_to()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = "if not new_back then new_back = G.P_CENTERS.b_red end"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = "self.atlas = new_back.unlocked and new_back.atlas or nil"
|
||||
|
||||
# G.FUNCS.change_viewed_back
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = "G.PROFILES[G.SETTINGS.profile].MEMORY.deck = args.to_val"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for key, val in pairs(G.sticker_card.area.cards) do
|
||||
val.children.back = false
|
||||
val:set_ability(val.config.center, true)
|
||||
end'''
|
||||
|
||||
# Back:apply_to_run()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = "function Back:apply_to_run()"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.effect.center
|
||||
if obj.apply and type(obj.apply) == 'function' then
|
||||
obj:apply(self)
|
||||
end'''
|
||||
|
||||
# Back:trigger_effect(args)
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = "if not args then return end"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.effect.center
|
||||
if type(obj.calculate) == 'function' then
|
||||
local o = {obj:calculate(self, args)}
|
||||
if next(o) ~= nil then return unpack(o) end
|
||||
elseif type(obj.trigger_effect) == 'function' then
|
||||
-- kept for compatibility
|
||||
local o = {obj:trigger_effect(args)}
|
||||
if next(o) ~= nil then
|
||||
sendWarnMessage(('Found `trigger_effect` function on SMODS.Back object "%s". This field is deprecated; please use `calculate` instead.'):format(obj.key), 'Back')
|
||||
return unpack(o)
|
||||
end
|
||||
end'''
|
||||
|
||||
## Back:generate_UI
|
||||
|
||||
# Localization with `unlock` field in loc_txt, same as for Jokers
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = 'if not back_config.unlock_condition then'
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local localized_by_smods
|
||||
local key_override
|
||||
if back_config.locked_loc_vars and type(back_config.locked_loc_vars) == 'function' then
|
||||
local res = back_config:locked_loc_vars() or {}
|
||||
loc_args = res.vars or {}
|
||||
key_override = res.key
|
||||
end
|
||||
if G.localization.descriptions.Back[key_override or back_config.key].unlock_parsed then
|
||||
localize{type = 'unlocks', key = key_override or back_config.key, set = 'Back', nodes = loc_nodes, vars = loc_args}
|
||||
localized_by_smods = true
|
||||
end
|
||||
if not back_config.unlock_condition then'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = '''localize{type = 'descriptions', key = 'demo_locked', set = "Other", nodes = loc_nodes, vars = loc_args}'''
|
||||
position = 'at'
|
||||
payload = '''
|
||||
if not localized_by_smods then
|
||||
localize{type = 'descriptions', key = 'demo_locked', set = "Other", nodes = loc_nodes, vars = loc_args}
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = 'loc_args = {other_name}'
|
||||
position = 'at'
|
||||
payload = 'loc_args = loc_args or {other_name}'
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = 'loc_args = {tostring(back_config.unlock_condition.amount)}'
|
||||
position = 'at'
|
||||
payload = 'loc_args = loc_args or {tostring(back_config.unlock_condition.amount)}'
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = 'loc_args = {other_name, colours = {get_stake_col(back_config.unlock_condition.stake)}}'
|
||||
position = 'at'
|
||||
payload = 'loc_args = loc_args or {other_name, colours = {get_stake_col(back_config.unlock_condition.stake)}}'
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
pattern = "if name_to_check == 'Blue Deck'*"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local key_override
|
||||
if back_config.loc_vars and type(back_config.loc_vars) == 'function' then
|
||||
local res = back_config:loc_vars() or {}
|
||||
loc_args = res.vars or {}
|
||||
key_override = res.key
|
||||
elseif name_to_check == 'Blue Deck' then loc_args = {effect_config.hands}'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'back.lua'
|
||||
pattern = "key = back_config\\.key"
|
||||
position = 'at'
|
||||
payload = "key = key_override or back_config.key"
|
||||
|
||||
# Back:apply_to_run() - add jokers support to config
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'back.lua'
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
pattern = '''
|
||||
if self.effect.config.voucher then
|
||||
'''
|
||||
payload = '''
|
||||
if self.effect.config.jokers then
|
||||
delay(0.4)
|
||||
G.E_MANAGER:add_event(Event({
|
||||
func = function()
|
||||
for k, v in ipairs(self.effect.config.jokers) do
|
||||
local card = create_card('Joker', G.jokers, nil, nil, nil, nil, v, 'deck')
|
||||
card:add_to_deck()
|
||||
G.jokers:emplace(card)
|
||||
card:start_materialize()
|
||||
end
|
||||
return true
|
||||
end
|
||||
}))
|
||||
end
|
||||
'''
|
File diff suppressed because it is too large
Load diff
|
@ -1,362 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Blind API
|
||||
|
||||
## Set debuffed_by_blind, use it for Matador behavior
|
||||
## Blind:debuff_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = 'card:set_debuff(true)'
|
||||
position = 'after'
|
||||
payload = "if card.debuff then card.debuffed_by_blind = true end"
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'blind.lua'
|
||||
pattern = 'card:set_debuff\(true\); return end'
|
||||
position = 'at'
|
||||
payload = """
|
||||
card:set_debuff(true); if card.debuff then card.debuffed_by_blind = true end; return end"""
|
||||
|
||||
## Card:set_debuff()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = '''
|
||||
self\.debuff = should_debuff
|
||||
(?<indent>[\t ]*)end
|
||||
'''
|
||||
position = 'after'
|
||||
payload = """if not self.debuff then self.debuffed_by_blind = false end
|
||||
|
||||
"""
|
||||
line_prepend = '$indent'
|
||||
|
||||
## Blind functions
|
||||
|
||||
# Blind:set_blind()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "G.GAME.last_blind = G.GAME.last_blind or {}"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
self.children.animatedSprite.atlas = G.ANIMATION_ATLAS[obj.atlas] or G.ANIMATION_ATLAS['blind_chips']'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = '--add new debuffs'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if not reset then
|
||||
local obj = self.config.blind
|
||||
if obj.set_blind and type(obj.set_blind) == 'function' then
|
||||
obj:set_blind()
|
||||
end
|
||||
end'''
|
||||
|
||||
# Blind:disable()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.name == 'The Water' then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.disable and type(obj.disable) == 'function' then
|
||||
obj:disable()
|
||||
end'''
|
||||
|
||||
# Blind:defeat()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.name == 'The Manacle' and not self.disabled then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.defeat and type(obj.defeat) == 'function' then
|
||||
obj:defeat()
|
||||
end'''
|
||||
|
||||
# Blind:debuff_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.debuff and not self.disabled and card.area ~= G.jokers then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if not self.disabled and obj.recalc_debuff and type(obj.recalc_debuff) == 'function' then
|
||||
if obj:recalc_debuff(card, from_blind) then
|
||||
card:set_debuff(true)
|
||||
if card.debuff then card.debuffed_by_blind = true end
|
||||
else
|
||||
card:set_debuff(false)
|
||||
end
|
||||
return
|
||||
elseif not self.disabled and obj.debuff_card and type(obj.debuff_card) == 'function' then
|
||||
sendWarnMessage(("Blind object %s has debuff_card function, recalc_debuff is preferred"):format(obj.key), obj.set)
|
||||
if obj:debuff_card(card, from_blind) then
|
||||
card:set_debuff(true)
|
||||
if card.debuff then card.debuffed_by_blind = true end
|
||||
else
|
||||
card:set_debuff(false)
|
||||
end
|
||||
return
|
||||
end'''
|
||||
|
||||
# Blind:stay_flipped()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if area == G.hand then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.stay_flipped and type(obj.stay_flipped) == 'function' then
|
||||
return obj:stay_flipped(area, card)
|
||||
end'''
|
||||
|
||||
# Blind:drawn_to_hand()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'blind.lua'
|
||||
pattern = "(?<indent>[\t ]*)if self.name == 'Cerulean Bell' then\n"
|
||||
position = 'before'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.drawn_to_hand and type(obj.drawn_to_hand) == 'function' then
|
||||
obj:drawn_to_hand()
|
||||
end'''
|
||||
|
||||
# Blind:debuff_hand()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.debuff then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.debuff_hand and type(obj.debuff_hand) == 'function' then
|
||||
return obj:debuff_hand(cards, hand, handname, check)
|
||||
end'''
|
||||
|
||||
# Blind:modify_hand()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.disabled then return mult, hand_chips, false end"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.modify_hand and type(obj.modify_hand) == 'function' then
|
||||
return obj:modify_hand(cards, poker_hands, text, mult, hand_chips)
|
||||
end'''
|
||||
|
||||
# Blind:press_play()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = 'if self.name == "The Hook" then'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.press_play and type(obj.press_play) == 'function' then
|
||||
return obj:press_play()
|
||||
end'''
|
||||
|
||||
# Blind:get_loc_debuff_text()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = 'function Blind:get_loc_debuff_text()'
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.blind
|
||||
if obj.get_loc_debuff_text and type(obj.get_loc_debuff_text) == 'function' then
|
||||
return obj:get_loc_debuff_text()
|
||||
end'''
|
||||
|
||||
# Blind:set_text()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "local loc_target = localize{type = 'raw_descriptions', key = self.config.blind.key, set = 'Blind', vars = loc_vars or self.config.blind.vars}"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local target = {type = 'raw_descriptions', key = self.config.blind.key, set = 'Blind', vars = loc_vars or self.config.blind.vars}
|
||||
local obj = self.config.blind
|
||||
if obj.loc_vars and type(obj.loc_vars) == 'function' then
|
||||
local res = obj:loc_vars() or {}
|
||||
target.vars = res.vars or target.vars
|
||||
target.key = res.key or target.key
|
||||
end
|
||||
local loc_target = localize(target)'''
|
||||
|
||||
# Blind:load()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = 'if G.P_BLINDS[blindTable.config_blind] then'
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if self.config.blind.atlas then
|
||||
self.children.animatedSprite.atlas = G.ANIMATION_ATLAS[self.config.blind.atlas]
|
||||
end'''
|
||||
|
||||
|
||||
# create_UIBox_blind_choice()
|
||||
# create_UIBox_round_scores_row()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "(?<indent>[\t ]*)blind_choice.animation = AnimatedSprite\\(0,0, 1.4, 1.4, (?<atlas>G.ANIMATION_ATLAS\\['blind_chips'\\]), blind_choice.config.pos\\)"
|
||||
position = 'at'
|
||||
root_capture = 'atlas'
|
||||
payload = "G.ANIMATION_ATLAS[blind_choice.config.atlas] or G.ANIMATION_ATLAS['blind_chips']"
|
||||
|
||||
# create_UIBox_your_collection_blinds()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "(?<indent>[\t ]*)local temp_blind = AnimatedSprite\\(0,0,1.3,1.3, G.ANIMATION_ATLAS\\['blind_chips'\\], discovered and v.pos or G.b_undiscovered.pos\\)"
|
||||
position = 'at'
|
||||
payload = '''
|
||||
|
||||
local s = 1.3
|
||||
if math.ceil(#blind_tab/6) > 6 then
|
||||
s = s * 6/math.ceil(#blind_tab/6)
|
||||
end
|
||||
local temp_blind = AnimatedSprite(0,0,s,s, G.ANIMATION_ATLAS[discovered and v.atlas or 'blind_chips'], discovered and v.pos or G.b_undiscovered.pos)'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'blind_matrix[math.ceil((k-1)/5+0.001)][1+((k-1)%5)] = {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local blinds_per_row = math.ceil(#blind_tab / 6)
|
||||
local row = math.ceil((k - 1) / blinds_per_row + 0.001)
|
||||
table.insert(blind_matrix[row], {
|
||||
n = G.UIT.C,
|
||||
config = { align = "cm", padding = 0.1 },
|
||||
nodes = {
|
||||
((k - blinds_per_row) % (2 * blinds_per_row) == 1) and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil,
|
||||
{ n = G.UIT.O, config = { object = temp_blind, focus_with_object = true } },
|
||||
((k - blinds_per_row) % (2 * blinds_per_row) == 0) and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil,
|
||||
}
|
||||
})'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '[\t ]*\(k==6 or k ==16 or k == 26\) and \{n=G.UIT.B, config=\{h=0.2,w=0.5\}\} or nil,\n[\t ]*\{n=G.UIT.O, config=\{object = temp_blind, focus_with_object = true\}\},\n[\t ]*\(k==5 or k ==15 or k == 25\) and \{n=G.UIT.B, config=\{h=0.2,w=0.5\}\} or nil,\n[\t ]*\}\}'
|
||||
position = 'at'
|
||||
payload = ''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'table.sort(blind_tab, function (a, b) return a.order < b.order end)'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
table.sort(blind_tab, function(a, b) return a.order + (a.boss and a.boss.showdown and 1000 or 0) < b.order + (b.boss and b.boss.showdown and 1000 or 0) end)'''
|
||||
|
||||
# add_round_eval_row()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "local blind_sprite = AnimatedSprite(0, 0, 1.2,1.2, G.ANIMATION_ATLAS['blind_chips'], copy_table(G.GAME.blind.pos))"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local obj = G.GAME.blind.config.blind
|
||||
local blind_sprite = AnimatedSprite(0, 0, 1.2, 1.2, G.ANIMATION_ATLAS[obj.atlas] or G.ANIMATION_ATLAS['blind_chips'], copy_table(G.GAME.blind.pos))'''
|
||||
|
||||
# create_UIBox_blind_choice()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "local loc_target = localize{type = 'raw_descriptions', key = blind_choice.config.key, set = 'Blind', vars = {localize(G.GAME.current_round.most_played_poker_hand, 'poker_hands')}}"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local target = {type = 'raw_descriptions', key = blind_choice.config.key, set = 'Blind', vars = {}}
|
||||
if blind_choice.config.name == 'The Ox' then
|
||||
target.vars = {localize(G.GAME.current_round.most_played_poker_hand, 'poker_hands')}
|
||||
end
|
||||
local obj = blind_choice.config
|
||||
if obj.loc_vars and _G['type'](obj.loc_vars) == 'function' then
|
||||
local res = obj:loc_vars() or {}
|
||||
target.vars = res.vars or target.vars
|
||||
target.key = res.key or target.key
|
||||
end
|
||||
local loc_target = localize(target)'''
|
||||
|
||||
# create_UIBox_blind_popup()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''local loc_target = localize{type = 'raw_descriptions', key = blind.key, set = 'Blind', vars = vars or blind.vars}'''
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local target = {type = 'raw_descriptions', key = blind.key, set = 'Blind', vars = vars or blind.vars}
|
||||
if blind.collection_loc_vars and type(blind.collection_loc_vars) == 'function' then
|
||||
local res = blind:collection_loc_vars() or {}
|
||||
target.vars = res.vars or target.vars
|
||||
target.key = res.key or target.key
|
||||
end
|
||||
local loc_target = localize(target)'''
|
||||
|
||||
# get_new_boss()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = 'elseif not v.boss.showdown*'
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
payload = '''
|
||||
elseif v.in_pool and type(v.in_pool) == 'function' then
|
||||
local res, options = v:in_pool()
|
||||
if
|
||||
(
|
||||
((G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2) ==
|
||||
(v.boss.showdown or false)
|
||||
) or
|
||||
(options or {}).ignore_showdown_check
|
||||
then
|
||||
eligible_bosses[k] = res and true or nil
|
||||
end'''
|
||||
|
||||
# G.UIDEF.challenge_description_tab
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "local temp_blind = AnimatedSprite(0,0,1,1, G.ANIMATION_ATLAS['blind_chips'], v.pos)"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = "local temp_blind = AnimatedSprite(0,0,1,1, G.ANIMATION_ATLAS[v.atlas or ''] or G.ANIMATION_ATLAS['blind_chips'], v.pos)"
|
|
@ -1,150 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Allow blinds to have more than 2 lines
|
||||
|
||||
# create_UIBox_blind_choice()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "if blind_state == 'Select' then blind_state = 'Current' end"
|
||||
position = 'after'
|
||||
payload = '''
|
||||
local blind_desc_nodes = {}
|
||||
for k, v in ipairs(text_table) do
|
||||
blind_desc_nodes[#blind_desc_nodes+1] = {n=G.UIT.R, config={align = "cm", maxw = 2.8}, nodes={
|
||||
{n=G.UIT.T, config={text = v or '-', scale = 0.32, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.WHITE, shadow = not disabled}}
|
||||
}}
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)text_table\[1\] and \{n=G\.UIT\.R, config=\{align = "cm", minh = 0\.7, padding = 0\.05, minw = 2\.9}, nodes=\{
|
||||
[\t ]* text_table\[1\] and \{n=G\.UIT\.R, config=\{align = "cm", maxw = 2\.8\}, nodes=\{
|
||||
[\t ]* \{n=G\.UIT\.T, config=\{id = blind_choice\.config\.key, ref_table = \{val = ''\}, ref_value = 'val', scale = 0\.32, colour = disabled and G\.C\.UI\.TEXT_INACTIVE or G\.C\.WHITE, shadow = not disabled, func = 'HUD_blind_debuff_prefix'\}\},
|
||||
[\t ]* \{n=G\.UIT\.T, config=\{text = text_table\[1\] or '\-', scale = 0\.32, colour = disabled and G\.C\.UI\.TEXT_INACTIVE or G\.C\.WHITE, shadow = not disabled\}\}
|
||||
[\t ]* \}\} or nil,
|
||||
[\t ]* text_table\[2\] and \{n=G\.UIT\.R, config=\{align = "cm", maxw = 2\.8\}, nodes=\{
|
||||
[\t ]* \{n=G\.UIT\.T, config=\{text = text_table\[2\] or '\-', scale = 0\.32, colour = disabled and G\.C\.UI\.TEXT_INACTIVE or G\.C\.WHITE, shadow = not disabled\}\}
|
||||
[\t ]* \}\} or nil,
|
||||
[\t ]*\}\} or nil,'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
text_table[1] and {n=G.UIT.R, config={align = "cm", minh = 0.7, padding = 0.05, minw = 2.9}, nodes = blind_desc_nodes} or nil,'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# create_UIBox_HUD_blind()
|
||||
# Padding and contained nodes are set in G.FUNCS.HUD_blind_debuff (overrides.lua)
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)\{n=G\.UIT\.R, config=\{align = "cm", padding = 0\.05\}, nodes=\{
|
||||
[\t ]* \{n=G\.UIT\.R, config=\{align = "cm", minh = 0\.3, maxw = 4\.2\}, nodes=\{
|
||||
[\t ]* \{n=G\.UIT\.T, config=\{ref_table = \{val = ''\}, ref_value = 'val', scale = scale\*0\.9, colour = G\.C\.UI\.TEXT_LIGHT, func = 'HUD_blind_debuff_prefix'\}\},
|
||||
[\t ]* \{n=G\.UIT\.T, config=\{ref_table = G\.GAME\.blind\.loc_debuff_lines, ref_value = 1, scale = scale\*0\.9, colour = G\.C\.UI\.TEXT_LIGHT, id = 'HUD_blind_debuff_1', func = 'HUD_blind_debuff'\}\}
|
||||
[\t ]* \}\},
|
||||
[\t ]* \{n=G\.UIT\.R, config=\{align = "cm", minh = 0\.3, maxw = 4\.2\}, nodes=\{
|
||||
[\t ]* \{n=G\.UIT\.T, config=\{ref_table = G\.GAME\.blind\.loc_debuff_lines, ref_value = 2, scale = scale\*0\.9, colour = G\.C\.UI\.TEXT_LIGHT, id = 'HUD_blind_debuff_2', func = 'HUD_blind_debuff'\}\}
|
||||
[\t ]* \}\},
|
||||
[\t ]*\}\},'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
{n=G.UIT.R, config={align = "cm", id = 'HUD_blind_debuff', func = 'HUD_blind_debuff'}, nodes={}},'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# Blind:set_text
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "blind.lua"
|
||||
pattern = """
|
||||
(?<indent>[\t ]*)self\\.loc_debuff_lines\\[1\\] = ''
|
||||
[\t ]*self\\.loc_debuff_lines\\[2\\] = ''"""
|
||||
position = 'at'
|
||||
payload = 'EMPTY(self.loc_debuff_lines)'
|
||||
line_prepend = '$indent'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "blind.lua"
|
||||
pattern = "for k, v in ipairs(loc_target) do"
|
||||
position = 'before'
|
||||
payload = 'EMPTY(self.loc_debuff_lines)'
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "blind.lua"
|
||||
pattern = "self.loc_debuff_text = self.loc_debuff_text..v..(k <= #loc_target and ' ' or '')"
|
||||
position = 'after'
|
||||
payload = "self.loc_debuff_lines[k] = v"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "blind.lua"
|
||||
pattern = """
|
||||
(?<indent>[\t ]*)self\\.loc_debuff_lines\\[1\\] = loc_target\\[1\\] or ''
|
||||
[\t ]*self\\.loc_debuff_lines\\[2\\] = loc_target\\[2\\] or ''
|
||||
"""
|
||||
position = 'at'
|
||||
payload = ''
|
||||
|
||||
## Add a box with h=3.64 (magic number equal to the height of HUD_blind)
|
||||
## centered inside 'row_blind'
|
||||
# create_UIBox_HUD
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = """{n=G.UIT.R, config={align = "cm", id = 'row_blind', minw = 1, minh = 3.75}, nodes={}},"""
|
||||
position = 'at'
|
||||
payload = """{n=G.UIT.R, config={align = "cm", id = 'row_blind', minw = 1, minh = 3.75}, nodes={
|
||||
{n=G.UIT.B, config={w=0, h=3.64, id = 'row_blind_bottom'}, nodes={}}
|
||||
}},"""
|
||||
match_indent = true
|
||||
|
||||
## Blind UI's bottom edge is aligned to it
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "config = {major = G.HUD:get_UIE_by_ID('row_blind'), align = 'cm', offset = {x=0,y=-10}, bond = 'Weak'}"
|
||||
position = 'at'
|
||||
payload = "config = {major = G.HUD:get_UIE_by_ID('row_blind_bottom'), align = 'bmi', offset = {x=0,y=-10}, bond = 'Weak'}"
|
||||
match_indent = true
|
||||
|
||||
## Patch G.GAME.blind:juice_up() across all files
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_1'\):juice_up\(0\.3, 0\)
|
||||
[\t ]*G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_2'\):juice_up\(0\.3, 0\)
|
||||
[\t ]*G\.GAME\.blind:juice_up\(\)'''
|
||||
position = 'at'
|
||||
payload = 'SMODS.juice_up_blind()'
|
||||
line_prepend = '$indent'
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_1'\):juice_up\(0\.3, 0\)
|
||||
[\t ]*G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_2'\):juice_up\(0\.3, 0\)
|
||||
[\t ]*G\.GAME\.blind:juice_up\(\)'''
|
||||
position = 'at'
|
||||
payload = 'SMODS.juice_up_blind()'
|
||||
line_prepend = '$indent'
|
||||
|
||||
# remove statically added 1 from The Wheel's collection description
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''\(k ==1 and blind\.name == 'The Wheel' and '1' or ''\)\.\.'''
|
||||
position = 'at'
|
||||
payload = ''
|
|
@ -1,264 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
## Booster Pack API
|
||||
|
||||
# Card:open
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if self\.ability\.name:find\('Arcana'\) then \n[\s\S]{12}G\.STATE'''
|
||||
position = "before"
|
||||
payload = '''
|
||||
booster_obj = self.config.center
|
||||
if booster_obj and SMODS.Centers[booster_obj.key] then
|
||||
G.STATE = G.STATES.SMODS_BOOSTER_OPENED
|
||||
SMODS.OPENED_BOOSTER = self
|
||||
end'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# Card:open
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if self\.ability\.name:find\('Arcana'\) then[\t\n ]*if G\.GAME\.used_vouchers\.v_omen_globe and pseudorandom\('omen_globe'\) > 0\.8 then''' # Possibly try to target something else
|
||||
position = "at"
|
||||
payload = '''if booster_obj.create_card and type(booster_obj.create_card) == "function" then
|
||||
local _card_to_spawn = booster_obj:create_card(self, i)
|
||||
if type((_card_to_spawn or {}).is) == 'function' and _card_to_spawn:is(Card) then
|
||||
card = _card_to_spawn
|
||||
else
|
||||
card = SMODS.create_card(_card_to_spawn)
|
||||
end
|
||||
elseif self.ability.name:find('Arcana') then
|
||||
if G.GAME.used_vouchers.v_omen_globe and pseudorandom('omen_globe') > 0.8 then'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# Game:set_globals
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "globals.lua"
|
||||
pattern = '''(?<indent>[\t ]*)self\.STATES = \{'''
|
||||
position = "after"
|
||||
payload = '''
|
||||
|
||||
SMODS_BOOSTER_OPENED = 999,'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# Game:update
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "game.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if self\.STATE == self\.STATES\.TAROT_PACK then'''
|
||||
position = "before"
|
||||
payload = '''
|
||||
if G.STATE == G.STATES.SMODS_BOOSTER_OPENED then
|
||||
SMODS.OPENED_BOOSTER.config.center:update_pack(dt)
|
||||
end
|
||||
|
||||
'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# G.FUNC.can_skip_booster
|
||||
# TODO customize whether pack can be skipped
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = '''(?<indent>[\t ]*)\(G\.STATE == G\.STATES\.PLANET_PACK or G\.STATE == G\.STATES\.STANDARD_PACK'''
|
||||
position = "at"
|
||||
payload = '''(G.STATE == G.STATES.SMODS_BOOSTER_OPENED or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.STANDARD_PACK'''
|
||||
|
||||
# CardArea:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "cardarea.lua"
|
||||
pattern = "(self.config.type == 'deck' and self ~= G.deck) or"
|
||||
position = "before"
|
||||
payload = '''
|
||||
(self.config.type == 'hand' and state == G.STATES.SMODS_BOOSTER_OPENED) or'''
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.use_card
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = "prev_state == G.STATES.SPECTRAL_PACK or prev_state == G.STATES.STANDARD_PACK or"
|
||||
position = "after"
|
||||
payload = '''
|
||||
prev_state == G.STATES.SMODS_BOOSTER_OPENED or'''
|
||||
match_indent = true
|
||||
|
||||
# CardArea:align_cards()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "cardarea.lua"
|
||||
pattern = "if self.config.type == 'hand' and (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK) then"
|
||||
position = "at"
|
||||
payload = "if self.config.type == 'hand' and (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then"
|
||||
match_indent = true
|
||||
|
||||
# CardArea:align_cards()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "cardarea.lua"
|
||||
pattern = "if self.config.type == 'hand' and not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK) then"
|
||||
position = "at"
|
||||
payload = "if self.config.type == 'hand' and not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then"
|
||||
match_indent = true
|
||||
|
||||
# Card:can_use_consumable()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK then"
|
||||
position = "at"
|
||||
payload = "if G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED then"
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.use_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = "if G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SPECTRAL_PACK then"
|
||||
position = "at"
|
||||
payload = """
|
||||
if nc then
|
||||
if area then area:remove_from_highlighted(card) end
|
||||
play_sound('cardSlide2', nil, 0.3)
|
||||
dont_dissolve = true
|
||||
end
|
||||
if (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then"""
|
||||
match_indent = true
|
||||
|
||||
# G.FUNC.use_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = 'if area == G.consumeables then'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if nc and area == G.pack_cards then G.pack_cards:remove_card(card); G.consumeables:emplace(card) end'''
|
||||
|
||||
# G.FUNC.use_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = "(G.STATE == G.STATES.BUFFOON_PACK and G.STATES.BUFFOON_PACK) or"
|
||||
position = "before"
|
||||
payload = "(G.STATE == G.STATES.SMODS_BOOSTER_OPENED and G.STATES.SMODS_BOOSTER_OPENED) or"
|
||||
match_indent = true
|
||||
|
||||
# G.FUNC.use_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "if not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK) and"
|
||||
position = "at"
|
||||
payload = "if not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and"
|
||||
match_indent = true
|
||||
|
||||
# Card:use_consumeable()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)align = \(G\.STATE[\s\S]*and -0\.2 or 0},'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
align = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and 'tm' or 'cm',
|
||||
offset = {x = 0, y = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and -0.2 or 0},'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# G.FUNCS.use_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = "e.config.ref_table:redeem()"
|
||||
position = "before"
|
||||
payload = "if area == G.pack_cards then e.config.ref_table.cost = 0 end"
|
||||
match_indent = true
|
||||
|
||||
## Stopping ease_dollars anim from playing when voucher is free
|
||||
# Card:redeem()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)ease_dollars\(-self\.cost\)\n[\s\S]{8}inc_career_stat\('c_shop_dollars_spent', self\.cost\)'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if self.cost ~= 0 then
|
||||
ease_dollars(-self.cost)
|
||||
inc_career_stat('c_shop_dollars_spent', self.cost)
|
||||
end'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# Add support for saving consumables
|
||||
# comment
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
pattern = '''
|
||||
if card.ability.consumeable then
|
||||
if (card.area == G.pack_cards and G.pack_cards) then
|
||||
'''
|
||||
payload = '''
|
||||
if card.ability.consumeable and booster_obj and booster_obj.select_card then
|
||||
if (card.area == G.pack_cards and G.pack_cards) then
|
||||
return {n=G.UIT.ROOT, config = {padding = 0, colour = G.C.CLEAR}, nodes={
|
||||
{n=G.UIT.R, config={ref_table = card, r = 0.08, padding = 0.1, align = "bm", minw = 0.5*card.T.w - 0.15, maxw = 0.9*card.T.w - 0.15, minh = 0.3*card.T.h, hover = true, shadow = true, colour = G.C.UI.BACKGROUND_INACTIVE, one_press = true, button = 'use_card', func = 'can_select_from_booster'}, nodes={
|
||||
{n=G.UIT.T, config={text = localize('b_select'),colour = G.C.UI.TEXT_LIGHT, scale = 0.45, shadow = true}}
|
||||
}},
|
||||
}}
|
||||
end
|
||||
end
|
||||
'''
|
||||
|
||||
# comment
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = '''
|
||||
if card.ability.consumeable then
|
||||
if nc then
|
||||
'''
|
||||
payload = '''
|
||||
if booster_obj and booster_obj.select_card then
|
||||
local area = type(booster_obj.select_card) == 'table' and (booster_obj.select_card[e.config.ref_table.ability.set] or nil) or booster_obj.select_card
|
||||
G[area]:emplace(card)
|
||||
play_sound('card1', 0.8, 0.6)
|
||||
play_sound('generic1')
|
||||
dont_dissolve = true
|
||||
delay_fac = 0.2
|
||||
elseif card.ability.consumeable then
|
||||
if nc then
|
||||
'''
|
||||
# comment
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
pattern = '''
|
||||
if area == G.consumeables then
|
||||
'''
|
||||
payload = '''
|
||||
booster_obj = nil
|
||||
'''
|
||||
# comment
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
pattern = '''
|
||||
G.FUNCS.skip_booster = function(e)
|
||||
'''
|
||||
payload = '''
|
||||
booster_obj = nil
|
||||
'''
|
|
@ -1,552 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Center API
|
||||
|
||||
# Card:set_ability()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = "(?<indent>[\t ]*)if not G\\.OVERLAY_MENU then \n"
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj.set_ability and type(obj.set_ability) == 'function' then
|
||||
obj:set_ability(self, initial, delay_sprites)
|
||||
end
|
||||
|
||||
'''
|
||||
line_prepend = '$indent'
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "self.ability.bonus = (self.ability.bonus or 0) + (center.config.bonus or 0)"
|
||||
position = "after"
|
||||
payload = """
|
||||
for k, v in pairs(center.config) do
|
||||
if k ~= 'bonus' then
|
||||
if type(v) == 'table' then
|
||||
self.ability[k] = copy_table(v)
|
||||
else
|
||||
self.ability[k] = v
|
||||
end
|
||||
end
|
||||
end"""
|
||||
match_indent = true
|
||||
|
||||
# Card:calculate_joker()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''function Card:calculate_joker(context)
|
||||
if self.debuff then return nil end
|
||||
'''
|
||||
position = 'after'
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if self.ability.set ~= "Enhanced" and obj.calculate and type(obj.calculate) == 'function' then
|
||||
local o, t = obj:calculate(self, context)
|
||||
if o or t then return o, t end
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# Card:update()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = 'if G.STAGE == G.STAGES.RUN then'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj.update and type(obj.update) == 'function' then
|
||||
obj:update(self, dt)
|
||||
end'''
|
||||
|
||||
# Card:generate_UIBox_ability_table()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<else>else)\n[\t ]*if self.ability.name == 'Loyalty Card' then\n[\t ]*self.ability.loyalty_remaining"
|
||||
root_capture = 'else'
|
||||
position = 'at'
|
||||
payload = 'elseif context.joker_main then'
|
||||
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = 'return generate_card_ui(self.config.center, nil, loc_vars, card_type, badges, hide_desc, main_start, main_end)'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = 'return generate_card_ui(self.config.center, nil, loc_vars, card_type, badges, hide_desc, main_start, main_end, self)'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "full_UI_table.name = localize{type = 'name', set = _c.set, key = _c.key, nodes = full_UI_table.name}"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if not _c.generate_ui or type(_c.generate_ui) ~= 'function' then
|
||||
full_UI_table.name = localize{type = 'name', set = _c.set, key = _c.key, nodes = full_UI_table.name}
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "elseif specific_vars and specific_vars.debuffed then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
elseif _c.generate_ui and type(_c.generate_ui) == 'function' then
|
||||
_c:generate_ui(info_queue, card, desc_nodes, specific_vars, full_UI_table)
|
||||
if specific_vars and specific_vars.pinned then info_queue[#info_queue+1] = {key = 'pinned_left', set = 'Other'} end
|
||||
if specific_vars and specific_vars.sticker then info_queue[#info_queue+1] = {key = string.lower(specific_vars.sticker)..'_sticker', set = 'Other'} end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "(?<indent>[\t ]+)if (?<rest>_c.name == 'Golden Ticket' then)"
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local res = {}
|
||||
if _c.locked_loc_vars and type(_c.locked_loc_vars) == 'function' then
|
||||
local _card = _c.create_fake_card and _c:create_fake_card()
|
||||
res = _c:locked_loc_vars(info_queue, _card) or {}
|
||||
loc_vars = res.vars or {}
|
||||
specific_vars = specific_vars or {}
|
||||
specific_vars.not_hidden = res.not_hidden or specific_vars.not_hidden
|
||||
if res.main_start then desc_nodes[#desc_nodes+1] = res.main_start end
|
||||
main_end = res.main_end or main_end
|
||||
elseif $rest'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
pattern = "localize{type = 'unlocks', key = 'joker_locked_legendary', set = 'Other', nodes = desc_nodes, vars = loc_vars}"
|
||||
payload = "localize{type = 'unlocks', key = res.key or 'joker_locked_legendary', set = res.set or 'Other', nodes = desc_nodes, vars = loc_vars, text_colour = res.text_colour, scale = res.scale}"
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
pattern = "localize{type = 'unlocks', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}"
|
||||
payload = "localize{type = 'unlocks', key = res.key or _c.key, set = res.set or _c.set, nodes = desc_nodes, vars = loc_vars, text_colour = res.text_colour, scale = res.scale}"
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
pattern = 'elseif desc_nodes ~= full_UI_table.main then'
|
||||
payload = 'elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name then'
|
||||
|
||||
|
||||
|
||||
# check_for_unlock()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "(?<indent>[\t ]*)if not card.unlocked and card.unlock_condition and args.type == 'career_stat' then"
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
|
||||
local custom_check
|
||||
if not card.unlocked and card.check_for_unlock and type(card.check_for_unlock) == 'function' then
|
||||
ret = card:check_for_unlock(args)
|
||||
if ret then unlock_card(card) end
|
||||
custom_check = true
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "(?<indent>[\t ]*)if(?<a> )not card.unlocked and card.unlock_condition and args.type == 'career_stat' then"
|
||||
position = 'at'
|
||||
root_capture = 'a'
|
||||
payload = ' not custom_check and '
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "(?<indent>[\t ]*)if(?<a> )not card.unlocked and card.unlock_condition and card.unlock_condition.type == args.type then"
|
||||
position = 'at'
|
||||
root_capture = 'a'
|
||||
payload = ' not custom_check and '
|
||||
|
||||
#Card:use_consumable()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<indent>[\t ]*)if self.ability.consumeable.mod_conv or self.ability.consumeable.suit_conv then"
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj.use and type(obj.use) == 'function' then
|
||||
obj:use(self, area, copier)
|
||||
return
|
||||
end'''
|
||||
|
||||
# Card:can_use_consumable()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<indent>[\t ]*)if self.ability.name == 'The Hermit' or self.ability.consumeable.hand_type"
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj.can_use and type(obj.can_use) == 'function' then
|
||||
return obj:can_use(self)
|
||||
end'''
|
||||
|
||||
# G.UIDEF.card_h_popup()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "(?<indent>[\t ]*)(?<if>if AUT.badges.card_type or AUT.badges.force_rarity then)\n[\t ]*(?<rest>.*)\n[\t ]*end"
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local obj = card.config.center
|
||||
$if
|
||||
if obj and (obj.set_card_type_badge or obj.type and obj.type.set_card_type_badge) then
|
||||
if obj.type and type(obj.type.set_card_type_badge) == 'function' then
|
||||
obj.type:set_card_type_badge(obj, card, badges)
|
||||
end
|
||||
if type(obj.set_card_type_badge) == 'function' then
|
||||
obj:set_card_type_badge(card, badges)
|
||||
end
|
||||
else
|
||||
$rest
|
||||
end
|
||||
end
|
||||
if obj and obj.set_badges and type(obj.set_badges) == 'function' then
|
||||
obj:set_badges(card, badges)
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "(?<indent>[\t ]*)if AUT.badges then\n([\t ]*.*\n){4}[\t ]*end"
|
||||
line_prepend = '$indent'
|
||||
position = 'after'
|
||||
payload = '''
|
||||
if AUT.card_type ~= 'Locked' and AUT.card_type ~= 'Undiscovered' then
|
||||
SMODS.create_mod_badges(card.config.center, badges)
|
||||
if card.base then
|
||||
SMODS.create_mod_badges(SMODS.Ranks[card.base.value], badges)
|
||||
SMODS.create_mod_badges(SMODS.Suits[card.base.suit], badges)
|
||||
end
|
||||
if card.config and card.config.tag then
|
||||
SMODS.create_mod_badges(SMODS.Tags[card.config.tag.key], badges)
|
||||
end
|
||||
badges.mod_set = nil
|
||||
end'''
|
||||
|
||||
# set_discover_tallies()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = "(?<indent>[\t ]*)if v.set == 'Planet' then(\n[\t ]*.*){15}"
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local tally = G.DISCOVER_TALLIES[v.set:lower()..'s']
|
||||
if tally then
|
||||
tally.of = tally.of + 1
|
||||
if v.discovered then
|
||||
tally.tally = tally.tally + 1
|
||||
end
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = "[\t ]*tarots = \\{tally = 0, of = 0\\},\n(.*\n){2}"
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
payload = ''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = "(?<indent>[\t ]*)for _, v in pairs\\(G.DISCOVER_TALLIES\\) do"
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do
|
||||
G.DISCOVER_TALLIES[v:lower()..'s'] = {tally = 0, of = 0}
|
||||
end'''
|
||||
|
||||
# create_UIBox_your_collection()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "(?<indent>[\t ]*)local t = create_UIBox_generic_options\\(\\{ back_func = G.STAGE"
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local consumable_nodes = {}
|
||||
if #SMODS.ConsumableType.ctype_buffer <= 3 then
|
||||
for _, key in ipairs(SMODS.ConsumableType.ctype_buffer) do
|
||||
local id = 'your_collection_'..key:lower()..'s'
|
||||
consumable_nodes[#consumable_nodes+1] = UIBox_button({button = id, label = {localize('b_'..key:lower()..'_cards')}, count = G.DISCOVER_TALLIES[key:lower()..'s'], minw = 4, id = id, colour = G.C.SECONDARY_SET[key]})
|
||||
end
|
||||
else
|
||||
consumable_nodes[#consumable_nodes+1] = UIBox_button({ button = 'your_collection_consumables', label = {localize('b_stat_consumables'), localize{ type = 'variable', key = 'c_types', vars = {#SMODS.ConsumableType.ctype_buffer} } }, count = G.DISCOVER_TALLIES['consumeables'], minw = 4, minh = 4, id = 'your_collection_consumables', colour = G.C.FILTER })
|
||||
end
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "(?<indent>[\t ]*)nodes=\\{\n[\t ]*UIBox_button\\(\\{button = 'your_collection_tarots'(.*\n){3}[\t ]*}"
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
payload = 'nodes = consumable_nodes'
|
||||
|
||||
# Card:apply_to_run()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<indent>[\t ]*)if center_table.name == 'Overstock'"
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local obj = center or self.config.center
|
||||
if obj.redeem and type(obj.redeem) == 'function' then
|
||||
obj:redeem(card_to_save)
|
||||
return
|
||||
end'''
|
||||
|
||||
# create_card_for_shop()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "local total_rate = G.GAME.joker_rate + G.GAME.tarot_rate + G.GAME.planet_rate + G.GAME.playing_card_rate + G.GAME.spectral_rate"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local total_rate = G.GAME.joker_rate + G.GAME.playing_card_rate
|
||||
for _,v in ipairs(SMODS.ConsumableType.ctype_buffer) do
|
||||
total_rate = total_rate + G.GAME[v:lower()..'_rate']
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '(?<indent>[\t ]*)for _, v in ipairs\((?<li>\{\n(.*\n){5}[\t ]*\})\) do'
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
payload = '''
|
||||
-- need to preserve order to leave RNG unchanged
|
||||
local rates = $li
|
||||
for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do
|
||||
if not (v == 'Tarot' or v == 'Planet' or v == 'Spectral') then
|
||||
table.insert(rates, { type = v, val = G.GAME[v:lower()..'_rate'] })
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(rates) do'''
|
||||
|
||||
# create_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if not forced_key and soulable and (not G.GAME.banned_keys['c_soul']) then"
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
payload = '''
|
||||
for _, v in ipairs(SMODS.Consumable.legendaries) do
|
||||
if (_type == v.type.key or _type == v.soul_set) and not (G.GAME.used_jokers[v.key] and not next(find_joker("Showman")) and not v.can_repeat_soul) and (not v.in_pool or (type(v.in_pool) ~= "function") or v:in_pool()) then
|
||||
if pseudorandom('soul_'..v.key.._type..G.GAME.round_resets.ante) > (1 - v.soul_rate) then
|
||||
forced_key = v.key
|
||||
end
|
||||
end
|
||||
end'''
|
||||
|
||||
# Card:add_to_deck()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = '(?<indent>[\t ]*)if self.ability.h_size ~= 0 then\n[\t ]*G\.hand:change_size\(self.ability.h_size\)'
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj and obj.add_to_deck and type(obj.add_to_deck) == 'function' then
|
||||
obj:add_to_deck(self, from_debuff)
|
||||
end'''
|
||||
|
||||
# Card:remove_from_deck()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = '(?<indent>[\t ]*)if self.ability.h_size ~= 0 then\n[\t ]*G\.hand:change_size\(-self.ability.h_size\)'
|
||||
line_prepend = '$indent'
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj and obj.remove_from_deck and type(obj.remove_from_deck) == 'function' then
|
||||
obj:remove_from_deck(self, from_debuff)
|
||||
end'''
|
||||
|
||||
# G.FUNCS.use_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = "if card.area then card.area:remove_card(card) end"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local nc
|
||||
if card.ability.consumeable then
|
||||
local obj = card.config.center
|
||||
if obj.keep_on_use and type(obj.keep_on_use) == 'function' then
|
||||
nc = obj:keep_on_use(card)
|
||||
end
|
||||
end
|
||||
if card.area and (not nc or card.area == G.pack_cards) then card.area:remove_card(card) end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = "else draw_card(G.hand, G.play, 1, 'up', true, card, nil, mute) end"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''elseif not nc then draw_card(G.hand, G.play, 1, 'up', true, card, nil, mute) end'''
|
||||
|
||||
# Card:set_sprites()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "if not self.children.back then"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
if _center.set_sprites and type(_center.set_sprites) == 'function' then
|
||||
_center:set_sprites(self, _front)
|
||||
end
|
||||
if true then'''
|
||||
|
||||
# Card:load()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = 'if self.config.center.name == "Half Joker" then'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj.load and type(obj.load) == 'function' then
|
||||
obj:load(self, cardTable, other_card)
|
||||
elseif self.config.center.name == "Half Joker" then'''
|
||||
|
||||
# Card:calculate_dollar_bonus()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = """function Card:calculate_dollar_bonus()
|
||||
if self.debuff then return end"""
|
||||
position = "after"
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = self.config.center
|
||||
if obj.calc_dollar_bonus and type(obj.calc_dollar_bonus) == 'function' then
|
||||
return obj:calc_dollar_bonus(self)
|
||||
end
|
||||
'''
|
||||
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = '--If the card has any edition/seal, add that here'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local center = self.config.center
|
||||
if center.draw and type(center.draw) == 'function' then
|
||||
center:draw(self, layer)
|
||||
end
|
||||
if center.set == 'Default' or center.set == 'Enhanced' and not center.replace_base_card then
|
||||
if not center.no_suit then
|
||||
local suit = SMODS.Suits[self.base.suit] or {}
|
||||
if suit.draw and type(suit.draw) == 'function' then
|
||||
suit:draw(self, layer)
|
||||
end
|
||||
end
|
||||
if not center.no_rank then
|
||||
local rank = SMODS.Ranks[self.base.value] or {}
|
||||
if rank.draw and type(rank.draw) == 'function' then
|
||||
rank:draw(self, layer)
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = 'if self.seal then'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local seal = G.P_SEALS[self.seal or {}] or {}
|
||||
if type(seal.draw) == 'function' then
|
||||
seal:draw(self, layer)
|
||||
elseif self.seal then
|
||||
'''
|
||||
|
||||
# no_blueprint check
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = 'if other_joker and other_joker ~= self then'
|
||||
payload = 'if other_joker and other_joker ~= self and not context.no_blueprint then'
|
||||
|
||||
# extract joker loc_vars
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = 'function Card:generate_UIBox_ability_table()'
|
||||
payload = 'function Card:generate_UIBox_ability_table(vars_only)'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
pattern = 'local badges = {}'
|
||||
payload = 'if vars_only then return loc_vars, main_start, main_end end'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
pattern = "elseif _c.set == 'Joker' then"
|
||||
payload = '''
|
||||
if not card then
|
||||
local ability = copy_table(_c.config)
|
||||
ability.set = 'Joker'
|
||||
ability.name = _c.name
|
||||
local ret = {Card.generate_UIBox_ability_table({ ability = ability, config = { center = _c }, bypass_lock = true}, true)}
|
||||
specific_vars = ret[1]
|
||||
if ret[2] then desc_nodes[#desc_nodes+1] = ret[2] end
|
||||
main_end = ret[3]
|
||||
end
|
||||
'''
|
|
@ -1,21 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
# function G.UIDEF.challenge_list_page()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "local challenge_unlocked = G.PROFILES[G.SETTINGS.profile].challenges_unlocked and (G.PROFILES[G.SETTINGS.profile].challenges_unlocked >= k)"
|
||||
position = 'after'
|
||||
payload = """
|
||||
if v.unlocked and type(v.unlocked) == 'function' then
|
||||
challenge_unlocked = v:unlocked()
|
||||
elseif type(v.unlocked) == 'boolean' then
|
||||
challenge_unlocked = v.unlocked
|
||||
end
|
||||
challenge_unlocked = challenge_unlocked or G.PROFILES[G.SETTINGS.profile].all_unlocked
|
||||
|
||||
"""
|
||||
match_indent = true
|
|
@ -1,65 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
|
||||
#
|
||||
# End of round money
|
||||
#
|
||||
|
||||
# Hide off screen rows
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if config.name ~= 'bottom' then"
|
||||
position = "after"
|
||||
payload = '''
|
||||
total_cashout_rows = (total_cashout_rows or 0) + 1
|
||||
if total_cashout_rows > 7 then
|
||||
return
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# Reset rows amount
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = 'G\.FUNCS\.evaluate_round = function\(\)'
|
||||
position = "after"
|
||||
payload = '''
|
||||
|
||||
total_cashout_rows = 0'''
|
||||
|
||||
# Add UI row with total rows hidden
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "add_round_eval_row({name = 'bottom', dollars = dollars})"
|
||||
position = "before"
|
||||
payload = '''
|
||||
if total_cashout_rows > 7 then
|
||||
local total_hidden = total_cashout_rows - 7
|
||||
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'before',delay = 0.38,
|
||||
func = function()
|
||||
local hidden = {n=G.UIT.R, config={align = "cm"}, nodes={
|
||||
{n=G.UIT.O, config={object = DynaText({
|
||||
string = {localize{type = 'variable', key = 'cashout_hidden', vars = {total_hidden}}},
|
||||
colours = {G.C.WHITE}, shadow = true, float = false,
|
||||
scale = 0.45,
|
||||
font = G.LANGUAGES['en-us'].font, pop_in = 0
|
||||
})}}
|
||||
}}
|
||||
|
||||
G.round_eval:add_child(hidden, G.round_eval:get_UIE_by_ID('bonus_round_eval'))
|
||||
return true
|
||||
end
|
||||
}))
|
||||
end'''
|
||||
match_indent = true
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
# fallback for card.ability.name
|
||||
# Card:set_ability()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "self.ability.bonus = (self.ability.bonus or 0) + (center.config.bonus or 0)"
|
||||
position = 'after'
|
||||
payload = "if not self.ability.name then self.ability.name = center.key end"
|
||||
match_indent = true
|
||||
|
||||
# generate_card_ui()
|
||||
# `card_type` is used to check whether card should be nil; non-recursive calls
|
||||
# to generate_card_ui always have that arg set
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '(?<indent>[\t ]*)function generate_card_ui\([^)]*\)\n'
|
||||
position = "after"
|
||||
line_prepend = '$indent'
|
||||
payload = """
|
||||
if card == nil and card_type then
|
||||
card = SMODS.compat_0_9_8.generate_UIBox_ability_table_card
|
||||
end
|
||||
|
||||
"""
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "for _, v in ipairs(info_queue) do"
|
||||
position = 'before'
|
||||
payload = "SMODS.compat_0_9_8.generate_UIBox_ability_table_card = nil"
|
||||
match_indent = true
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "self.SPEEDFACTOR = 1"
|
||||
position = "after"
|
||||
payload = "initSteamodded()"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.copy]
|
||||
target = "main.lua"
|
||||
position = "append"
|
||||
sources = ["src/core.lua"]
|
||||
|
||||
[[patches]]
|
||||
[patches.module]
|
||||
before = "main.lua"
|
||||
source = "version.lua"
|
||||
name = "SMODS.version"
|
|
@ -1,20 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "main.lua"
|
||||
pattern = "function love.errhand(msg)"
|
||||
position = "at"
|
||||
payload = "if false then"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.copy]
|
||||
target = "main.lua"
|
||||
position = "prepend"
|
||||
sources = [
|
||||
"src/crash_handler.lua",
|
||||
]
|
|
@ -1,218 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
#========================================================#
|
||||
# Choose any rank for custom deck and use provided atlas #
|
||||
#========================================================#
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = '''if _front and _front.suit and \(_front.value == 'Jack' or _front.value == 'Queen' or _front.value == 'King'\) then([\s\S]*?)end([\s\S]*?)end([\s\S]*?)end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if _front and _front.suit and G.SETTINGS.CUSTOM_DECK and G.SETTINGS.CUSTOM_DECK.Collabs then
|
||||
local collab = G.SETTINGS.CUSTOM_DECK.Collabs[_front.suit]
|
||||
if collab then
|
||||
local deckSkin = SMODS.DeckSkins[collab]
|
||||
if deckSkin then
|
||||
if deckSkin.outdated then
|
||||
local hasRank = false
|
||||
for i = 1, #deckSkin.ranks do
|
||||
if deckSkin.ranks[i] == _front.value then hasRank = true break end
|
||||
end
|
||||
if hasRank then
|
||||
local atlas = G.ASSET_ATLAS[G.SETTINGS.colour_palettes[_front.suit] == 'hc' and deckSkin.hc_atlas or deckSkin.lc_atlas]
|
||||
if atlas then
|
||||
if deckSkin.pos_style == 'collab' then
|
||||
return atlas, G.COLLABS.pos[_front.value]
|
||||
elseif deckSkin.pos_style == 'suit' then
|
||||
return atlas, { x = _front.pos.x, y = 0}
|
||||
elseif deckSkin.pos_style == 'deck' then
|
||||
return atlas, _front.pos
|
||||
elseif deckSkin.pos_style == 'ranks' or nil then
|
||||
for i, rank in ipairs(deckSkin.ranks) do
|
||||
if rank == _front.value then
|
||||
return atlas, { x = i - 1, y = 0}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return G.ASSET_ATLAS[G.SETTINGS.colour_palettes[_front.suit] == 'hc' and _front.hc_atlas or _front.lc_atlas or {}] or G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(G.SETTINGS.colour_palettes[_front.suit] == 'hc' and 2 or 1)], _front.pos
|
||||
else
|
||||
local palette = deckSkin.palette_map and deckSkin.palette_map[G.SETTINGS.colour_palettes[_front.suit] or ''] or (deckSkin.palettes or {})[1]
|
||||
local hasRank = false
|
||||
for i = 1, #palette.ranks do
|
||||
if palette.ranks[i] == _front.value then hasRank = true break end
|
||||
end
|
||||
if hasRank then
|
||||
local atlas = G.ASSET_ATLAS[palette.atlas]
|
||||
if type(palette.pos_style) == "table" then
|
||||
if palette.pos_style[_front.value] then
|
||||
if palette.pos_style[_front.value].atlas then
|
||||
atlas = G.ASSET_ATLAS[palette.pos_style[_front.value].atlas]
|
||||
end
|
||||
if palette.pos_style[_front.value].pos then
|
||||
return atlas, palette.pos_style[_front.value].pos
|
||||
end
|
||||
elseif palette.pos_style.fallback_style then
|
||||
if palette.pos_style.fallback_style == 'collab' then
|
||||
return atlas, G.COLLABS.pos[_front.value]
|
||||
elseif palette.pos_style.fallback_style == 'suit' then
|
||||
return atlas, { x = _front.pos.x, y = 0}
|
||||
elseif palette.pos_style.fallback_style == 'deck' then
|
||||
return atlas, _front.pos
|
||||
end
|
||||
end
|
||||
elseif palette.pos_style == 'collab' then
|
||||
return atlas, G.COLLABS.pos[_front.value]
|
||||
elseif palette.pos_style == 'suit' then
|
||||
return atlas, { x = _front.pos.x, y = 0}
|
||||
elseif palette.pos_style == 'deck' then
|
||||
return atlas, _front.pos
|
||||
elseif palette.pos_style == 'ranks' or nil then
|
||||
for i, rank in ipairs(palette.ranks) do
|
||||
if rank == _front.value then
|
||||
return atlas, { x = i - 1, y = 0}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return G.ASSET_ATLAS[palette.hc_default and _front.hc_atlas or _front.lc_atlas or {}] or G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(palette.hc_default and 2 or 1)], _front.pos
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "{n=G.UIT.O, config={object = face_cards}}"
|
||||
position = "at"
|
||||
payload = "{n=G.UIT.O, config={object = G.cdds_cards}}"
|
||||
match_indent = true
|
||||
overwrite = false
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "table.insert(t, create_toggle({label = localize('b_high_contrast_cards'), ref_table = G.SETTINGS, ref_value = 'colourblind_option', callback = G.FUNCS.refresh_contrast_mode}))"
|
||||
position = "at"
|
||||
payload = '''
|
||||
local deckskin_key = G.COLLABS.options[_suit][current_option]
|
||||
|
||||
local palette_loc_options = SMODS.DeckSkin.get_palette_loc_options(deckskin_key, _suit)
|
||||
|
||||
local selected_palette = 1
|
||||
for i, v in ipairs(G.COLLABS.colour_palettes[deckskin_key]) do
|
||||
if G.SETTINGS.colour_palettes[_suit] == v then
|
||||
selected_palette = i
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(t,
|
||||
{n=G.UIT.R, config={align = "cm", id = 'palette_selector'}, nodes={
|
||||
create_option_cycle({options = palette_loc_options, w = 5.5, cycle_shoulders = false, curr_suit = _suit, curr_skin = deckskin_key, opt_callback = 'change_colour_palette', current_option = selected_palette, colour = G.C.ORANGE, focus_args = {snap_to = true, nav = 'wide'}}),
|
||||
}}
|
||||
)
|
||||
'''
|
||||
match_indent = true
|
||||
overwrite = false
|
||||
|
||||
#=======================#
|
||||
# Extend custom deck ui #
|
||||
#=======================#
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''local face_cards = CardArea\(([\s\S]*?)\)'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
local rankCount = 0
|
||||
local lookup = {}
|
||||
for i, s in ipairs(SMODS.Suit:obj_list(true)) do
|
||||
local options = G.COLLABS.options[s.key]
|
||||
for i = 1, #options do
|
||||
local skin = SMODS.DeckSkins[options[i]]
|
||||
if skin.palettes and not (skin.display_ranks or skin.ranks) then
|
||||
for _, p in ipairs(skin.palettes) do
|
||||
local p_ranks = p.display_ranks or p.ranks
|
||||
for j = 1, #p_ranks do
|
||||
if not lookup[p_ranks[j]] then
|
||||
lookup[p_ranks[j]] = true
|
||||
rankCount = rankCount + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif not skin.palettes and (skin.display_ranks or skin.ranks) then
|
||||
local ranks = skin.display_ranks or skin.ranks
|
||||
for j = 1, #ranks do
|
||||
if not lookup[skin.ranks[j]] then
|
||||
lookup[skin.ranks[j]] = true
|
||||
rankCount = rankCount + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
G.cdds_cards = CardArea(
|
||||
0,0,
|
||||
math.min(math.max(rankCount*G.CARD_W*0.6, 4*G.CARD_W), 10*G.CARD_W),
|
||||
1.4*G.CARD_H,
|
||||
{card_limit = rankCount, type = 'title', highlight_limit = 0})
|
||||
|
||||
G.cdds_cards.rankCount = rankCount
|
||||
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''for i = 1, 3 do([\s\S]*?)end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
G.FUNCS.update_collab_cards(current_option, _suit, true)
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''function create_UIBox_customize_deck()([\s\S]*?)end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
function create_UIBox_customize_deck()
|
||||
local suitTabs = {}
|
||||
|
||||
local index = 1
|
||||
for i, suit in ipairs(SMODS.Suit:obj_list(true)) do
|
||||
if G.COLLABS.options[suit.key] then
|
||||
suitTabs[index] = {
|
||||
label = localize(suit.key, 'suits_plural'),
|
||||
tab_definition_function = G.UIDEF.custom_deck_tab,
|
||||
tab_definition_function_args = suit.key
|
||||
}
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
|
||||
if suitTabs[1] then
|
||||
suitTabs[1].chosen = true
|
||||
end
|
||||
|
||||
local t = create_UIBox_generic_options({ back_func = 'options', snap_back = nil, contents = {
|
||||
{n=G.UIT.R, config={align = "cm", padding = 0}, nodes={
|
||||
create_tabs(
|
||||
{tabs = suitTabs, snap_to_nav = true, no_shoulders = true}
|
||||
)}}}
|
||||
})
|
||||
|
||||
return t
|
||||
end
|
||||
'''
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Dollar row patches (API removed)
|
||||
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if num_dollars > 60 then"
|
||||
position = "at"
|
||||
payload = '''
|
||||
if num_dollars > 60 or num_dollars < -60 then'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "local dollar_string = localize('$')..num_dollars"
|
||||
position = "at"
|
||||
payload = '''
|
||||
if num_dollars < 0 then --if negative
|
||||
G.E_MANAGER:add_event(Event({
|
||||
trigger = 'before',delay = 0.38,
|
||||
func = function()
|
||||
G.round_eval:add_child(
|
||||
{n=G.UIT.R, config={align = "cm", id = 'dollar_row_'..(dollar_row+1)..'_'..config.name}, nodes={
|
||||
{n=G.UIT.O, config={object = DynaText({string = {'-'..localize('$')..format_ui_value(-num_dollars)}, colours = {G.C.MONEY}, shadow = true, pop_in = 0, scale = 0.65, float = true})}}
|
||||
}},
|
||||
G.round_eval:get_UIE_by_ID('dollar_'..config.name))
|
||||
play_sound('coin3', 0.9+0.2*math.random(), 0.7)
|
||||
play_sound('coin6', 1.3, 0.8)
|
||||
return true
|
||||
end
|
||||
}))
|
||||
else --if positive
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "for i = 1, num_dollars or 1 do"
|
||||
position = "at"
|
||||
payload = '''
|
||||
local dollars_to_loop
|
||||
if num_dollars < 0 then dollars_to_loop = (num_dollars*-1)+1 else dollars_to_loop = num_dollars end
|
||||
for i = 1, dollars_to_loop do'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''(?<indent>[\t ]*)else\n[\t ]*local dollars_to_loop'''
|
||||
position = "before"
|
||||
line_prepend = "$indent"
|
||||
payload = '''
|
||||
--asdf
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "local r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.MONEY, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}}"
|
||||
position = "at"
|
||||
payload = '''
|
||||
local r
|
||||
if i == 1 and num_dollars < 0 then
|
||||
r = {n=G.UIT.T, config={text = '-', colour = G.C.RED, scale = ((num_dollars < -20 and 0.28) or (num_dollars < -9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}}
|
||||
play_sound('coin3', 0.9+0.2*math.random(), 0.7 - (num_dollars < -20 and 0.2 or 0))
|
||||
else
|
||||
if num_dollars < 0 then r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.RED, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}}
|
||||
else r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.MONEY, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}} end
|
||||
end'''
|
||||
match_indent = true
|
|
@ -1,390 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
# Fix debug mode edition cycling
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "engine/controller.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)local _edition = \{
|
||||
[\t ]*foil = not _card\.edition,
|
||||
[\t ]*holo = _card\.edition and _card\.edition\.foil,
|
||||
[\t ]*polychrome = _card\.edition and _card\.edition\.holo,
|
||||
[\t ]*negative = _card\.edition and _card\.edition\.polychrome,
|
||||
[\t ]*\}'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
local found_index = 1
|
||||
if _card.edition then
|
||||
for i, v in ipairs(G.P_CENTER_POOLS.Edition) do
|
||||
if v.key == _card.edition.key then
|
||||
found_index = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
found_index = found_index + 1
|
||||
if found_index > #G.P_CENTER_POOLS.Edition then found_index = found_index - #G.P_CENTER_POOLS.Edition end
|
||||
local _edition = G.P_CENTER_POOLS.Edition[found_index].key'''
|
||||
line_prepend = "$indent"
|
||||
|
||||
|
||||
# Sort P_CENTER_POOLS["Editions"]
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = 'table.sort(self.P_CENTER_POOLS["Enhanced"], function (a, b) return a.order < b.order end)'
|
||||
position = 'after'
|
||||
payload = 'table.sort(self.P_CENTER_POOLS["Edition"], function (a, b) return a.order < b.order end)'
|
||||
match_indent = true
|
||||
|
||||
|
||||
# generate_card_ui()
|
||||
# Adds tooltips for all editions
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)if v == 'foil' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_foil'\] end
|
||||
[\t ]*if v == 'holographic' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_holo'\] end
|
||||
[\t ]*if v == 'polychrome' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_polychrome'\] end
|
||||
[\t ]*if v == 'negative' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_negative'\] end
|
||||
[\t ]*if v == 'negative_consumable' then info_queue\[#info_queue\+1\] = \{key = 'e_negative_consumable', set = 'Edition', config = \{extra = 1\}\} end'''
|
||||
position = 'at'
|
||||
payload = '''
|
||||
v = (v == 'holographic' and 'holo' or v)
|
||||
if v:sub(1,9) == 'negative_' then
|
||||
info_queue[#info_queue+1] = {key = 'e_'..v, set = 'Edition', config = {extra = G.P_CENTERS['e_negative'].config.card_limit}}
|
||||
end
|
||||
if G.P_CENTERS[v] and G.P_CENTERS[v].set == 'Edition' then
|
||||
info_queue[#info_queue + 1] = G.P_CENTERS[v]
|
||||
end
|
||||
if G.P_CENTERS['e_'..v] and G.P_CENTERS['e_'..v].set == 'Edition' then
|
||||
local t = {key = 'e_'..v, set = 'Edition', config = {}}
|
||||
info_queue[#info_queue + 1] = t
|
||||
if G.P_CENTERS['e_'..v].loc_vars and type(G.P_CENTERS['e_'..v].loc_vars) == 'function' then
|
||||
local res = G.P_CENTERS['e_'..v]:loc_vars(info_queue, card) or {}
|
||||
t.vars = res.vars
|
||||
t.key = res.key or t.key
|
||||
t.set = res.set or t.set
|
||||
end
|
||||
end'''
|
||||
line_prepend = "$indent"
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = '''localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars}'''
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = _c.vars or loc_vars}'''
|
||||
|
||||
|
||||
# get_badge_colour()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for _, v in ipairs(G.P_CENTER_POOLS.Edition) do
|
||||
G.BADGE_COL[v.key:sub(3)] = v.badge_colour
|
||||
end'''
|
||||
|
||||
# Limit ARGS.send_to_shader[1] to wiggle between 0 and 2 instead of growing infinitely
|
||||
# this makes shaders responsiveness on tilt reliable over time
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
G\.TIMERS\.REAL/\(28\)'''
|
||||
position = "at"
|
||||
payload = '''math.sin(G.TIMERS.REAL/28) + 1'''
|
||||
|
||||
# Allow editions to not draw shadow
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
self\.ability\.effect ~= 'Glass Card' and not self\.greyed'''
|
||||
position = "after"
|
||||
payload = ''' and self:should_draw_shadow() '''
|
||||
|
||||
# If shader modifies shape of card, this will stop "back" layer of the card being rendered.
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
elseif not self.greyed then'''
|
||||
position = "before"
|
||||
payload = '''
|
||||
elseif not self:should_draw_base_shader() then
|
||||
-- Don't render base dissolve shader.
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
# If shader modifies shape of card, this will stop "back" layer of the card being rendered.
|
||||
# spectral cards and booster packs only.
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
if self.ability.set == 'Booster' or self.ability.set == 'Spectral' then'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if (self.ability.set == 'Booster' or self.ability.set == 'Spectral') and self:should_draw_base_shader() then'''
|
||||
match_indent = true
|
||||
|
||||
# If shader modifies shape of card, this will stop "back" layer of the card being rendered.
|
||||
# invisible joker and vouchers.
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
self.children.center:draw_shader('voucher', nil, self.ARGS.send_to_shader)'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if self:should_draw_base_shader() then
|
||||
self.children.center:draw_shader('voucher', nil, self.ARGS.send_to_shader)
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# Inject shaders applying to cards
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)if self\.edition and self\.editi[A-z\.\:\n\t _(',)~=]*me', nil, self.ARGS.send_to_shader\)
|
||||
[\t ]*end
|
||||
[\t ]*end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if self.edition then
|
||||
for k, v in pairs(G.P_CENTER_POOLS.Edition) do
|
||||
if self.edition[v.key:sub(3)] and v.shader then
|
||||
if type(v.draw) == 'function' then
|
||||
v:draw(self, layer)
|
||||
else
|
||||
self.children.center:draw_shader(v.shader, nil, self.ARGS.send_to_shader)
|
||||
if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then
|
||||
self.children.front:draw_shader(v.shader, nil, self.ARGS.send_to_shader)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end'''
|
||||
line_prepend = "$indent"
|
||||
|
||||
# Inject shaders applying to floating sprites
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "self.children.floating_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)"
|
||||
position = "after"
|
||||
payload = '''
|
||||
if self.edition then
|
||||
for k, v in pairs(G.P_CENTER_POOLS.Edition) do
|
||||
if v.apply_to_float then
|
||||
if self.edition[v.key:sub(3)] then
|
||||
self.children.floating_sprite:draw_shader(v.shader, nil, nil, nil, self.children.center, scale_mod, rotate_mod)
|
||||
end
|
||||
end
|
||||
end
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# Remove prefix from shader key when calling send()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/sprite.lua"
|
||||
pattern = "if _send then G.SHADERS[_shader or 'dissolve']:send(_shader,_send) end"
|
||||
position = "at"
|
||||
payload = '''
|
||||
if _send then
|
||||
G.SHADERS[_shader or 'dissolve']:send((SMODS.Shaders[_shader or 'dissolve'] and SMODS.Shaders[_shader or 'dissolve'].original_key) or _shader,_send)
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# Inject change to edition cost in shop
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '(?<indent>[\t ]*)self.ex([a-z._\s=+(0-9)]*)\n([\t ]*)([a-z._\s=+(0-9)]*)or 0\)'
|
||||
position = "at"
|
||||
payload = '''
|
||||
for k, v in pairs(G.P_CENTER_POOLS.Edition) do
|
||||
if self.edition[v.key:sub(3)] then
|
||||
if v.extra_cost then
|
||||
self.extra_cost = self.extra_cost + v.extra_cost
|
||||
end
|
||||
end
|
||||
end'''
|
||||
line_prepend = "$indent"
|
||||
|
||||
## Fix card_limit logic
|
||||
# Card:add_to_deck()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if self\.edition[A-z\.\:\n\t _(',)~=+\-0-9]*limit \+ 1'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if true then
|
||||
if from_debuff then
|
||||
self.ability.joker_added_to_deck_but_debuffed = nil
|
||||
else
|
||||
if self.edition and self.edition.card_limit then
|
||||
if self.ability.consumeable then
|
||||
G.consumeables.config.card_limit = G.consumeables.config.card_limit + self.edition.card_limit
|
||||
else
|
||||
G.jokers.config.card_limit = G.jokers.config.card_limit + self.edition.card_limit
|
||||
end'''
|
||||
line_prepend = "$indent"
|
||||
# Card:remove_from_deck()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if self\.edition[A-z\.\:\n\t _(',)~=+\-0-9]*limit \- 1'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if G.jokers then
|
||||
if from_debuff then
|
||||
self.ability.joker_added_to_deck_but_debuffed = true
|
||||
else
|
||||
if self.edition and self.edition.card_limit then
|
||||
if self.ability.consumeable then
|
||||
G.consumeables.config.card_limit = G.consumeables.config.card_limit - self.edition.card_limit
|
||||
elseif self.ability.set == 'Joker' then
|
||||
G.jokers.config.card_limit = G.jokers.config.card_limit - self.edition.card_limit
|
||||
end'''
|
||||
line_prepend = "$indent"
|
||||
# Card:remove()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if self\.ability\.queue_neg[A-z\.\:\n\t _(',)~=+\-0-9]*limit \- 1'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if self.ability.joker_added_to_deck_but_debuffed then
|
||||
if self.edition and self.edition.card_limit then
|
||||
if self.ability.consumeable then
|
||||
G.consumeables.config.card_limit = G.consumeables.config.card_limit - self.edition.card_limit
|
||||
elseif self.ability.set == 'Joker' then
|
||||
G.jokers.config.card_limit = G.jokers.config.card_limit - self.edition.card_limit
|
||||
end'''
|
||||
line_prepend = "$indent"
|
||||
# Card:save()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "added_to_deck = self.added_to_deck,"
|
||||
position = "after"
|
||||
payload = "joker_added_to_deck_but_debuffed = self.joker_added_to_deck_but_debuffed,"
|
||||
match_indent = true
|
||||
|
||||
|
||||
|
||||
## Negative playing card logic
|
||||
# CardArea:emplace()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "cardarea.lua"
|
||||
pattern = "function CardArea:emplace(*"
|
||||
position = "after"
|
||||
payload = '''
|
||||
if card.edition and card.edition.card_limit and (self == G.hand) then
|
||||
self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) + card.edition.card_limit
|
||||
self.config.card_limit = math.max(0, self.config.real_card_limit)
|
||||
end'''
|
||||
match_indent = true
|
||||
# CardArea:remove_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "cardarea.lua"
|
||||
pattern = "card:remove_from_area()"
|
||||
position = "before"
|
||||
payload = '''
|
||||
if card.edition and card.edition.card_limit and (self == G.hand) then
|
||||
self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) - card.edition.card_limit
|
||||
self.config.card_limit = math.max(0, self.config.real_card_limit)
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.draw_from_deck_to_hand()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "local hand_space = e or*"
|
||||
position = "at"
|
||||
payload = """local hand_space = e
|
||||
if not hand_space then
|
||||
local limit = G.hand.config.card_limit - #G.hand.cards
|
||||
local n = 0
|
||||
while n < #G.deck.cards do
|
||||
local card = G.deck.cards[#G.deck.cards-n]
|
||||
limit = limit - 1 + (card.edition and card.edition.card_limit or 0)
|
||||
if limit < 0 then break end
|
||||
n = n + 1
|
||||
end
|
||||
hand_space = n
|
||||
end"""
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "badges[#badges + 1] = 'negative_consumable'"
|
||||
position = "after"
|
||||
payload = """
|
||||
elseif self.edition.type == 'negative' and (self.ability.set == 'Enhanced' or self.ability.set == 'Default') then
|
||||
badges[#badges + 1] = 'negative_playing_card'"""
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/sprite.lua"
|
||||
pattern = "love.graphics.setShader( G.SHADERS[_shader or 'dissolve'], G.SHADERS[_shader or 'dissolve'])"
|
||||
position = "before"
|
||||
payload = '''
|
||||
local p_shader = SMODS.Shader.obj_table[_shader or 'dissolve']
|
||||
if p_shader and type(p_shader.send_vars) == "function" then
|
||||
local sh = G.SHADERS[_shader or 'dissolve']
|
||||
local parent_card = self.role.major and self.role.major:is(Card) and self.role.major
|
||||
local send_vars = p_shader.send_vars(self, parent_card)
|
||||
|
||||
if type(send_vars) == "table" then
|
||||
for key, value in pairs(send_vars) do
|
||||
sh:send(key, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "if v == 'negative_consumable' then v = 'negative' end"
|
||||
position = "at"
|
||||
payload = '''if v == 'negative_consumable' or v == 'negative_playing_card' then v = 'negative' end'''
|
||||
match_indent = true
|
||||
|
||||
#
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = '''(?<indent>[\t ]*)(?<edi>local edition = poll_edition\('edi'\.\.\(key_append or ''\)\.\.G\.GAME\.round_resets\.ante\)(\n.*){2})'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
if not SMODS.bypass_create_card_edition then
|
||||
$edi
|
||||
end'''
|
|
@ -1,269 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
## no_rank, no_suit, all_suits
|
||||
|
||||
# Card:get_id()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if self.ability.effect == 'Stone Card' and not self.vampired then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if SMODS.has_no_rank(self) and not self.vampired then'''
|
||||
|
||||
# Card:get_chip_bonus()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)if self\.ability\.effect == 'Stone Card' then
|
||||
[\t ]* return self\.ability\.bonus \+ \(self\.ability\.perma_bonus or 0\)
|
||||
[\t ]*end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if self.ability.effect == 'Stone Card' or self.config.center.replace_base_card then
|
||||
return self.ability.bonus + (self.ability.perma_bonus or 0)
|
||||
end'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# Card:calculate_joker()
|
||||
# Raised Fist
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if temp_ID >= G.hand.cards[i].base.id and G.hand.cards[i].ability.effect ~= 'Stone Card' then temp_Mult = G.hand.cards[i].base.nominal; temp_ID = G.hand.cards[i].base.id; raised_card = G.hand.cards[i] end"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = """if temp_ID >= G.hand.cards[i].base.id and not SMODS.has_no_rank(G.hand.cards[i]) then
|
||||
temp_Mult = G.hand.cards[i].base.nominal
|
||||
temp_ID = G.hand.cards[i].base.id
|
||||
raised_card = G.hand.cards[i]
|
||||
end"""
|
||||
# Flower Pot, Seeing Double
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if context.scoring_hand[i].ability.name ~= 'Wild Card' then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if not SMODS.has_any_suit(context.scoring_hand[i]) then'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if context.scoring_hand[i].ability.name == 'Wild Card' then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if SMODS.has_any_suit(context.scoring_hand[i]) then'''
|
||||
|
||||
# Card:get_suit()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if self\.ability\.effect == 'Stone Card' then'''
|
||||
line_prepend = '$indent'
|
||||
position = "at"
|
||||
payload = '''if SMODS.has_no_suit(self) then'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = 'if self.ability.name == "Wild Card" then'
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if SMODS.has_any_suit(self) then'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = 'if self.ability.name == "Wild Card" and not self.debuff then'
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if SMODS.has_any_suit(self) and not self.debuff then'''
|
||||
|
||||
# check_for_unlock
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if v.ability.name ~= 'Stone Card' and v.base.suit == 'Hearts' then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = "if not SMODS.has_no_suit(v) and v.base.suit == 'Hearts' then"
|
||||
|
||||
# reset_idol_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "valid_idol_cards[#valid_idol_cards+1] = v"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = """if not SMODS.has_no_suit(v) and not SMODS.has_no_rank(v) then
|
||||
valid_idol_cards[#valid_idol_cards+1] = v
|
||||
end"""
|
||||
|
||||
# reset_mail_rank()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "valid_mail_cards[#valid_mail_cards+1] = v"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = """if not SMODS.has_no_rank(v) then
|
||||
valid_mail_cards[#valid_mail_cards+1] = v
|
||||
end"""
|
||||
|
||||
# reset_castle_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "valid_castle_cards[#valid_castle_cards+1] = v"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = """if not SMODS.has_no_suit(v) then
|
||||
valid_castle_cards[#valid_castle_cards+1] = v
|
||||
end"""
|
||||
|
||||
# G.FUNCS.evaluate_play()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "if G.play.cards[i].ability.effect == 'Stone Card' then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if SMODS.always_scores(G.play.cards[i]) then'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "if scoring_hand[i].ability.effect ~= 'Stone Card' then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if not SMODS.has_no_rank(scoring_hand[i]) then'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "G.GAME.cards_played[scoring_hand[i].base.value].suits[scoring_hand[i].base.suit] = true"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = """if not SMODS.has_no_suit(scoring_hand[i]) then
|
||||
G.GAME.cards_played[scoring_hand[i].base.value].suits[scoring_hand[i].base.suit] = true
|
||||
end"""
|
||||
|
||||
|
||||
## replace_base_card
|
||||
# Determines whether to draw the base card's front or not
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if self.children.front and self.ability.effect ~= 'Stone Card' then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = "if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then"
|
||||
|
||||
# Card:generate_UIBox_ability_table()
|
||||
# replaces two consecutive lines
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if (_c.name == 'Stone Card') then full_UI_table.name = true end"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = "if _c.name == 'Stone Card' or _c.replace_base_card then full_UI_table.name = true"
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if (specific_vars.playing_card and (_c.name ~= 'Stone Card')) then"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = "elseif specific_vars.playing_card then"
|
||||
|
||||
|
||||
## Allow cards to function as multiple enhancements (e.g. from jokers)
|
||||
# Calculate extra enhancements when held in hand at end of round
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "local effects = {G.hand.cards[i]:get_end_of_round_effect()}"
|
||||
position = "at"
|
||||
payload = '''
|
||||
local effects = {[1] = {playing_card = G.hand.cards[i]:get_end_of_round_effect()}}
|
||||
local extra_enhancements = SMODS.get_enhancements(G.hand.cards[i], true)
|
||||
local old_ability = copy_table(G.hand.cards[i].ability)
|
||||
local old_center = G.hand.cards[i].config.center
|
||||
local old_center_key = G.hand.cards[i].config.center_key
|
||||
for k, _ in pairs(extra_enhancements) do
|
||||
if G.P_CENTERS[k] then
|
||||
G.hand.cards[i]:set_ability(G.P_CENTERS[k])
|
||||
G.hand.cards[i].ability.extra_enhancement = k
|
||||
effects[#effects+1] = {[1] = {playing_card = G.hand.cards[i]:get_end_of_round_effect()}}
|
||||
end
|
||||
end
|
||||
G.hand.cards[i].ability = old_ability
|
||||
G.hand.cards[i].config.center = old_center
|
||||
G.hand.cards[i].config.center_key = old_center_key
|
||||
G.hand.cards[i]:set_sprites(old_center)
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
# Prevent blue seal effect on extra enhancements at end of round
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card"
|
||||
pattern = "if self.seal == 'Blue' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then"
|
||||
position = "before"
|
||||
payload = '''
|
||||
if self.extra_enhancement then return ret end
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
# Use the has enhancement function for enhancement gates
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if vv.config.center.key == v.enhancement_gate then"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(vv, v.enhancement_gate) then"
|
||||
match_indent = true
|
||||
|
||||
# Glass Card shattering
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if card.ability.name == 'Glass Card' then"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(card, 'm_glass') then"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "if G.hand.highlighted[i].ability.name == 'Glass Card' then"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(G.hand.highlighted[i], 'm_glass') then"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "if scoring_hand[i].ability.name == 'Glass Card' then"
|
||||
position = "at"
|
||||
payload = "if SMODS.shatters(scoring_hand[i]) then"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "if cards_destroyed[i].ability.name == 'Glass Card' then"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(cards_destroyed[i], 'm_glass') then"
|
||||
match_indent = true
|
||||
|
||||
# Prevent blue seals from applying on quantum enhancement calc
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if self.seal == 'Blue' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then"
|
||||
position = "at"
|
||||
payload = "if self.seal == 'Blue' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit and not self.ability.extra_enhancement then"
|
||||
match_indent = true
|
|
@ -1,643 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Fixes for either base game code or general mod compatibility
|
||||
|
||||
## Mods assume Game:start_run() is called with non-nil argument
|
||||
# G.FUNCS.start_run()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = "G.FUNCS.start_run = function(e, args)"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = "args = args or {}"
|
||||
|
||||
## Allows running the game without Steam being active
|
||||
# love.load()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'main.lua'
|
||||
pattern = "(?<indent>[\t ]*)if not \\(st.init and st:init\\(\\)\\) then\n[\t ]*(?<quit>love.event.quit\\(\\))"
|
||||
position = 'at'
|
||||
root_capture = 'quit'
|
||||
payload = 'st = nil'
|
||||
|
||||
|
||||
## Prevents the game from crashing when hitting play with a corrupt/invalid save file
|
||||
# G.FUNCS.can_continue(e)
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = "if G.SAVED_GAME ~= nil then G.SAVED_GAME = STR_UNPACK(G.SAVED_GAME) end"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = """
|
||||
if G.SAVED_GAME == nil then
|
||||
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
|
||||
e.config.button = nil
|
||||
return _can_continue
|
||||
end
|
||||
"""
|
||||
|
||||
## Fix loading a blind with $0 reward
|
||||
# Blind:load()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'blind.lua'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*) G\.HUD_blind\.alignment\.offset\.y = 0
|
||||
[\t ]*end'''
|
||||
position = 'at'
|
||||
payload = '''
|
||||
end
|
||||
if G.GAME.blind.name and G.GAME.blind.name ~= '' then
|
||||
G.HUD_blind.alignment.offset.y = 0
|
||||
end'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
## Remove incorrect check for Moveable alignment change
|
||||
# Moveable:align_to_major()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'engine/moveable.lua'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)if +self\.alignment\.prev_offset\.x == self\.alignment\.offset\.x[\s\S]*?return end
|
||||
'''
|
||||
position = 'at'
|
||||
payload = 'if not self.alignment.type_list then return end'
|
||||
line_prepend = '$indent'
|
||||
|
||||
## Prevent softlock if booster pack is empty
|
||||
## Crashes the game when you skip too fast on this PR, along with being the culprit for allowing you to skip boosters early
|
||||
# G.FUNCS.can_skip_booster()
|
||||
# [[patches]]
|
||||
# [patches.pattern]
|
||||
# target = 'functions/button_callbacks.lua'
|
||||
# pattern = 'if G.pack_cards and (G.pack_cards.cards[1]) and'
|
||||
# position = 'at'
|
||||
# payload = 'if G.pack_cards and'
|
||||
# match_indent = true
|
||||
|
||||
## Set `G.your_collection.config.collection` to true in all cases
|
||||
# create_UIBox_your_collection_seals()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''\{card_limit = 4, type = 'title', highlight_limit = 0\}'''
|
||||
position = 'at'
|
||||
payload = '''{card_limit = 4, type = 'title', highlight_limit = 0, collection = true}'''
|
||||
|
||||
## Save and load Card.unique_val
|
||||
# Card:save()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "bypass_lock = self.bypass_lock,"
|
||||
position = "after"
|
||||
payload = """
|
||||
unique_val = self.unique_val,
|
||||
unique_val__saved_ID = self.ID,
|
||||
ignore_base_shader = self.ignore_base_shader,
|
||||
ignore_shadow = self.ignore_shadow,"""
|
||||
match_indent = true
|
||||
|
||||
# Card:load()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "self.bypass_lock = cardTable.bypass_lock"
|
||||
position = "after"
|
||||
payload = """
|
||||
self.unique_val = cardTable.unique_val or self.unique_val
|
||||
if cardTable.unique_val__saved_ID and G.ID <= cardTable.unique_val__saved_ID then
|
||||
G.ID = cardTable.unique_val__saved_ID + 1
|
||||
end
|
||||
|
||||
self.ignore_base_shader = cardTable.ignore_base_shader or {}
|
||||
self.ignore_shadow = cardTable.ignore_shadow or {}"""
|
||||
match_indent = true
|
||||
|
||||
## Vars in card descriptions should use `card.ability` instead of `_c.config` where possible
|
||||
## Allow passing in custom vars
|
||||
# generate_card_ui()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = 'function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end)'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end, card)
|
||||
if _c.specific_vars then specific_vars = _c.specific_vars end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if _c.set == 'Other' then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = "local cfg = (card and card.ability) or _c['config']" # string index to make sure the next patch doesn't eat it
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or _c.config.bonus) then"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = "if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or (cfg.bonus ~= 0 and cfg.bonus)) then"
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = '_c.config'
|
||||
position = 'at'
|
||||
payload = 'cfg'
|
||||
|
||||
## When overriding with set_ability and card is added to deck, call add / remove effects
|
||||
# Card:set_ability()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "self.config.center = center"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if self.added_to_deck and old_center and not self.debuff then
|
||||
self:remove_from_deck()
|
||||
self.added_to_deck = true
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "if G.consumeables and self.area == G.consumeables then"
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if self.added_to_deck and old_center and not self.debuff then
|
||||
self.added_to_deck = false
|
||||
self:add_to_deck()
|
||||
end'''
|
||||
|
||||
## set_ability() transfers over old fields
|
||||
# special cases:
|
||||
# extra_value should be transferred
|
||||
# name, effect, set, extra should always be overwritten
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)self\.ability = (\{[\s\S]*?
|
||||
[\t ]*\})
|
||||
'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local new_ability = $2
|
||||
self.ability = self.ability or {}
|
||||
new_ability.extra_value = nil
|
||||
self.ability.extra_value = self.ability.extra_value or 0
|
||||
for k, v in pairs(new_ability) do
|
||||
self.ability[k] = v
|
||||
end
|
||||
-- reset keys do not persist an ability change
|
||||
local reset_keys = {'name', 'effect', 'set', 'extra', 'played_this_ante'}
|
||||
for _, mod in ipairs(SMODS.mod_list) do
|
||||
if mod.set_ability_reset_keys then
|
||||
local keys = mod.set_ability_reset_keys()
|
||||
for _, v in pairs(keys) do table.insert(reset_keys, v) end
|
||||
end
|
||||
end
|
||||
for _, k in ipairs(reset_keys) do
|
||||
self.ability[k] = new_ability[k]
|
||||
end
|
||||
'''
|
||||
|
||||
## Fix crash if self.config.card == nil for non-vanilla set_ability() calls
|
||||
# Card:set_ability()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "self.label = center.label or self.config.card.label or self.ability.set"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = "self.label = center.label or self.config.card and self.config.card.label or self.ability.set"
|
||||
|
||||
### Fix Matador
|
||||
|
||||
# These patches have been removed for altering vanilla behavior. Git blame this line to see what they were
|
||||
|
||||
### Fix Crimson Heart
|
||||
|
||||
## Blind:drawn_to_hand()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.name == 'Crimson Heart' and self.prepped and G.jokers.cards[1] then"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = """
|
||||
local prev_chosen_set = {}
|
||||
local fallback_jokers = {}"""
|
||||
# Fix bad logic if not enough choices for debuff
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'blind.lua'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)for i = 1, #G\.jokers\.cards do
|
||||
[\t ]*if not G\.jokers\.cards\[i\]\.debuff or #G\.jokers\.cards < 2 then jokers\[#jokers\+1\] = ?G\.jokers\.cards\[i\] end
|
||||
[\t ]*G\.jokers\.cards\[i\]:set_debuff\(false\)
|
||||
[\t ]*end'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = """
|
||||
for i = 1, #G.jokers.cards do
|
||||
if G.jokers.cards[i].ability.crimson_heart_chosen then
|
||||
prev_chosen_set[G.jokers.cards[i]] = true
|
||||
G.jokers.cards[i].ability.crimson_heart_chosen = nil
|
||||
if G.jokers.cards[i].debuff then SMODS.recalc_debuff(G.jokers.cards[i]) end
|
||||
end
|
||||
end
|
||||
for i = 1, #G.jokers.cards do
|
||||
if not G.jokers.cards[i].debuff then
|
||||
if not prev_chosen_set[G.jokers.cards[i]] then
|
||||
jokers[#jokers+1] = G.jokers.cards[i]
|
||||
end
|
||||
table.insert(fallback_jokers, G.jokers.cards[i])
|
||||
end
|
||||
end
|
||||
if #jokers == 0 then jokers = fallback_jokers end"""
|
||||
# Add variable for Crimson Heart's choice
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "_card:set_debuff(true)"
|
||||
position = "at"
|
||||
match_indent = true
|
||||
payload = """
|
||||
_card.ability.crimson_heart_chosen = true
|
||||
SMODS.recalc_debuff(_card)"""
|
||||
|
||||
## Blind:debuff_card()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'blind.lua'
|
||||
pattern = '''
|
||||
if self\.name == 'Crimson Heart' and not self\.disabled and card\.area == G\.jokers then\s+
|
||||
((?<indent>[\t ]*)return)'''
|
||||
root_capture = '$1'
|
||||
position = "at"
|
||||
line_prepend = '$indent'
|
||||
payload = """
|
||||
if card.ability.crimson_heart_chosen then
|
||||
card:set_debuff(true);
|
||||
if card.debuff then card.debuffed_by_blind = true end
|
||||
return
|
||||
end"""
|
||||
|
||||
## Blind:press_play()
|
||||
# Shouldn't work with Matador
|
||||
# yes it should
|
||||
|
||||
## Blind:disable()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.name == 'The Water' then"
|
||||
position = 'before'
|
||||
payload = """
|
||||
if self.name == 'Crimson Heart' then
|
||||
for _, v in ipairs(G.jokers.cards) do
|
||||
v.ability.crimson_heart_chosen = nil
|
||||
end
|
||||
end"""
|
||||
match_indent = true
|
||||
|
||||
## Blind:defeat()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'blind.lua'
|
||||
pattern = "if self.name == 'The Manacle' and not self.disabled then"
|
||||
position = 'before'
|
||||
payload = """
|
||||
if self.name == 'Crimson Heart' then
|
||||
for _, v in ipairs(G.jokers.cards) do
|
||||
v.ability.crimson_heart_chosen = nil
|
||||
end
|
||||
end"""
|
||||
match_indent = true
|
||||
|
||||
|
||||
## Fix Manacle's unnecessary card draw after positive G.hand:change_size()
|
||||
# Blind:disable()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'blind.lua'
|
||||
pattern = 'G\.hand:change_size\(1\)(\s+G\.FUNCS\.draw_from_deck_to_hand\(1\))'
|
||||
root_capture = '$1'
|
||||
position = 'at'
|
||||
payload = ""
|
||||
|
||||
#
|
||||
# Money scaling fix
|
||||
#
|
||||
|
||||
## create_UIBox_HUD
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''
|
||||
string = \{\{ref_table = G\.GAME\, ref_value = 'dollars'\, prefix = localize\('\$'\)\}\}\,'''
|
||||
position = "after"
|
||||
payload = '''
|
||||
|
||||
scale_function = function ()
|
||||
return scale_number(G.GAME.dollars, 2.2 * scale, 99999, 1000000)
|
||||
end,'''
|
||||
|
||||
## DynaText:update_text
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/text.lua"
|
||||
pattern = 'self.config.H = 0'
|
||||
position = "after"
|
||||
payload = "self.scale = self.config.scale_function and self.config.scale_function() or self.scale"
|
||||
match_indent = true
|
||||
|
||||
|
||||
#
|
||||
# Fix gold stake legendary infloop
|
||||
# Do not try to roll for jokers that are not in_pool
|
||||
#
|
||||
|
||||
# generate_starting_seed()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = '''if win_ante and (win_ante >= 8) then'''
|
||||
match_indent = true
|
||||
position = "at"
|
||||
payload = '''if win_ante and (win_ante >= 8) or (v.in_pool and type(v.in_pool) == 'function' and not v:in_pool()) then'''
|
||||
|
||||
#
|
||||
# Fix G.GAME.blind:set_blind(nil, true, nil)
|
||||
# being called when not in blind.
|
||||
#
|
||||
|
||||
# Card:add_to_deck
|
||||
# Card:remove_from_deck
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = 'if G\.GAME\.blind then'
|
||||
position = "at"
|
||||
payload = "if G.GAME.blind and G.GAME.blind.in_blind then"
|
||||
|
||||
# Blind:set_blind
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "blind.lua"
|
||||
match_indent = true
|
||||
pattern = "if not reset then"
|
||||
position = "after"
|
||||
payload = '''
|
||||
if blind then
|
||||
self.in_blind = true
|
||||
end'''
|
||||
|
||||
# end_round()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/state_events.lua"
|
||||
pattern = "local game_over = true"
|
||||
position = "before"
|
||||
payload = "G.GAME.blind.in_blind = false"
|
||||
match_indent = true
|
||||
|
||||
|
||||
|
||||
# Make sure new param is loaded
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "blind.lua"
|
||||
match_indent = true
|
||||
pattern = "function Blind:load(blindTable)"
|
||||
position = "after"
|
||||
payload = '''
|
||||
self.in_blind = blindTable.in_blind'''
|
||||
|
||||
# Make sure new param is saved
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "blind.lua"
|
||||
match_indent = true
|
||||
pattern = "local blindTable = {"
|
||||
position = "after"
|
||||
payload = '''
|
||||
in_blind = self.in_blind,'''
|
||||
|
||||
# Cartomancer and astronomer unlock when *actually all* Tarot/Planet cards are discovered
|
||||
# check_for_unlock()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
match_indent = true
|
||||
pattern = "if card.unlock_condition.tarot_count <= args.tarot_count then"
|
||||
position = "at"
|
||||
payload = 'if #G.P_CENTER_POOLS.Tarot <= args.tarot_count then'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
match_indent = true
|
||||
pattern = "if card.unlock_condition.planet_count <= args.planet_count then"
|
||||
position = "at"
|
||||
payload = 'if #G.P_CENTER_POOLS.Planet <= args.planet_count then'
|
||||
|
||||
# wtf
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/animatedsprite.lua"
|
||||
match_indent = true
|
||||
pattern = "for _, v in pairs(G.ANIMATIONS) do"
|
||||
position = "at"
|
||||
payload = 'for k, v in pairs(G.ANIMATIONS) do'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/animatedsprite.lua"
|
||||
match_indent = true
|
||||
pattern = "for _, v in pairs(G.I.SPRITE) do"
|
||||
position = "at"
|
||||
payload = 'for k, v in pairs(G.I.SPRITE) do'
|
||||
|
||||
##
|
||||
## Card:draw() - improved mod compatibility
|
||||
##
|
||||
# Add option for sprites to not be drawn
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
match_indent = true
|
||||
pattern = '''
|
||||
if k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if not v.custom_draw and k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end'''
|
||||
|
||||
# This check is not necessary?
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
match_indent = true
|
||||
pattern = '''
|
||||
if self.edition or self.seal or self.ability.eternal or self.ability.rental or self.ability.perishable or self.sticker or ((self.sticker_run and self.sticker_run ~= 'NONE') and G.SETTINGS.run_stake_stickers) or (self.ability.set == 'Spectral') or self.debuff or self.greyed or (self.ability.name == 'The Soul') or (self.ability.set == 'Voucher') or (self.ability.set == 'Booster') or self.config.center.soul_pos or self.config.center.demo then'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if true then'''
|
||||
|
||||
## Make vanilla enhancement jokers work with extra enhancements
|
||||
|
||||
# Steel Joker
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
match_indent = true
|
||||
pattern = "if v.config.center == G.P_CENTERS.m_steel then self.ability.steel_tally = self.ability.steel_tally+1 end"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(v, 'm_steel') then self.ability.steel_tally = self.ability.steel_tally+1 end"
|
||||
|
||||
# Stone Joker
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
match_indent = true
|
||||
pattern = "if v.config.center == G.P_CENTERS.m_stone then self.ability.stone_tally = self.ability.stone_tally+1 end"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(v, 'm_stone') then self.ability.stone_tally = self.ability.stone_tally+1 end"
|
||||
|
||||
# Golden Ticket
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
match_indent = true
|
||||
pattern = "context.other_card.ability.name == 'Gold Card' then"
|
||||
position = "at"
|
||||
payload = "SMODS.has_enhancement(context.other_card, 'm_gold') then"
|
||||
|
||||
# Golden Ticket Unlock
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
match_indent = true
|
||||
pattern = "if args.cards[j].ability.name == 'Gold Card' then"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(args.cards[j], 'm_gold') then"
|
||||
|
||||
# Glass Joker
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
match_indent = true
|
||||
pattern = "if val.ability.name == 'Glass Card' then shattered_glass = shattered_glass + 1 end"
|
||||
position = "at"
|
||||
payload = "if SMODS.has_enhancement(val, 'm_glass') then shattered_glass = shattered_glass + 1 end"
|
||||
|
||||
# Driver's License
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
match_indent = true
|
||||
pattern = "if v.config.center ~= G.P_CENTERS.c_base then self.ability.driver_tally = self.ability.driver_tally+1 end"
|
||||
position = "at"
|
||||
payload = "if next(SMODS.get_enhancements(v)) then self.ability.driver_tally = self.ability.driver_tally+1 end"
|
||||
# Basegame fix for the reroll vouchers redeem function when only the center but no card object exists
|
||||
|
||||
# Card:apply_to_run
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = """G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost - self.ability.extra"""
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = """G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost - center_table.extra"""
|
||||
|
||||
# Card:apply_to_run
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = """G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost - self.ability.extra)"""
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = """G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost - center_table.extra)"""
|
||||
|
||||
|
||||
# Fix booster skip issues maybe?
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = "if G.pack_cards and (G.pack_cards.cards[1]) and"
|
||||
position = "at"
|
||||
payload = '''
|
||||
if G.pack_cards and (not (G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
# Due to STOP_USE being used we can remove this dumb check
|
||||
# could probably remove the rest of it since it serves no purpose otherwise, but whatever
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = '''and \(G\.hand\.cards\[1\] or \(G\.hand\.config\.card_limit <= 0\)\)'''
|
||||
position = "at"
|
||||
payload = ''' '''
|
||||
|
||||
# Fixes Steam API not loading on unix
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'main.lua'
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
pattern = '--To control when steam communication happens, make sure to send updates to steam as little as possible'
|
||||
payload = '''local cwd = NFS.getWorkingDirectory()
|
||||
NFS.setWorkingDirectory(love.filesystem.getSourceBaseDirectory())
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'main.lua'
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
pattern = '--Set up the render window and the stage for the splash screen, then enter the gameloop with :update'
|
||||
payload = '''NFS.setWorkingDirectory(cwd)
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "main.lua"
|
||||
pattern = '''if os == 'OS X' or os == 'Windows' then'''
|
||||
position = "at"
|
||||
payload = '''if os == 'OS X' or os == 'Windows' or os == 'Linux' then'''
|
||||
overwrite = true
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "main.lua"
|
||||
pattern = '''if os == 'OS X' then'''
|
||||
position = "at"
|
||||
payload = '''if os == 'OS X' or os == 'Linux' then'''
|
||||
overwrite = true
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "main.lua"
|
||||
pattern = "st = require 'luasteam'"
|
||||
position = "at"
|
||||
payload = """local success, _st = pcall(require, 'luasteam')
|
||||
if success then st = _st else sendWarnMessage(_st); st = {} end"""
|
||||
overwrite = true
|
||||
match_indent = true
|
|
@ -1,364 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = 0
|
||||
|
||||
|
||||
# Luchador
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''[ \t]*G\.GAME\.blind:disable\(\)
|
||||
(?<indent>[ \t]*)end'''
|
||||
position = "at"
|
||||
line_prepend = '$indent'
|
||||
payload = ''' G.GAME.blind:disable()
|
||||
return nil, true
|
||||
end'''
|
||||
|
||||
# Diet Cola
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*return true
|
||||
[ \t]*end\)
|
||||
[ \t]*\}\)\)
|
||||
(?<indent>[ \t]*)end
|
||||
'''
|
||||
position = "at"
|
||||
line_prepend = '$indent'
|
||||
payload = ''' return true
|
||||
end)
|
||||
}))
|
||||
return nil, true
|
||||
end
|
||||
'''
|
||||
|
||||
# Invisible Joker
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''[ \t]*if card\.ability\.invis_rounds then card\.ability\.invis_rounds = 0 end
|
||||
[ \t]*card:add_to_deck\(\)
|
||||
(?<indent>[ \t]*)G\.jokers:emplace\(card\)'''
|
||||
position = "after"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Campfire
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''localize\('k_upgrade_ex'\)\}\); return true
|
||||
[ \t]*end\}\)\)
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)return'''
|
||||
position = "at"
|
||||
line_prepend = '$indent'
|
||||
payload = '''localize('k_upgrade_ex')}); return true
|
||||
end}))
|
||||
end
|
||||
if self.ability.name == 'Campfire' and not context.blueprint then return nil, true end'''
|
||||
|
||||
# Flash Card
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''[ \t]*G\.C\.MULT\}\)
|
||||
[ \t]*return true
|
||||
[ \t]*end\)\}\)\)
|
||||
(?<indent>[ \t]*)end'''
|
||||
position = "at"
|
||||
line_prepend = '$indent'
|
||||
payload = '''G.C.MULT})
|
||||
return true
|
||||
end)}))
|
||||
end
|
||||
if self.ability.name == 'Flash Card' and not context.blueprint then return nil, true end'''
|
||||
|
||||
# Perkeo
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''[ \t]*card_eval_status_text\(context\.blueprint_card or self, 'extra', nil, nil, nil, \{message = localize\('k_duplicated_ex'\)\}\)
|
||||
(?<indent>[ \t]*)end'''
|
||||
position = "at"
|
||||
line_prepend = '$indent'
|
||||
payload = ''' card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex')})
|
||||
return nil, true
|
||||
end'''
|
||||
|
||||
# Throwback
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
[ \t]*return
|
||||
(?<indent>[ \t]*)elseif context\.skipping_booster'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Red Card
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
[ \t]*return
|
||||
(?<indent>[ \t]*)elseif context\.playing_card_added'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Hologram
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)elseif context\.first_hand_drawn'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Certificate
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'DNA' and not context\.blueprint'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Chicot
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Madness' '''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Madness
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Burglar' '''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Burglar
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Riff-raff' '''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Riff-raff
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Cartomancer' '''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Cartomancer
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Ceremonial Dagger' and not context.blueprint'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Ceremonial Dagger
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Marble Joker' '''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Marble Joker
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
[ \t]*return
|
||||
(?<indent>[ \t]*)elseif context.destroying_card'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Caino
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''[ \t]*func = function\(\) card_eval_status_text\(self, 'extra', nil, nil, nil, \{message = localize\{type = 'variable', key = 'a_xmult', vars = \{self\.ability\.caino_xmult\}\}\}\); return true
|
||||
(?<indent>[ \t]*)end\}\)\)'''
|
||||
position = "after"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Glass Joker
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''glass_cards\}\}\}\)
|
||||
[ \t]*return true
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)\}\)\)'''
|
||||
position = "after"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
[ \t]*return
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Fortune Teller' and not context\.blueprint'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Fortune Teller
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)if self\.ability\.name == 'Constellation' and not context\.blueprint'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Constellation
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
[ \t]*return
|
||||
(?<indent>[ \t]*)elseif context.debuffed_hand'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "nil, true"
|
||||
|
||||
# Burnt Joker
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
(?<indent>[ \t]*)elseif context.discard'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "return nil, true"
|
||||
|
||||
# Faceless Joker
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
[ \t]*end
|
||||
[ \t]*end
|
||||
[ \t]*return
|
||||
(?<indent>[ \t]*)elseif context.end_of_round'''
|
||||
position = "before"
|
||||
line_prepend = '$indent'
|
||||
payload = "nil, true"
|
||||
|
||||
# Yorick
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "self.ability.yorick_discards = self.ability.yorick_discards - 1"
|
||||
position = "after"
|
||||
match_indent = true
|
||||
payload = "return nil, true"
|
||||
|
||||
# Hallucination
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_plus_tarot'), colour = G.C.PURPLE})"
|
||||
position = "after"
|
||||
match_indent = true
|
||||
payload = "return nil, true"
|
||||
|
||||
## Change card returns
|
||||
# Ramen
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
message = localize('k_eaten_ex'),
|
||||
colour = G.C.FILTER'''
|
||||
position = "before"
|
||||
match_indent = true
|
||||
payload = "card = self,"
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''message = localize{type='variable',key='a_xmult_minus',vars={self.ability.extra}},'''
|
||||
position = "before"
|
||||
match_indent = true
|
||||
payload = "card = self,"
|
||||
|
||||
# Yorick
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
delay = 0.2,
|
||||
message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}},'''
|
||||
position = "before"
|
||||
match_indent = true
|
||||
payload = "card = self,"
|
||||
|
||||
# To Do List
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''message = localize('$')..self.ability.extra.dollars,
|
||||
dollars = self.ability.extra.dollars,'''
|
||||
position = "at"
|
||||
match_indent = true
|
||||
payload = '''message = localize('$')..self.ability.extra.dollars,'''
|
||||
|
||||
# Matador
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''message = localize('$')..self.ability.extra,
|
||||
dollars = self.ability.extra,'''
|
||||
position = "at"
|
||||
match_indent = true
|
||||
payload = '''message = localize('$')..self.ability.extra,'''
|
|
@ -1,73 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = 0
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
if _center.name == 'Square Joker' and (_center.discovered or self.bypass_discovery_center) then
|
||||
self.children.center.scale.y = self.children.center.scale.x
|
||||
end
|
||||
'''
|
||||
position = "after"
|
||||
payload = '''
|
||||
if _center.pixel_size and _center.pixel_size.h and (_center.discovered or self.bypass_discovery_center) then
|
||||
self.children.center.scale.y = self.children.center.scale.y*(_center.pixel_size.h/95)
|
||||
end
|
||||
if _center.pixel_size and _center.pixel_size.w and (_center.discovered or self.bypass_discovery_center) then
|
||||
self.children.center.scale.x = self.children.center.scale.x*(_center.pixel_size.w/71)
|
||||
end
|
||||
'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
if center.name == "Wee Joker" and (center.discovered or self.bypass_discovery_center) then
|
||||
H = H*0.7
|
||||
W = W*0.7
|
||||
self.T.h = H
|
||||
self.T.w = W
|
||||
end
|
||||
'''
|
||||
position = "after"
|
||||
payload = '''
|
||||
if center.display_size and center.display_size.h and (center.discovered or self.bypass_discovery_center) then
|
||||
H = H*(center.display_size.h/95)
|
||||
self.T.h = H
|
||||
elseif center.pixel_size and center.pixel_size.h and (center.discovered or self.bypass_discovery_center) then
|
||||
H = H*(center.pixel_size.h/95)
|
||||
self.T.h = H
|
||||
end
|
||||
if center.display_size and center.display_size.w and (center.discovered or self.bypass_discovery_center) then
|
||||
W = W*(center.display_size.w/71)
|
||||
self.T.w = W
|
||||
elseif center.pixel_size and center.pixel_size.w and (center.discovered or self.bypass_discovery_center) then
|
||||
W = W*(center.pixel_size.w/71)
|
||||
self.T.w = W
|
||||
end
|
||||
'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
self.VT.h = self.T.H
|
||||
self.VT.w = self.T.w
|
||||
'''
|
||||
position = "before"
|
||||
payload = '''
|
||||
if self.config.center.display_size and self.config.center.display_size.h then
|
||||
self.T.h = H*(self.config.center.display_size.h/95)
|
||||
elseif self.config.center.pixel_size and self.config.center.pixel_size.h then
|
||||
self.T.h = H*(self.config.center.pixel_size.h/95)
|
||||
end
|
||||
if self.config.center.display_size and self.config.center.display_size.w then
|
||||
self.T.w = W*(self.config.center.display_size.w/71)
|
||||
elseif self.config.center.pixel_size and self.config.center.pixel_size.w then
|
||||
self.T.w = W*(self.config.center.pixel_size.w/71)
|
||||
end
|
||||
'''
|
||||
match_indent = true
|
|
@ -1,82 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
# Check all registered keybinds
|
||||
# inserted inside Controller:key_press_update
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/controller.lua'
|
||||
pattern = "if not _RELEASE_MODE then"
|
||||
position = "before"
|
||||
payload = '''
|
||||
for _, keybind in pairs(SMODS.Keybinds) do
|
||||
if keybind.action and keybind.key_pressed == key and keybind.event == 'pressed' then
|
||||
local execute = true
|
||||
for _, other_key in pairs(keybind.held_keys) do
|
||||
if not self.held_keys[other_key] then
|
||||
execute = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if execute then
|
||||
keybind:action()
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
# Controller:key_release_update
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/controller.lua'
|
||||
pattern = "function Controller:key_release_update(key, dt)"
|
||||
position = "after"
|
||||
payload = '''
|
||||
for _, keybind in pairs(SMODS.Keybinds) do
|
||||
if keybind.action and keybind.key_pressed == key and keybind.event == 'released' then
|
||||
local execute = true
|
||||
for _, other_key in pairs(keybind.held_keys) do
|
||||
if not self.held_keys[other_key] then
|
||||
execute = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if execute then
|
||||
keybind:action()
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'engine/controller.lua'
|
||||
pattern = 'if key == "r"'
|
||||
position = 'before'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
for _, keybind in pairs(SMODS.Keybinds) do
|
||||
if keybind.key_pressed == key and keybind.event == 'held' and keybind.held_duration then
|
||||
if self.held_key_times[key] > keybind.held_duration then
|
||||
local execute = true
|
||||
for _, other_key in pairs(keybind.held_keys) do
|
||||
if not self.held_keys[other_key] then
|
||||
execute = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if execute then
|
||||
keybind:action()
|
||||
self.held_key_times[key] = nil
|
||||
end
|
||||
else
|
||||
self.held_key_times[key] = self.held_key_times[key] + dt
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
|
@ -1,59 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Language API
|
||||
|
||||
# Game:set_language()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = "if not (love.filesystem.read('localization/'..G.SETTINGS.language..'.lua')) or G.F_ENGLISH_ONLY then"
|
||||
position = 'at'
|
||||
payload = 'if false then'
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = "local localization = love.filesystem.getInfo('localization/'..G.SETTINGS.language..'.lua')"
|
||||
position = 'at'
|
||||
payload = "local localization = love.filesystem.getInfo('localization/'..G.SETTINGS.language..'.lua') or love.filesystem.getInfo('localization/en-us.lua')"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = "self.localization = assert(loadstring(love.filesystem.read('localization/'..G.SETTINGS.language..'.lua')))()"
|
||||
position = 'at'
|
||||
payload = "self.localization = assert(loadstring(love.filesystem.read('localization/'..G.SETTINGS.language..'.lua') or love.filesystem.read('localization/en-us.lua')))()"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = "self.LANG = self.LANGUAGES[self.SETTINGS.language] or self.LANGUAGES['en-us']"
|
||||
position = 'at'
|
||||
payload = "self.LANG = self.LANGUAGES[self.SETTINGS.real_language or self.SETTINGS.language] or self.LANGUAGES['en-us']"
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.change_lang
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = "G.SETTINGS.language = lang.key"
|
||||
position = 'at'
|
||||
payload = """G.SETTINGS.language = lang.loc_key or lang.key
|
||||
G.SETTINGS.real_language = lang.key"""
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.warn_lang (wtf)
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = 'if (_infotip_object.config.set ~= e.config.ref_table.label) and (not G.F_NO_ACHIEVEMENTS) then'
|
||||
position = 'at'
|
||||
payload = 'if (_infotip_object.config.set ~= e.config.ref_table.label) then'
|
||||
match_indent = true
|
|
@ -1,16 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
[[patches]]
|
||||
[patches.module]
|
||||
source = "libs/json/json.lua"
|
||||
before = "main.lua"
|
||||
name = "json"
|
||||
|
||||
[[patches]]
|
||||
[patches.module]
|
||||
source = "libs/nativefs/nativefs.lua"
|
||||
before = "main.lua"
|
||||
name = "nativefs"
|
|
@ -1,27 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
### Supporting code for loader.lua
|
||||
|
||||
## Save discovered, unlocked states
|
||||
# Game:init_item_prototypes()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = "meta.alerted = meta.alerted or {}"
|
||||
position = 'after'
|
||||
payload = '''
|
||||
for _, t in ipairs{
|
||||
G.P_CENTERS,
|
||||
G.P_BLINDS,
|
||||
G.P_TAGS,
|
||||
G.P_SEALS,
|
||||
} do
|
||||
for k, v in pairs(t) do
|
||||
SMODS._save_d_u(v)
|
||||
v._discovered_unlocked_overwritten = true
|
||||
end
|
||||
end'''
|
||||
match_indent = true
|
|
@ -1,60 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''local main_menu = nil'''
|
||||
position = "after"
|
||||
payload = '''local mods = nil'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''main_menu = UIBox_button{ label = {localize('b_main_menu')}, button = "go_to_menu", minw = 5}'''
|
||||
position = "after"
|
||||
payload = '''mods = UIBox_button{ id = "mods_button", label = {localize('b_mods')}, button = "mods_button", minw = 5}'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''G.ARGS.set_alerts_alertables[11].should_alert = alert_booster'''
|
||||
position = "after"
|
||||
payload = '''table.insert(G.ARGS.set_alerts_alertables, {id = 'mods_button', alert_uibox_name = 'mods_button_alert', should_alert = SMODS.mod_button_alert})'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''main_menu,'''
|
||||
position = "after"
|
||||
payload = '''mods,'''
|
||||
match_indent = true
|
||||
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = '''self.ASSET_ATLAS[self.asset_atli[i].name].image = love.graphics.newImage(self.asset_atli[i].path, {mipmaps = true, dpiscale = self.SETTINGS.GRAPHICS.texture_scaling})'''
|
||||
position = 'after'
|
||||
payload = '''
|
||||
local mipmap_level = SMODS.config.graphics_mipmap_level_options[SMODS.config.graphics_mipmap_level]
|
||||
if mipmap_level and mipmap_level > 0 then
|
||||
self.ASSET_ATLAS[self.asset_atli[i].name].image:setMipmapFilter('linear', mipmap_level)
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''create_option_cycle({w = 4,scale = 0.8, label = localize("b_set_CRT_bloom"),options = localize('ml_bloom_opt'), opt_callback = 'change_crt_bloom', current_option = G.SETTINGS.GRAPHICS.bloom}),'''
|
||||
position = 'after'
|
||||
payload = '''
|
||||
create_option_cycle({label = localize('b_graphics_mipmap_level'),scale = 0.8, options = SMODS.config.graphics_mipmap_level_options, opt_callback = 'SMODS_change_mipmap', current_option = SMODS.config.graphics_mipmap_level}),'''
|
||||
match_indent = true
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
### Per-mod functions
|
||||
|
||||
# end_round()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/state_events.lua'
|
||||
pattern = '''(?<indent>[\t ]*)reset_castle_card\(\)'''
|
||||
line_prepend = '$indent'
|
||||
position = 'after'
|
||||
payload = '''
|
||||
for _, mod in ipairs(SMODS.mod_list) do
|
||||
if mod.reset_game_globals and type(mod.reset_game_globals) == 'function' then
|
||||
mod.reset_game_globals(false)
|
||||
end
|
||||
end'''
|
||||
|
||||
# Game:start_run()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'game.lua'
|
||||
pattern = '''(?<indent>[\t ]*)reset_castle_card\(\)'''
|
||||
line_prepend = '$indent'
|
||||
position = 'after'
|
||||
payload = '''
|
||||
for _, mod in ipairs(SMODS.mod_list) do
|
||||
if mod.reset_game_globals and type(mod.reset_game_globals) == 'function' then
|
||||
mod.reset_game_globals(true)
|
||||
end
|
||||
end'''
|
||||
|
||||
# Card:set_debuff()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "function Card:set_debuff(should_debuff)"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for _, mod in ipairs(SMODS.mod_list) do
|
||||
if mod.set_debuff and type(mod.set_debuff) == 'function' then
|
||||
local res = mod.set_debuff(self)
|
||||
if res == 'prevent_debuff' then
|
||||
if self.debuff then
|
||||
self.debuff = false
|
||||
if self.area == G.jokers then self:add_to_deck(true) end
|
||||
self.debuffed_by_blind = false
|
||||
end
|
||||
return
|
||||
end
|
||||
should_debuff = should_debuff or res
|
||||
end
|
||||
end
|
||||
for k, v in pairs(self.ability.debuff_sources or {}) do
|
||||
if v == 'prevent_debuff' then
|
||||
if self.debuff then
|
||||
self.debuff = false
|
||||
if self.area == G.jokers then self:add_to_deck(true) end
|
||||
end
|
||||
self.debuffed_by_blind = false
|
||||
return
|
||||
end
|
||||
should_debuff = should_debuff or v
|
||||
end
|
||||
|
||||
'''
|
|
@ -1,171 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
#
|
||||
# Use number_format for...
|
||||
#
|
||||
|
||||
# DynaText
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "engine/text.lua"
|
||||
pattern = 'tostring\((?<param>v\.ref_table and v\.ref_table\[v\.ref_value\] or v\.string)\)'
|
||||
position = "at"
|
||||
payload = "format_ui_value($param)"
|
||||
|
||||
# Cash Out
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''
|
||||
localize\('\$'\)\.\.config\.dollars'''
|
||||
position = "at"
|
||||
payload = "localize('$')..format_ui_value(config.dollars)"
|
||||
|
||||
# End of round money
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''
|
||||
localize\('\$'\)\.\.num_dollars\}'''
|
||||
position = "at"
|
||||
payload = "localize('$')..format_ui_value(num_dollars)}"
|
||||
|
||||
# Tooltip numbers
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = '(?<param>args\.vars\[tonumber\(subpart\[1\]\)\])'
|
||||
position = "at"
|
||||
payload = 'format_ui_value($param)'
|
||||
|
||||
# Poker Hand chips
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},"
|
||||
position = "at"
|
||||
payload = "{n=G.UIT.T, config={text = number_format(G.GAME.hands[handname].chips, 1000000), scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},"
|
||||
match_indent = true
|
||||
|
||||
# Poker Hand mult
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}"
|
||||
position = "at"
|
||||
payload = "{n=G.UIT.T, config={text = number_format(G.GAME.hands[handname].mult, 1000000), scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}"
|
||||
match_indent = true
|
||||
|
||||
# Continue Run - Money
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = 'tostring\(saved_game\.GAME\.dollars\)'
|
||||
position = "at"
|
||||
payload = "format_ui_value(saved_game.GAME.dollars)"
|
||||
|
||||
# Continue Run - Best Hand - bigger size
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = 'scale_number\(saved_game\.GAME\.round_scores\.hand\.amt\, 0\.8\*scale\)'
|
||||
position = "at"
|
||||
payload = "scale_number(saved_game.GAME.round_scores.hand.amt, 0.8*scale, 100000000000)"
|
||||
|
||||
|
||||
#
|
||||
# Custom sci notation switch point
|
||||
#
|
||||
|
||||
## number_format
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'function number_format(num)'
|
||||
position = "at"
|
||||
payload = '''
|
||||
function number_format(num, e_switch_point)
|
||||
if type(num) ~= 'number' then return num end
|
||||
|
||||
local sign = (num >= 0 and "") or "-"
|
||||
num = math.abs(num)'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'num >= G\.E_SWITCH_POINT'
|
||||
position = "at"
|
||||
payload = "num >= (e_switch_point or G.E_SWITCH_POINT)"
|
||||
|
||||
# 1. Fix floating point error (1.000e92 instead of 10.000e91)
|
||||
# 2. Lower precision with higher numbers
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = '''
|
||||
return string.format("%.3f",x/(10^fac))..'e'..fac'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if num == math.huge then
|
||||
return sign.."naneinf"
|
||||
end
|
||||
|
||||
local mantissa = round_number(x/(10^fac), 3)
|
||||
if mantissa >= 10 then
|
||||
mantissa = mantissa / 10
|
||||
fac = fac + 1
|
||||
end
|
||||
return sign..(string.format(fac >= 100 and "%.1fe%i" or fac >= 10 and "%.2fe%i" or "%.3fe%i", mantissa, fac))'''
|
||||
match_indent = true
|
||||
|
||||
# Remove trailing zeroes
|
||||
# E.g. X1.5 being displayed as X1.50
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = '''
|
||||
return string.format(num ~= math.floor(num) and (num >= 100 and "%.0f" or num >= 10 and "%.1f" or "%.2f") or "%.0f", num):reverse():gsub("(%d%d%d)", "%1,"):gsub(",$", ""):reverse()'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
local formatted
|
||||
if num ~= math.floor(num) and num < 100 then
|
||||
formatted = string.format(num >= 10 and "%.1f" or "%.2f", num)
|
||||
if formatted:sub(-1) == "0" then
|
||||
formatted = formatted:gsub("%.?0+$", "")
|
||||
end
|
||||
-- Return already to avoid comas being added
|
||||
if num < 0.01 then return tostring(num) end
|
||||
else
|
||||
formatted = string.format("%.0f", num)
|
||||
end
|
||||
return sign..(formatted:reverse():gsub("(%d%d%d)", "%1,"):gsub(",$", ""):reverse())'''
|
||||
match_indent = true
|
||||
|
||||
## scale_number
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = 'function scale_number(number, scale, max)'
|
||||
position = "at"
|
||||
payload = 'function scale_number(number, scale, max, e_switch_point)'
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = 'number >= G\.E_SWITCH_POINT'
|
||||
position = "at"
|
||||
payload = "math.abs(number) >= (e_switch_point or G.E_SWITCH_POINT)"
|
||||
|
|
@ -1,299 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Playing Card API
|
||||
|
||||
# Game:init_game_object()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'game.lua'
|
||||
pattern = 'function Game:init_game_object()'
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local cards_played = {}
|
||||
for _,v in ipairs(SMODS.Rank.obj_buffer) do
|
||||
cards_played[v] = { suits = {}, total = 0 }
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "game.lua"
|
||||
pattern = '(?<indent>[\t ]*)cards_played = \{\n(.*\n){13}[\t ]*\},'
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
cards_played = cards_played,
|
||||
disabled_suits = {},
|
||||
disabled_ranks = {},'''
|
||||
|
||||
# Game:start_run()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = 'local _ = nil'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if type(SMODS.Ranks[v.value].in_pool) == 'function' and not SMODS.Ranks[v.value]:in_pool({initial_deck = true, suit = v.suit})
|
||||
or type(SMODS.Suits[v.suit].in_pool) == 'function' and not SMODS.Suits[v.suit]:in_pool({initial_deck = true, rank = v.value}) then
|
||||
goto continue
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "if self.GAME.starting_params.erratic_suits_and_ranks then _, k = pseudorandom_element(G.P_CARDS, pseudoseed('erratic')) end"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''if self.GAME.starting_params.erratic_suits_and_ranks then
|
||||
v, k = pseudorandom_element(G.P_CARDS, pseudoseed('erratic'), {starting_deck = true})
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = 'local _r, _s = string.sub(k, 3, 3), string.sub(k, 1, 1)'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = 'local _r, _s = SMODS.Ranks[v.value].card_key, SMODS.Suits[v.suit].card_key'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "if self.GAME.starting_params.no_faces and (_r == 'K' or _r == 'Q' or _r == 'J') then keep = false end"
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if self.GAME.starting_params.no_faces and SMODS.Ranks[v.value].face then keep = false end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "if keep then card_protos[#card_protos+1] = {s=_s,r=_r,e=_e,d=_d,g=_g} end"
|
||||
position = "after"
|
||||
payload = "::continue::"
|
||||
match_indent = true
|
||||
|
||||
# loc_colour()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = 'return G.ARGS.LOC_COLOURS[_c] or _default or G.C.UI.TEXT_DARK'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for _, v in ipairs(SMODS.Rarity.obj_buffer) do
|
||||
G.ARGS.LOC_COLOURS[v:lower()] = G.C.RARITY[v]
|
||||
end
|
||||
for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do
|
||||
G.ARGS.LOC_COLOURS[v:lower()] = G.C.SECONDARY_SET[v]
|
||||
end
|
||||
for _, v in ipairs(SMODS.Suit.obj_buffer) do
|
||||
G.ARGS.LOC_COLOURS[v:lower()] = G.C.SUITS[v]
|
||||
end'''
|
||||
|
||||
# get_flush()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = '(?<indent>[\t ]*)local suits = \{\n[\t ]*"Spades",\n[\t ]*"Hearts",\n[\t ]*"Clubs",\n[\t ]*"Diamonds"\n[\t ]*\}\n[\t ]*if #hand > 5 or (?<restcond>#hand < \(5 - \(four_fingers and 1 or 0\)\) then return ret else)'
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local suits = SMODS.Suit.obj_buffer
|
||||
if $restcond'''
|
||||
|
||||
# get_X_same()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = 'local vals = {{},{},{},{},{},{},{},{},{},{},{},{},{},{}}'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local vals = {}
|
||||
for i = 1, SMODS.Rank.max_id.value do
|
||||
vals[i] = {}
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = 'function get_X_same(num, hand)'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''function get_X_same(num, hand, or_more)'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = 'if #curr == num then'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''if or_more and (#curr >= num) or (#curr == num) then'''
|
||||
|
||||
# Card:get_nominal()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = 'function Card:get_nominal\(mod\)\n([\t ]+.*\n)*end'
|
||||
position = 'at'
|
||||
payload = '''
|
||||
function Card:get_nominal(mod)
|
||||
local mult = 1
|
||||
local rank_mult = 1
|
||||
if mod == 'suit' then mult = 10000 end
|
||||
if self.ability.effect == 'Stone Card' or (self.config.center.no_suit and self.config.center.no_rank) then
|
||||
mult = -10000
|
||||
elseif self.config.center.no_suit then
|
||||
mult = 0
|
||||
elseif self.config.center.no_rank then
|
||||
rank_mult = 0
|
||||
end
|
||||
return 10*self.base.nominal*rank_mult + self.base.suit_nominal*mult + (self.base.suit_nominal_original or 0)*0.0001*mult + 10*self.base.face_nominal*rank_mult + 0.000001*self.unique_val
|
||||
end'''
|
||||
|
||||
# Card:set_base()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<indent>[\t ]*)if self.base.value == '2' then self.base.nominal = 2; self.base.id = 2(\n[\t ]+elseif .*)*"
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local rank = SMODS.Ranks[self.base.value] or {}
|
||||
self.base.nominal = rank.nominal or 0
|
||||
self.base.face_nominal = rank.face_nominal or 0
|
||||
self.base.id = rank.id'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<indent>[\t ]*)if self.base.suit == 'Diamonds' then self.base.suit_nominal = 0.01; self.base.suit_nominal_original = suit_base_nominal_original or 0.001 (\n[\t ]+elseif .*)*"
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local suit = SMODS.Suits[self.base.suit] or {}
|
||||
self.base.suit_nominal = suit.suit_nominal or 0
|
||||
self.base.suit_nominal_original = suit_base_nominal_original or suit.suit_nominal or 0'''
|
||||
|
||||
# Card:change_suit()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<indent>[\t ]*)local new_code = [\\s\\S]*?local new_val = [\\s\\S]*?local new_card = G.P_CARDS\\[new_code..new_val\\]"
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local new_code = SMODS.Suits[new_suit].card_key
|
||||
local new_val = SMODS.Ranks[self.base.value].card_key
|
||||
local new_card = G.P_CARDS[new_code..'_'..new_val]'''
|
||||
|
||||
# Card:is_face()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = "(?<indent>[\t ]*)if id == 11 or id == 12 or id == 13 or next\\(find_joker\\(\"Pareidolia\"\\)\\) then"
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local rank = SMODS.Ranks[self.base.value]
|
||||
if not id then return end
|
||||
if (id > 0 and rank and rank.face) or next(find_joker("Pareidolia")) then'''
|
||||
|
||||
|
||||
# tally_sprite()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '(?<start>[\t ]*local t_s = Sprite\(0,0,0.5,0.5,)G.ASSET_ATLAS\[.*?\](?<rest>.*?\))'
|
||||
position = 'at'
|
||||
payload = '$start G.ASSET_ATLAS[suit and SMODS.Suits[suit][G.SETTINGS.colourblind_option and "hc_ui_atlas" or "lc_ui_atlas"]] or G.ASSET_ATLAS[("ui_"..(G.SETTINGS.colourblind_option and "2" or "1"))]$rest'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'function tally_sprite(pos, value, tooltip)'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = 'function tally_sprite(pos, value, tooltip, suit)'
|
||||
|
||||
# G.UIDEF.challenge_description_tab()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = "(?<indent>[\t ]*)local SUITS = \\{(\n.*){5}\n[\t ]*local suit_map = \\{'S', 'H', 'C', 'D'\\}"
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local SUITS = {}
|
||||
local suit_map = {}
|
||||
for i = #SMODS.Suit.obj_buffer, 1, -1 do
|
||||
local suit = SMODS.Suits[SMODS.Suit.obj_buffer[i]]
|
||||
SUITS[suit.card_key] = {}
|
||||
suit_map[#suit_map+1] = suit.card_key
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = 'local _r, _s = string.sub(k, 3, 3), string.sub(k, 1, 1)'
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = 'local _r, _s = SMODS.Ranks[v.value].card_key, SMODS.Suits[v.suit].card_key'
|
||||
|
||||
# TODO there may need to be a way to let in_pool know what challenge is being displayed
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = 'local keep, _e, _d, _g = true, nil, nil, nil'
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if type(SMODS.Ranks[v.value].in_pool) == 'function' and not SMODS.Ranks[v.value]:in_pool({initial_deck = true, suit = v.suit}) then
|
||||
keep = false
|
||||
end
|
||||
if type(SMODS.Suits[v.suit].in_pool) == 'function' and not SMODS.Suits[v.suit]:in_pool({initial_deck = true, rank = v.value}) then
|
||||
keep = false
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '(?<indent>[\t ]*)for j = 1, 4 do\n[\t ]*(?<mid>if SUITS\[suit_map\[j\]\]\[1\] then\n[\t ]*table.sort.*(\n.*)*?)\n[\t ]*0\.42\*G.CARD_H,'
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local num_suits = 0
|
||||
for j = 1, #suit_map do
|
||||
if SUITS[suit_map[j]][1] then num_suits = num_suits + 1 end
|
||||
end
|
||||
for j = 1, #suit_map do
|
||||
$mid
|
||||
(0.42 - (num_suits <= 4 and 0 or num_suits >= 8 and 0.28 or 0.07 * (num_suits - 4))) * G.CARD_H,'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = '--Fill all remaining info if this is the main desc'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''if card_type == 'Default' or card_type == 'Enhanced' and not _c.replace_base_card and card and card.base then
|
||||
if not _c.no_suit then
|
||||
local suit = SMODS.Suits[card.base.suit] or {}
|
||||
if suit.loc_vars and type(suit.loc_vars) == 'function' then
|
||||
suit:loc_vars(info_queue, card)
|
||||
end
|
||||
end
|
||||
if not _c.no_rank then
|
||||
local rank = SMODS.Ranks[card.base.value] or {}
|
||||
if rank.loc_vars and type(rank.loc_vars) == 'function' then
|
||||
rank:loc_vars(info_queue, card)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
'''
|
|
@ -1,60 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
### Poker Hand API
|
||||
|
||||
# evaluate_poker_hand()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "local parts = {"
|
||||
position = 'before'
|
||||
payload = '''
|
||||
for _,v in ipairs(SMODS.PokerHand.obj_buffer) do
|
||||
results[v] = {}
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "if next(parts._5) and next(parts._flush) then"
|
||||
position = 'before'
|
||||
payload = '''
|
||||
for _,_hand in pairs(SMODS.PokerHands) do
|
||||
if _hand.atomic_part and type(_hand.atomic_part) == 'function' then
|
||||
parts[_hand.key] = _hand.atomic_part(hand)
|
||||
end
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "return results"
|
||||
position = 'before'
|
||||
payload = '''
|
||||
for _,_hand in pairs(SMODS.PokerHands) do
|
||||
if _hand.composite and type(_hand.composite) == 'function' then
|
||||
local other_hands
|
||||
results[_hand.key], other_hands = _hand.composite(parts)
|
||||
results[_hand.key] = results[_hand.key] or {}
|
||||
if other_hands and type(other_hands) == 'table' then
|
||||
for k, v in pairs(other_hands) do
|
||||
results[k] = v
|
||||
end
|
||||
end
|
||||
else
|
||||
results[_hand.key] = parts[_hand.key]
|
||||
end
|
||||
end
|
||||
results.top = nil
|
||||
for _, v in ipairs(G.handlist) do
|
||||
if not results.top and results[v] then
|
||||
results.top = results[v]
|
||||
break
|
||||
end
|
||||
end'''
|
||||
match_indent = true
|
|
@ -1,185 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Functions that affect random selection from pools
|
||||
|
||||
# pseudorandom_element()
|
||||
# TODO special cases for now
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "function pseudorandom_element(_t, seed)"
|
||||
position = "at"
|
||||
payload = """function pseudorandom_element(_t, seed, args)
|
||||
-- TODO special cases for now
|
||||
-- Preserves reverse nominal order for Suits, nominal+face_nominal order for Ranks
|
||||
-- for vanilla RNG
|
||||
if _t == SMODS.Suits then
|
||||
_t = SMODS.Suit:obj_list(true)
|
||||
end
|
||||
if _t == SMODS.Ranks then
|
||||
_t = SMODS.Rank:obj_list()
|
||||
end
|
||||
"""
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "keys[#keys+1] = {k = k,v = v}"
|
||||
position = "at"
|
||||
payload = """
|
||||
local keep = true
|
||||
local in_pool_func =
|
||||
args and args.in_pool
|
||||
or type(v) == 'table' and type(v.in_pool) == 'function' and v.in_pool
|
||||
or _t == G.P_CARDS and function(c)
|
||||
--Handles special case for Erratic Deck
|
||||
local initial_deck = args and args.starting_deck or false
|
||||
|
||||
return not (
|
||||
type(SMODS.Ranks[c.value].in_pool) == 'function' and not SMODS.Ranks[c.value]:in_pool({initial_deck = initial_deck, suit = c.suit})
|
||||
or type(SMODS.Suits[c.suit].in_pool) == 'function' and not SMODS.Suits[c.suit]:in_pool({initial_deck = initial_deck, rank = c.value})
|
||||
)
|
||||
end
|
||||
if in_pool_func then
|
||||
keep = in_pool_func(v, args)
|
||||
end
|
||||
if keep then
|
||||
keys[#keys+1] = {k = k,v = v}
|
||||
end"""
|
||||
match_indent = true
|
||||
|
||||
# fixes pseudorandom_element on an empty list
|
||||
# nil, nil is returned in that case
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "local key = keys[math.random(#keys)].k"
|
||||
position = "before"
|
||||
payload = "if #keys == 0 then return nil, nil end"
|
||||
match_indent = true
|
||||
|
||||
## get_current_pool()
|
||||
|
||||
# Centers
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "else _starting_pool, _pool_key = G.P_CENTER_POOLS[_type], _type..(_append or '')"
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
payload = '''
|
||||
elseif SMODS.ObjectTypes[_type] and SMODS.ObjectTypes[_type].rarities then
|
||||
local rarities = SMODS.ObjectTypes[_type].rarities
|
||||
local rarity
|
||||
if _legendary and rarities.legendary then
|
||||
rarity = rarities.legendary.key
|
||||
else
|
||||
rarity = _rarity or SMODS.poll_rarity(_type, 'rarity_'.._type..G.GAME.round_resets.ante..(_append or ''))
|
||||
end
|
||||
_starting_pool, _pool_key = SMODS.ObjectTypes[_type].rarity_pools[rarity], _type..rarity..(_append or '')'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if _type == 'Tarot' or _type == 'Tarot_Planet' then _pool[#_pool + 1] = \"c_strength\""
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
if SMODS.ObjectTypes[_type] and SMODS.ObjectTypes[_type].default and G.P_CENTERS[SMODS.ObjectTypes[_type].default] then
|
||||
_pool[#_pool+1] = SMODS.ObjectTypes[_type].default
|
||||
elseif _type == 'Tarot' or _type == 'Tarot_Planet' then _pool[#_pool + 1] = "c_strength"'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if v.name == 'Black Hole' or v.name == 'The Soul' then"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = "if v.name == 'Black Hole' or v.name == 'The Soul' or v.hidden then"
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if _type == 'Enhanced' then"
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local in_pool, pool_opts
|
||||
if v.in_pool and type(v.in_pool) == 'function' then
|
||||
in_pool, pool_opts = v:in_pool({ source = _append })
|
||||
end
|
||||
pool_opts = pool_opts or {}
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = 'elseif not (G.GAME.used_jokers[v.key] and not next(find_joker("Showman"))) and'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''elseif not (G.GAME.used_jokers[v.key] and not pool_opts.allow_duplicates and not next(find_joker("Showman"))) and'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if add and not G.GAME.banned_keys[v.key] then"
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
payload = '''
|
||||
if v.in_pool and type(v.in_pool) == 'function' then
|
||||
add = in_pool and (add or pool_opts.override_base_checks)
|
||||
end
|
||||
'''
|
||||
|
||||
## G.GAME.used_jokers now checks keys, not names
|
||||
# Card:set_ability()
|
||||
# Remove the old center from `used_jokers` if set_ability overrides
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "local old_center = self.config.center"
|
||||
position = 'after'
|
||||
payload = '''
|
||||
if old_center and not next(SMODS.find_card(old_center.key, true)) then
|
||||
G.GAME.used_jokers[old_center.key] = nil
|
||||
end'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)for k, v in pairs\(G\.P_CENTERS\) do
|
||||
[\t ]*if v\.name == self\.ability\.name then
|
||||
[\t ]*G\.GAME\.used_jokers\[k\] = true
|
||||
[\t ]*end
|
||||
[\t ]*end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if self.config.center.key then
|
||||
G.GAME.used_jokers[self.config.center.key] = true
|
||||
end
|
||||
'''
|
||||
line_prepend = "$indent"
|
||||
# Card:remove()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)for k, v in pairs\(G\.P_CENTERS\) do
|
||||
[\t ]*if v\.name == self\.ability\.name then
|
||||
[\t ]*if not next\(find_joker\(self\.ability\.name, true\)\) then
|
||||
[\t ]*G\.GAME\.used_jokers\[k\] = nil
|
||||
[\t ]*end
|
||||
[\t ]*end
|
||||
[\t ]*end'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
if not next(SMODS.find_card(self.config.center.key, true)) then
|
||||
G.GAME.used_jokers[self.config.center.key] = nil
|
||||
end'''
|
||||
line_prepend = "$indent"
|
|
@ -1,61 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Rarity API
|
||||
|
||||
# get_badge_colour
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for k, v in pairs(SMODS.Rarity.obj_buffer) do
|
||||
G.BADGE_COL[k] = G.C.RARITY[v]
|
||||
end'''
|
||||
|
||||
# G.UIDEF.card_h_popup
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "if AUT.card_type == 'Joker' or (AUT.badges and AUT.badges.force_rarity) then card_type = ({localize('k_common'), localize('k_uncommon'), localize('k_rare'), localize('k_legendary')})[card.config.center.rarity] end"
|
||||
position = "at"
|
||||
payload = "if AUT.card_type == 'Joker' or (AUT.badges and AUT.badges.force_rarity) then card_type = SMODS.Rarity:get_rarity_badge(card.config.center.rarity) end"
|
||||
match_indent = true
|
||||
|
||||
# Game:update
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "self.C.EDITION[2] = 0.7+0.2*(1+math.sin(self.TIMERS.REAL*1.5 + 6))"
|
||||
position = "after"
|
||||
payload = '''
|
||||
for k, v in pairs(SMODS.Rarities) do
|
||||
if v.gradient and type(v.gradient) == "function" then v:gradient(dt) end
|
||||
end'''
|
||||
match_indent = true
|
||||
|
||||
# get_current_pool
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = '''(?<indent>[\t ]*)local rarity = _rarity or pseudorandom\('rarity'\.\.G\.GAME\.round_resets\.ante\.\.\(_append or ''\)\) \n[\s\S]{12}rarity = \(_legendary and 4\) or \(rarity > 0\.95 and 3\) or \(rarity > 0\.7 and 2\) or 1'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
_rarity = (_legendary and 4) or (type(_rarity) == "number" and ((_rarity > 0.95 and 3) or (_rarity > 0.7 and 2) or 1)) or _rarity
|
||||
_rarity = ({Common = 1, Uncommon = 2, Rare = 3, Legendary = 4})[_rarity] or _rarity
|
||||
local rarity = _rarity or SMODS.poll_rarity("Joker", 'rarity'..G.GAME.round_resets.ante..(_append or ''))
|
||||
'''
|
||||
|
||||
## Ensure that other cards set to string rarity work the same as set for int rarity
|
||||
# Card:calculate_joker
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if self.ability.name == 'Baseball Card' and context.other_joker.config.center.rarity == 2 and self ~= context.other_joker then"
|
||||
position = "at"
|
||||
payload = '''if self.ability.name == 'Baseball Card' and (context.other_joker.config.center.rarity == 2 or context.other_joker.config.center.rarity == "Uncommon") and self ~= context.other_joker then'''
|
||||
match_indent = true
|
|
@ -1,222 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Seal API
|
||||
# Card:open()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)local seal_rate = 10
|
||||
[\n\t ]*local seal_poll = pseudorandom\(pseudoseed\('stdseal'..G.GAME.round_resets.ante\)\)
|
||||
[\n\t ]*if seal_poll > 1 - 0.02\*seal_rate then
|
||||
[\n\t ]*local seal_type = pseudorandom\(pseudoseed\('stdsealtype'..G.GAME.round_resets.ante\)\)
|
||||
[\n\t ]*if seal_type > 0.75 then card:set_seal\('Red'\)
|
||||
[\n\t ]*elseif seal_type > 0.5 then card:set_seal\('Blue'\)
|
||||
[\n\t ]*elseif seal_type > 0.25 then card:set_seal\('Gold'\)
|
||||
[\n\t ]*else card:set_seal\('Purple'\)
|
||||
[\n\t ]*end
|
||||
[\n\t ]*end'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
card:set_seal(SMODS.poll_seal({mod = 10}))'''
|
||||
|
||||
# Card:calculate_joker()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'card.lua'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)local seal_type = pseudorandom\(pseudoseed\('certsl'\)\)
|
||||
[\n\t ]*if seal_type > 0.75 then _card:set_seal\('Red', true\)
|
||||
[\n\t ]*elseif seal_type > 0.5 then _card:set_seal\('Blue', true\)
|
||||
[\n\t ]*elseif seal_type > 0.25 then _card:set_seal\('Gold', true\)
|
||||
[\n\t ]*else _card:set_seal\('Purple', true\)
|
||||
[\n\t ]*end'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''_card:set_seal(SMODS.poll_seal({guaranteed = true, type_key = 'certsl'}))'''
|
||||
|
||||
# get_badge_colour()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for k, v in pairs(SMODS.Seals) do
|
||||
G.BADGE_COL[k:lower()..'_seal'] = v.badge_colour
|
||||
end'''
|
||||
|
||||
# Card:calculate_seal()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = 'function Card:calculate_seal\(context\)\n(?<indent>[\t ]*)if self.debuff then return nil end'
|
||||
position = 'after'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local obj = G.P_SEALS[self.seal] or {}
|
||||
if obj.calculate and type(obj.calculate) == 'function' then
|
||||
local o = obj:calculate(self, context)
|
||||
if o then
|
||||
if not o.card then o.card = self end
|
||||
return o
|
||||
end
|
||||
end'''
|
||||
|
||||
# Card:update()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = 'if G.STAGE == G.STAGES.RUN then'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = G.P_SEALS[self.seal] or {}
|
||||
if obj.update and type(obj.update) == 'function' then
|
||||
obj:update(self, dt)
|
||||
end'''
|
||||
|
||||
# Card:get_p_dollars()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "card.lua"
|
||||
pattern = '''(?<indent>[\t ]*)if (?<cond>self\.seal == 'Gold' then\n)'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local obj = G.P_SEALS[self.seal] or {}
|
||||
if obj.get_p_dollars and type(obj.get_p_dollars) == 'function' then
|
||||
ret = ret + obj:get_p_dollars(self)
|
||||
elseif $cond'''
|
||||
|
||||
# generate_card_ui()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if v == 'gold_seal'*"
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
payload = '''
|
||||
local seal = G.P_SEALS[v] or G.P_SEALS[SMODS.Seal.badge_to_key[v] or '']
|
||||
if seal then
|
||||
info_queue[#info_queue+1] = seal
|
||||
else'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = "if v == 'purple_seal'*"
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
payload = 'end'
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/common_events.lua'
|
||||
position = 'at'
|
||||
pattern = '''\{key = (?<badge>'.*?_seal'), set = 'Other'\}'''
|
||||
payload = '''G.P_SEALS[$badge] or G.P_SEALS[SMODS.Seal.badge_to_key[$badge] or '']'''
|
||||
|
||||
# Card:update_alert()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "function Card:update_alert()"
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
payload = '''
|
||||
if self.ability.set == 'Default' and self.config.center and self.config.center.key == 'c_base' and self.seal then
|
||||
if G.P_SEALS[self.seal].alerted and self.children.alert then
|
||||
self.children.alert:remove()
|
||||
self.children.alert = nil
|
||||
elseif not G.P_SEALS[self.seal].alerted and not self.children.alert and G.P_SEALS[self.seal].discovered then
|
||||
self.children.alert = UIBox{
|
||||
definition = create_UIBox_card_alert(),
|
||||
config = {align="tli",
|
||||
offset = {x = 0.1, y = 0.1},
|
||||
parent = self}
|
||||
}
|
||||
end
|
||||
end'''
|
||||
|
||||
# Card:hover()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = "G:save_progress()"
|
||||
match_indent = false
|
||||
position = "after"
|
||||
payload = '''
|
||||
elseif self.children.alert and self.seal and not G.P_SEALS[self.seal].alerted then
|
||||
G.P_SEALS[self.seal].alerted = true
|
||||
G:save_progress()'''
|
||||
|
||||
# Game:init_item_prototypes()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'game.lua'
|
||||
pattern = '''(?<indent>[\t ]*)Gold =[ {A-z=1-4,"}\n]*},[\n\t ]*}'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
Red = {order = 1, discovered = false, set = "Seal"},
|
||||
Blue = {order = 2, discovered = false, set = "Seal"},
|
||||
Gold = {order = 3, discovered = false, set = "Seal"},
|
||||
Purple = {order = 4, discovered = false, set = "Seal"},
|
||||
}
|
||||
'''
|
||||
|
||||
# Card:set_seal()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = '''G.CONTROLLER.locks.seal = true'''
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''local sound = G.P_SEALS[_seal].sound or {sound = 'gold_seal', per = 1.2, vol = 0.4}'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = '''play_sound('gold_seal', 1.2, 0.4)'''
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''play_sound(sound.sound, sound.per, sound.vol)'''
|
||||
## Populate Seal Ability Table
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'card.lua'
|
||||
pattern = '''self.seal = _seal'''
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
self.ability.seal = {}
|
||||
for k, v in pairs(G.P_SEALS[_seal].config or {}) do
|
||||
if type(v) == 'table' then
|
||||
self.ability.seal[k] = copy_table(v)
|
||||
else
|
||||
self.ability.seal[k] = v
|
||||
end
|
||||
end
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
pattern = '''new_card:set_seal(other.seal, true)'''
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if other.seal then
|
||||
for k, v in pairs(other.ability.seal or {}) do
|
||||
if type(v) == 'table' then
|
||||
new_card.ability.seal[k] = copy_table(v)
|
||||
else
|
||||
new_card.ability.seal[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
|
@ -1,167 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
#modulate_sound()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = 'G.SOUND_MANAGER.channel:push(G.ARGS.push)'
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
payload = '''
|
||||
SMODS.previous_track = SMODS.previous_track or ''
|
||||
local in_sync = (SMODS.Sounds[desired_track] or {}).sync
|
||||
local out_sync = (SMODS.Sounds[SMODS.previous_track] or {}).sync
|
||||
local should_sync = true
|
||||
if (type(in_sync) == 'table' and not in_sync[SMODS.previous_track]) or in_sync == false then should_sync = false end
|
||||
if (type(out_sync) == 'table' and not out_sync[desired_track]) or out_sync == false then should_sync = false end
|
||||
if
|
||||
SMODS.previous_track and SMODS.previous_track ~= desired_track and
|
||||
not should_sync
|
||||
then
|
||||
G.ARGS.push.type = 'restart_music'
|
||||
G.SOUND_MANAGER.channel:push(G.ARGS.push)
|
||||
end
|
||||
SMODS.previous_track = desired_track'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = 'G.ARGS.push.ambient_control = G.SETTINGS.ambient_control'
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
payload = '''
|
||||
if SMODS.remove_replace_sound and SMODS.remove_replace_sound ~= desired_track then
|
||||
SMODS.Sound.replace_sounds[SMODS.remove_replace_sound] = nil
|
||||
SMODS.remove_replace_sound = nil
|
||||
end
|
||||
local replace_sound = SMODS.Sound.replace_sounds[desired_track]
|
||||
if replace_sound then
|
||||
local replaced_track = desired_track
|
||||
desired_track = replace_sound.key
|
||||
G.ARGS.push.desired_track = desired_track
|
||||
if SMODS.previous_track ~= desired_track then
|
||||
if replace_sound.times > 0 then replace_sound.times = replace_sound.times - 1 end
|
||||
if replace_sound.times == 0 then SMODS.remove_replace_sound = replaced_track end
|
||||
end
|
||||
end
|
||||
local stop_sound = SMODS.Sound.stop_sounds[desired_track]
|
||||
if SMODS.Sound.stop_sounds[desired_track] then
|
||||
if SMODS.previous_track ~= '' and stop_sound > 0 then stop_sound = stop_sound - 1 end
|
||||
SMODS.Sound.stop_sounds[desired_track] = stop_sound ~= 0 and stop_sound or nil
|
||||
SMODS.previous_track = ''
|
||||
return
|
||||
end
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
pattern = "(G.STATE == G.STATES.SPLASH and '') or"
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
payload = 'SMODS.Sound:get_current_music() or'
|
||||
|
||||
# PLAY_SOUND
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = '''local s = {sound = love.audio.newSource("resources/sounds/"..args.sound_code..'.ogg', should_stream and "stream" or 'static')}'''
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = '''
|
||||
local c = SMODS_Sounds[args.sound_code]
|
||||
local s = c and
|
||||
{sound = love.audio.newSource(love.sound.newDecoder(c.data), c.should_stream and 'stream' or 'static'), per = c.per, vol = c.vol } or
|
||||
{sound = love.audio.newSource("resources/sounds/"..args.sound_code..'.ogg', should_stream and "stream" or 'static')}'''
|
||||
|
||||
# pass in custom sounds
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = "DISABLE_SFX = false"
|
||||
match_indent = true
|
||||
position = 'after'
|
||||
payload = '''
|
||||
SMODS_Sounds = {}
|
||||
'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = "elseif request.type == 'stop' then"
|
||||
match_indent = true
|
||||
position = 'before'
|
||||
payload = '''
|
||||
elseif request.type == 'sound_source' then
|
||||
SMODS_Sounds[request.sound_code] = {
|
||||
sound_code = request.sound_code,
|
||||
data = request.data,
|
||||
sound = sound,
|
||||
per = request.per,
|
||||
vol = request.vol,
|
||||
}
|
||||
SOURCES[request.sound_code] = {}
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = "s.original_pitch = args.per or 1"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = 's.original_pitch = ((args.type ~= "sound") and s.per) or args.per or 1'
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = "s.original_volume = args.vol or 1"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = 's.original_volume = ((args.type ~= "sound") and s.vol) or args.vol or 1'
|
||||
|
||||
# don't crash RESTART_MUSIC
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = "RESTART_MUSIC()"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = 'RESTART_MUSIC(request)'
|
||||
|
||||
# fix looping for music of different length
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = """(?<indent>[\t ]*)function MODULATE\\(args\\)(\n.*){9}"""
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
payload = """function MODULATE(args)
|
||||
if args.desired_track ~= '' then
|
||||
local sound = ((SOURCES[current_track or {}] or {})[1] or {}).sound
|
||||
if not sound or not sound:isPlaying() then
|
||||
RESTART_MUSIC(args)
|
||||
end
|
||||
end
|
||||
"""
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/sound_manager.lua'
|
||||
pattern = "for _, s in pairs(v) do"
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
payload = """current_track = args.desired_track
|
||||
for _, s in pairs(v) do"""
|
||||
|
||||
# [[patches]]
|
||||
# [patches.pattern]
|
||||
# target = 'engine/sound_manager.lua'
|
||||
# pattern = 'if s.sound and not s.sound:isPlaying() then'
|
||||
# match_indent = true
|
||||
# position = 'at'
|
||||
# payload = '''if s.sound and s.sound:isPlaying() then
|
||||
# s.sound:stop()
|
||||
# elseif s.sound and not s.sound:isPlaying() then'''
|
|
@ -1,190 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
# Fix areas where highest stake is hardcoded as Gold Stake
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "if G.PROFILES[G.SETTINGS.profile].all_unlocked then max_stake = 8 end"
|
||||
position = "at"
|
||||
payload = "if G.PROFILES[G.SETTINGS.profile].all_unlocked then max_stake = #G.P_CENTER_POOLS['Stake'] end"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "for i = 1, math.min(max_stake+1, 8) do"
|
||||
position = "at"
|
||||
payload = "for i = 1, math.min(max_stake+1, #G.P_CENTER_POOLS['Stake']) do"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "if G.GAME.stake >= 8 then"
|
||||
position = "at"
|
||||
payload = "if G.GAME.stake >= #G.P_CENTER_POOLS['Stake'] then"
|
||||
match_indent = true
|
||||
|
||||
# Stake modifier API
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "if self.GAME.stake >= 2 then"
|
||||
position = "before"
|
||||
payload = "if false then"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "game.lua"
|
||||
pattern = "if self.GAME.stake >= 8 then self.GAME.modifiers.enable_rentals_in_shop = true end"
|
||||
position = "after"
|
||||
payload = "end SMODS.setup_stake(self.GAME.stake)"
|
||||
match_indent = true
|
||||
|
||||
# Stake shininess API
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = "if _stake == 8 then"
|
||||
position = "at"
|
||||
payload = "if G.P_CENTER_POOLS['Stake'][_stake].shiny then"
|
||||
match_indent = true
|
||||
|
||||
# Override stake listing to make room for our recursive version
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "for i = G.GAME.stake-1, 2, -1 do"
|
||||
position = "before"
|
||||
payload = "if false then"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = 'other_col = {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = G.C.L_BLACK}, nodes=stake_desc_rows}'
|
||||
position = "before"
|
||||
payload = "end SMODS.applied_stakes_UI(G.GAME.stake, stake_desc_rows)"
|
||||
match_indent = true
|
||||
|
||||
# Set win stake to that specified in unlocked stake
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'for i = 1, G.GAME.stake do'
|
||||
position = "at"
|
||||
payload = '''for i = 1,
|
||||
(G.P_CENTER_POOLS["Stake"][G.GAME.stake].unlocked_stake) and
|
||||
(G.P_STAKES[G.P_CENTER_POOLS["Stake"][G.GAME.stake].unlocked_stake].stake_level-1) or (G.GAME.stake-1)
|
||||
do'''
|
||||
match_indent = true
|
||||
|
||||
# Stake Sprites
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'local stake_sprite = Sprite(0,0,_scale*1,_scale*1,G.ASSET_ATLAS["chips"], G.P_CENTER_POOLS.Stake[_stake].pos)'
|
||||
position = "at"
|
||||
payload = 'local stake_sprite = Sprite(0,0,_scale*1,_scale*1,G.ASSET_ATLAS[G.P_CENTER_POOLS.Stake[_stake].atlas], G.P_CENTER_POOLS.Stake[_stake].pos)'
|
||||
match_indent = true
|
||||
|
||||
# Achievements and unlocks
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = 'if highest_win >= 2 then'
|
||||
position = "at"
|
||||
payload = 'if highest_win >= G.P_STAKES["stake_red"].stake_level then'
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = 'if highest_win >= 4 then'
|
||||
position = "at"
|
||||
payload = 'if highest_win >= G.P_STAKES["stake_black"].stake_level then'
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = 'if highest_win >= 8 then'
|
||||
position = "at"
|
||||
payload = 'if highest_win >= G.P_STAKES["stake_gold"].stake_level then'
|
||||
match_indent = true
|
||||
|
||||
# get_blind_amount
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'function get_blind_amount(ante)'
|
||||
position = "after"
|
||||
payload = '''if G.GAME.modifiers.scaling and G.GAME.modifiers.scaling > 3 then return SMODS.get_blind_amount(ante) end'''
|
||||
match_indent = true
|
||||
|
||||
# set_joker_usage
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = {count = 1, order = v.config.center.order, wins = {}, losses = {}}'
|
||||
position = "at"
|
||||
payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}'
|
||||
match_indent = true
|
||||
|
||||
# set_joker_win
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or {count = 1, order = v.config.center.order, wins = {}, losses = {}}'
|
||||
position = "at"
|
||||
payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}'
|
||||
match_indent = true
|
||||
|
||||
#set_joker_win
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] or 0) + 1'
|
||||
position = "after"
|
||||
payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1'
|
||||
match_indent = true
|
||||
|
||||
#set_joker_loss
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] or 0) + 1'
|
||||
position = "after"
|
||||
payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1'
|
||||
match_indent = true
|
||||
|
||||
# set_deck_usage
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}}'
|
||||
position = "at"
|
||||
payload = 'G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}'
|
||||
match_indent = true
|
||||
|
||||
# set_deck_loss
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/misc_functions.lua"
|
||||
pattern = 'if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}} end'
|
||||
position = "at"
|
||||
payload = 'if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} end'
|
||||
match_indent = true
|
||||
|
||||
# G.UIDEF.viewed_stake_option
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = 'G.viewed_stake = math.min(max_stake+1, G.viewed_stake)'
|
||||
position = "after"
|
||||
payload = '''if G.viewed_stake > #G.P_CENTER_POOLS.Stake then G.viewed_stake = #G.P_CENTER_POOLS.Stake end'''
|
||||
match_indent = true
|
|
@ -1,132 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Sticker API
|
||||
|
||||
# generate_UIBox_ability_table()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = "if self.sticker or ((self.sticker_run and self.sticker_run~='NONE') and G.SETTINGS.run_stake_stickers) then loc_vars = loc_vars or {}; loc_vars.sticker=(self.sticker or self.sticker_run) end"
|
||||
position = "before"
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for k, v in ipairs(SMODS.Sticker.obj_buffer) do
|
||||
if self.ability[v] and not SMODS.Stickers[v].hide_badge then
|
||||
badges[#badges+1] = v
|
||||
end
|
||||
end'''
|
||||
|
||||
# generate_card_ui()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if v == 'eternal' then*"
|
||||
match_indent = true
|
||||
position = "before"
|
||||
payload = '''
|
||||
local sticker = SMODS.Stickers[v]
|
||||
if sticker then
|
||||
local t = { key = v, set = 'Other' }
|
||||
local res = {}
|
||||
if sticker.loc_vars and type(sticker.loc_vars) == 'function' then
|
||||
res = sticker:loc_vars(info_queue, card) or {}
|
||||
t.vars = res.vars or {}
|
||||
t.key = res.key or t.key
|
||||
t.set = res.set or t.set
|
||||
end
|
||||
info_queue[#info_queue+1] = t
|
||||
else'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if v == 'rental' then*"
|
||||
match_indent = true
|
||||
position = "after"
|
||||
payload = '''end'''
|
||||
|
||||
# create_card()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if card.ability.consumeable and not skip_materialize then card:start_materialize() end"
|
||||
position = "after"
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for k, v in ipairs(SMODS.Sticker.obj_buffer) do
|
||||
local sticker = SMODS.Stickers[v]
|
||||
if sticker.should_apply and type(sticker.should_apply) == 'function' and sticker:should_apply(card, center, area) then
|
||||
sticker:apply(card, true)
|
||||
end
|
||||
end'''
|
||||
|
||||
## Remove base game sticker rolls if one is added
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.7 then"
|
||||
position = "at"
|
||||
match_indent = true
|
||||
payload = '''if G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.7 and not SMODS.Stickers["eternal"].should_apply then'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "elseif G.GAME.modifiers.enable_perishables_in_shop and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7)) then"
|
||||
position = "at"
|
||||
match_indent = true
|
||||
payload = '''elseif G.GAME.modifiers.enable_perishables_in_shop and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7)) and not SMODS.Stickers["perishable"].should_apply then'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "if G.GAME.modifiers.enable_rentals_in_shop and pseudorandom((area == G.pack_cards and 'packssjr' or 'ssjr')..G.GAME.round_resets.ante) > 0.7 then"
|
||||
position = "at"
|
||||
match_indent = true
|
||||
payload = '''if G.GAME.modifiers.enable_rentals_in_shop and pseudorandom((area == G.pack_cards and 'packssjr' or 'ssjr')..G.GAME.round_resets.ante) > 0.7 and not SMODS.Stickers["rental"].should_apply then'''
|
||||
|
||||
# Card:draw()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "card.lua"
|
||||
pattern = '''if self.ability.name == 'The Soul' and (self.config.center.discovered or self.bypass_discovery_center) then'''
|
||||
match_indent = true
|
||||
position = "before"
|
||||
payload = '''
|
||||
for k, v in pairs(SMODS.Stickers) do
|
||||
if self.ability[v.key] then
|
||||
if v and v.draw and type(v.draw) == 'function' then
|
||||
v:draw(self, layer)
|
||||
else
|
||||
G.shared_stickers[v.key].role.draw_major = self
|
||||
G.shared_stickers[v.key]:draw_shader('dissolve', nil, nil, nil, self.children.center)
|
||||
G.shared_stickers[v.key]:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center)
|
||||
end
|
||||
end
|
||||
end
|
||||
'''
|
||||
|
||||
# get_badge_colour()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}'
|
||||
position = 'before'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
for k, v in pairs(SMODS.Stickers) do
|
||||
G.BADGE_COL[k] = v.badge_colour
|
||||
end'''
|
||||
|
||||
## Remove Pinned effect when in Sticker collections
|
||||
# CardArea:aling_cards
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'cardarea.lua'
|
||||
pattern = '''table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end)'''
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*((a.pinned and not a.ignore_pinned) and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*((b.pinned and not b.ignore_pinned) and b.sort_id or 0) end)'''
|
|
@ -1,139 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Tag API
|
||||
# Tag:apply_to_run()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "tag.lua"
|
||||
pattern = "function Tag:apply_to_run(_context)"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
if self.triggered then return end
|
||||
local obj = SMODS.Tags[self.key]
|
||||
local res
|
||||
if obj and obj.apply and type(obj.apply) == 'function' then
|
||||
res = obj:apply(self, _context)
|
||||
end
|
||||
if res then return res end
|
||||
'''
|
||||
|
||||
# Tag:set_ability()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "tag.lua"
|
||||
pattern = "function Tag:set_ability()"
|
||||
position = 'after'
|
||||
match_indent = true
|
||||
payload = '''
|
||||
local obj = SMODS.Tags[self.key]
|
||||
local res
|
||||
if obj and obj.set_ability and type(obj.set_ability) == 'function' then
|
||||
obj:set_ability(self)
|
||||
end
|
||||
'''
|
||||
|
||||
# create_UIBox_your_collection_tags()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = "(?<indent>[\t ]*)local tag_matrix = \\{(\n.*){6}"
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local tag_matrix = {}
|
||||
local counter = 0
|
||||
local tag_tab = {}
|
||||
local tag_pool = {}
|
||||
if G.ACTIVE_MOD_UI then
|
||||
for k, v in pairs(G.P_TAGS) do
|
||||
if v.mod and G.ACTIVE_MOD_UI.id == v.mod.id then tag_pool[k] = v end
|
||||
end
|
||||
else
|
||||
tag_pool = G.P_TAGS
|
||||
end
|
||||
for k, v in pairs(tag_pool) do
|
||||
counter = counter + 1
|
||||
tag_tab[#tag_tab+1] = v
|
||||
end
|
||||
for i = 1, math.ceil(counter / 6) do
|
||||
table.insert(tag_matrix, {})
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''(?<indent>[\t ]*)v\.children\.alert\.states\.collide\.can = false\n[\s\S]{8}end\n[\s\S]{8}return true\n[\s\S]{4}end\)\n[\s\S]{2}\}\)\)\n{3}'''
|
||||
position = 'after'
|
||||
line_prepend = '$indent'
|
||||
payload = '''
|
||||
local table_nodes = {}
|
||||
for i = 1, math.ceil(counter / 6) do
|
||||
table.insert(table_nodes, {n=G.UIT.R, config={align = "cm"}, nodes=tag_matrix[i]})
|
||||
end'''
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''(?<indent>[\t ]*)\{\n[\s\S]{10}\{n=G\.UIT\.R, config=\{align = "cm"\}, nodes=tag_matrix\[1\]},[\s\S]*tag_matrix\[4\]\},\n[\s\S]{8}\}'''
|
||||
position = 'at'
|
||||
line_prepend = '$indent'
|
||||
payload = '''table_nodes'''
|
||||
|
||||
# Tag:generate_UI()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = "tag.lua"
|
||||
pattern = 'G.ASSET_ATLAS\["tags"\]'
|
||||
position = 'at'
|
||||
payload = 'G.ASSET_ATLAS[(not self.hide_ability) and G.P_TAGS[self.key].atlas or "tags"]'
|
||||
|
||||
# Tag:get_uibox_table()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "tag.lua"
|
||||
pattern = '''function Tag:get_uibox_table(tag_sprite)'''
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''function Tag:get_uibox_table(tag_sprite, vars_only)'''
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "tag.lua"
|
||||
pattern = '''tag_sprite.ability_UIBox_table = generate_card_ui(G.P_TAGS[self.key], nil, loc_vars, (self.hide_ability) and 'Undiscovered' or 'Tag', nil, (self.hide_ability))'''
|
||||
position = 'at'
|
||||
match_indent = true
|
||||
payload = '''if vars_only then return loc_vars end
|
||||
tag_sprite.ability_UIBox_table = generate_card_ui(G.P_TAGS[self.key], nil, loc_vars, (self.hide_ability) and 'Undiscovered' or 'Tag', nil, (self.hide_ability), nil, nil, self)'''
|
||||
|
||||
# generate_card_ui()
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/common_events.lua"
|
||||
pattern = "elseif _c.set == 'Tag' then"
|
||||
position = "after"
|
||||
match_indent = true
|
||||
payload = '''specific_vars = specific_vars or Tag.get_uibox_table({ name = _c.name, config = _c.config, ability = { orbital_hand = '['..localize('k_poker_hand')..']' }}, nil, true)
|
||||
'''
|
||||
|
||||
# Prevent Boss Tag from crashing when triggered at a bad time
|
||||
# by quietly rerolling it if blind select UI is not there
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = "G.FUNCS.reroll_boss = function(e)"
|
||||
position = "after"
|
||||
match_indent = true
|
||||
payload = '''if not G.blind_select_opts then
|
||||
G.GAME.round_resets.boss_rerolled = true
|
||||
if not G.from_boss_tag then ease_dollars(-10) end
|
||||
G.from_boss_tag = nil
|
||||
G.GAME.round_resets.blind_choices.Boss = get_new_boss()
|
||||
for i = 1, #G.GAME.tags do
|
||||
if G.GAME.tags[i]:apply_to_run({type = 'new_blind_choice'}) then break end
|
||||
end
|
||||
return true
|
||||
end'''
|
|
@ -1,30 +0,0 @@
|
|||
# Necessary to kill threads which lets us restart the game.
|
||||
|
||||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -5
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/save_manager.lua"
|
||||
pattern = "if request then"
|
||||
position = "after"
|
||||
payload = "if request.type == 'kill' then return end"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/http_manager.lua"
|
||||
pattern = "if request then"
|
||||
position = "after"
|
||||
payload = "if request.type == 'kill' then return end"
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/sound_manager.lua"
|
||||
pattern = "if request then"
|
||||
position = "after"
|
||||
payload = "if request.type == 'kill' then return end"
|
||||
match_indent = true
|
|
@ -1,213 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
### Addition Tab
|
||||
|
||||
## Decks tab
|
||||
# create_UIBox_your_collection_decks()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''G.GAME.viewed_back = Back(G.P_CENTERS.b_red)'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
local deck_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Back)
|
||||
G.GAME.viewed_back = Back(G.ACTIVE_MOD_UI and deck_pool[1] or G.P_CENTERS.b_red)'''
|
||||
match_indent = true
|
||||
|
||||
# create_UIBox_your_collection_decks()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''(?<indent>[\t ]*)for k, v in ipairs\(G\.P_CENTER_POOLS\.Back\) do\n[\s\S]{4}ordered_names\[#ordered_names\+1\] = v\.name\n[\s\S]{2}end'''
|
||||
position = 'at'
|
||||
payload = '''
|
||||
for k, v in ipairs(deck_pool) do
|
||||
ordered_names[#ordered_names+1] = v.key
|
||||
end'''
|
||||
line_prepend = '$indent'
|
||||
|
||||
# create_UIBox_your_collection_decks()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/UI_definitions.lua"
|
||||
pattern = '''local t = create_UIBox_generic_options({ back_func = 'your_collection', contents = {'''
|
||||
position = "at"
|
||||
payload = '''local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = {'''
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.your_collection_deck_page
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "functions/button_callbacks.lua"
|
||||
pattern = '''G.GAME.viewed_back:change_to(G.P_CENTER_POOLS.Back[args.to_key])'''
|
||||
position = "at"
|
||||
payload = '''
|
||||
local deck_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Back)
|
||||
G.GAME.viewed_back:change_to(deck_pool[args.to_key])'''
|
||||
match_indent = true
|
||||
|
||||
# create_UIBox_your_collection()
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''(?<indent>[\t ]*)UIBox_button\(\{button = 'your_collection_blinds', label = \{localize\('b_blinds'\)\}, count = G\.DISCOVER_TALLIES\.blinds, minw = 5, minh = 2.0, id = 'your_collection_blinds', focus_args = \{snap_to = true\}\}\),'''
|
||||
position = 'after'
|
||||
payload = '''UIBox_button({button = 'your_collection_other_gameobjects', label = {localize('k_other')}, minw = 5, id = 'your_collection_other_gameobjects', focus_args = {snap_to = true}}),'''
|
||||
|
||||
# Fix UIElement.config.chosen being overriden if choice=true is set
|
||||
# UIElement:click()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/ui.lua"
|
||||
match_indent = true
|
||||
position = "after"
|
||||
pattern = "if self.config.choice then"
|
||||
payload = " local chosen_temp = self.config.chosen"
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = "engine/ui.lua"
|
||||
match_indent = true
|
||||
position = "at"
|
||||
pattern = "self.config.chosen = true"
|
||||
payload = "self.config.chosen = chosen_temp or true"
|
||||
|
||||
# Escape from mod menu saves config
|
||||
# Needs to be before all checks
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/controller.lua'
|
||||
pattern = "function Controller:key_press_update(key, dt)"
|
||||
position = "after"
|
||||
payload = '''
|
||||
if key == "escape" and G.ACTIVE_MOD_UI then
|
||||
G.FUNCS.exit_mods()
|
||||
end
|
||||
'''
|
||||
match_indent = true
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
position = 'before'
|
||||
line_prepend = '$indent'
|
||||
pattern = '''
|
||||
(?<indent>[\t ]*)return \{n=G\.UIT\.ROOT, config = \{align = 'cm', colour = G\.C\.CLEAR\}, nodes=\{
|
||||
[\t ]*\{n=G\.UIT\.C,'''
|
||||
payload = '''
|
||||
local cols
|
||||
if #info_boxes <= 3 then
|
||||
cols = 1
|
||||
elseif #info_boxes <= 10 then
|
||||
cols = 2
|
||||
elseif #info_boxes <= 24 then
|
||||
cols = 3
|
||||
else
|
||||
cols = 4
|
||||
end
|
||||
local nodes_per_col = math.ceil(#info_boxes/cols)
|
||||
local info_cols = {}
|
||||
for i = 0, cols-1 do
|
||||
local col = {}
|
||||
for j = 1, nodes_per_col do
|
||||
local info_box = info_boxes[i*nodes_per_col+j]
|
||||
if info_box then
|
||||
table.insert(col, info_box)
|
||||
else break end
|
||||
end
|
||||
table.insert(info_cols, {n=G.UIT.C, config = {align="cm"}, nodes = col})
|
||||
end
|
||||
info_boxes = {{n=G.UIT.R, config = {align="cm", padding = 0.05, card_pos = card.T.x }, nodes = info_cols}}
|
||||
'''
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = "config = {offset = {x=-0.03,y=0}, align = 'cl', parent = e}"
|
||||
payload = """config = (not e.config.ref_table or not e.config.ref_table[1].config.card_pos or e.config.ref_table[1].config.card_pos > G.ROOM.T.w*0.4) and
|
||||
{offset = {x=-0.03,y=0}, align = 'cl', parent = e} or
|
||||
{offset = {x=0.03,y=0}, align = 'cr', parent = e}"""
|
||||
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'tag.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = "_self.config.h_popup_config ={align = 'cl', offset = {x=-0.1,y=0},parent = _self}"
|
||||
payload = """_self.config.h_popup_config = (_self.T.x > G.ROOM.T.w*0.4) and
|
||||
{align = 'cl', offset = {x=-0.1,y=0},parent = _self} or
|
||||
{align = 'cr', offset = {x=0.1,y=0},parent = _self}"""
|
||||
|
||||
# desc_from_rows
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
position = 'at'
|
||||
pattern = 'colour = empty and G\.C\.CLEAR or G\.C\.UI\.BACKGROUND_WHITE'
|
||||
payload = 'colour = desc_nodes.background_colour or empty and G.C.CLEAR or G.C.UI.BACKGROUND_WHITE'
|
||||
|
||||
# info_tip_from_rows
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
position = 'at'
|
||||
pattern = 'padding = 0\.05, colour = G\.C\.WHITE\}'
|
||||
payload = 'padding = 0.05, colour = desc_nodes.background_colour or G.C.WHITE}'
|
||||
|
||||
# localize
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/misc_functions.lua'
|
||||
position = 'after'
|
||||
pattern = '\(part\.control\.C and loc_colour\(part\.control\.C\)\)'
|
||||
payload = ' or args.text_colour'
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/misc_functions.lua'
|
||||
position = 'at'
|
||||
pattern = 'loc_colour\(part\.control\.C or nil, args\.default_col\)'
|
||||
payload = 'not part.control.C and args.text_colour or loc_colour(part.control.C or nil, args.default_col)'
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/misc_functions.lua'
|
||||
position = 'after'
|
||||
pattern = 'part\.control\.s and tonumber\(part\.control\.s\)'
|
||||
payload = ' or args.scale '
|
||||
|
||||
# set_discover_tallies()
|
||||
# exclude no_collection objects
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/misc_functions.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = "if not v.omit then"
|
||||
payload = "if not v.omit and not v.no_collection then"
|
||||
|
||||
[[patches]]
|
||||
[patches.regex]
|
||||
target = 'functions/misc_functions.lua'
|
||||
line_prepend = '$indent'
|
||||
position = 'at'
|
||||
pattern = '(?<indent>[\t ]*)(?<start>for _, v in pairs\(G\.P_[BT].*)(?<rest>(\n.*){7})'
|
||||
payload = '''$start
|
||||
if not v.no_collection then
|
||||
$rest
|
||||
end
|
||||
'''
|
||||
|
||||
#set_alerts()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/common_events.lua'
|
||||
match_indent = true
|
||||
position = 'at'
|
||||
pattern = "if v.discovered and not v.alerted then"
|
||||
payload = "if v.discovered and not v.alerted and not v.no_collection then"
|
|
@ -1,121 +0,0 @@
|
|||
[manifest]
|
||||
version = "1.0.0"
|
||||
dump_lua = true
|
||||
priority = -10
|
||||
|
||||
## colour argument fix
|
||||
# create_tabs()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''tab_buttons[#tab_buttons+1] = UIBox_button({id = 'tab_but_'..(v.label or ''), ref_table = v, button = 'change_tab', label = {v.label}, minh = 0.8*args.scale, minw = 2.5*args.scale, col = true, choice = true, scale = args.text_scale, chosen = v.chosen, func = v.func, focus_args = {type = 'none'}})'''
|
||||
position = 'at'
|
||||
payload = '''tab_buttons[#tab_buttons+1] = UIBox_button({id = 'tab_but_'..(v.label or ''), ref_table = v, button = 'change_tab', label = {v.label}, minh = 0.8*args.scale, minw = 2.5*args.scale, col = true, choice = true, scale = args.text_scale, chosen = v.chosen, func = v.func, colour = args.colour, focus_args = {type = 'none'}})'''
|
||||
match_indent = true
|
||||
|
||||
# UIElement:draw_self()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'engine/ui.lua'
|
||||
pattern = '''love.graphics.polygon("fill", get_chosen_triangle_from_rect(self.layered_parallax.x, self.layered_parallax.y, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE, self.config.chosen == 'vert'))'''
|
||||
position = 'before'
|
||||
payload = '''love.graphics.setColor(self.config.colour)'''
|
||||
match_indent = true
|
||||
|
||||
|
||||
## multiple text input fix
|
||||
# create_text_input()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''args.current_prompt_text = '''''
|
||||
position = 'after'
|
||||
payload = '''args.id = args.id or "text_input"'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''ui_letters[#ui_letters+1] = {n=G.UIT.T, config={ref_table = args, ref_value = 'current_prompt_text', scale = args.text_scale, colour = lighten(copy_table(args.colour), 0.4), id = 'prompt'}}'''
|
||||
position = 'at'
|
||||
payload = '''ui_letters[#ui_letters+1] = {n=G.UIT.T, config={ref_table = args, ref_value = 'current_prompt_text', scale = args.text_scale, colour = lighten(copy_table(args.colour), 0.4), id = args.id..'_prompt'}}'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''ui_letters[i] = {n=G.UIT.T, config={ref_table = text.letters, ref_value = i, scale = args.text_scale, colour = G.C.UI.TEXT_LIGHT, id = 'letter_'..i}}'''
|
||||
position = 'at'
|
||||
payload = '''ui_letters[i] = {n=G.UIT.T, config={ref_table = text.letters, ref_value = i, scale = args.text_scale, colour = G.C.UI.TEXT_LIGHT, id = args.id..'_letter_'..i}}'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''ui_letters[#ui_letters+1] = {n=G.UIT.B, config={r = 0.03,w=0.1, h=0.4, colour = position_text_colour, id = 'position', func = 'flash'}}'''
|
||||
position = 'at'
|
||||
payload = '''ui_letters[#ui_letters+1] = {n=G.UIT.B, config={r = 0.03,w=0.1, h=0.4, colour = position_text_colour, id = args.id..'_position', func = 'flash'}}'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''{n=G.UIT.C, config={align = "cm", draw_layer = 1, colour = G.C.CLEAR}, nodes = {'''
|
||||
position = 'at'
|
||||
payload = '''{n=G.UIT.C, config={align = "cm", colour = G.C.CLEAR}, nodes = {'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/UI_definitions.lua'
|
||||
pattern = '''{n=G.UIT.C, config={id = 'text_input', align = "cm", padding = 0.05, r = 0.1, draw_layer = 2, hover = true, colour = args.colour,minw = args.w, min_h = args.h, button = 'select_text_input', shadow = true}, nodes={'''
|
||||
position = 'at'
|
||||
payload = '''{n=G.UIT.C, config={id = args.id, align = "cm", padding = 0.05, r = 0.1, hover = true, colour = args.colour,minw = args.w, min_h = args.h, button = 'select_text_input', shadow = true}, nodes={'''
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.select_text_input
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = '''G.CONTROLLER.text_input_hook = e.children[1].children[1]'''
|
||||
position = 'after'
|
||||
payload = '''G.CONTROLLER.text_input_id = e.config.id'''
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.paste_seed
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = '''G.CONTROLLER.text_input_hook = e.UIBox:get_UIE_by_ID('text_input').children[1].children[1]'''
|
||||
position = 'after'
|
||||
payload = """G.CONTROLLER.text_input_id = 'text_input'"""
|
||||
match_indent = true
|
||||
|
||||
# G.FUNCS.flash
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = '''if G.CONTROLLER.text_input_hook then'''
|
||||
position = 'at'
|
||||
payload = '''if G.CONTROLLER.text_input_hook and G.CONTROLLER.text_input_id == e.config.id:sub(1,string.len(G.CONTROLLER.text_input_id)) then'''
|
||||
match_indent = true
|
||||
|
||||
# TRANSPOSE_TEXT_INPUT()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = '''if hook.children[i].config.id == 'position' then'''
|
||||
position = 'at'
|
||||
payload = '''if hook.children[i].config.id == G.CONTROLLER.text_input_id..'_position' then'''
|
||||
match_indent = true
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = '''local real_letter = hook.children[position_child+dir].config.id:sub(1, 7) == 'letter_' and hook.children[position_child+dir].config.text ~= '''''
|
||||
position = 'at'
|
||||
payload = '''local real_letter = hook.children[position_child+dir].config.id:sub(1, 8+string.len(G.CONTROLLER.text_input_id)) == G.CONTROLLER.text_input_id..'_letter_' and hook.children[position_child+dir].config.text ~= '''''
|
||||
match_indent = true
|
||||
|
||||
# GET_TEXT_FROM_INPUT()
|
||||
[[patches]]
|
||||
[patches.pattern]
|
||||
target = 'functions/button_callbacks.lua'
|
||||
pattern = '''if hook.children[i].config and hook.children[i].config.id:sub(1, 7) == 'letter_' and hook.children[i].config.text ~= '' then'''
|
||||
position = 'at'
|
||||
payload = '''if hook.children[i].config and hook.children[i].config.id:sub(1, 8+string.len(G.CONTROLLER.text_input_id)) == G.CONTROLLER.text_input_id..'_letter_' and hook.children[i].config.text ~= '' then'''
|
||||
match_indent = true
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "Steamodded",
|
||||
"version_number": "0.9.8",
|
||||
"website_url": "https://github.com/Steamopollys/Steamodded",
|
||||
"description": "A Balatro ModLoader",
|
||||
"dependencies": [
|
||||
"Thunderstore-lovely-0.3.1",
|
||||
"metherul-nativefs-1.0.0"
|
||||
]
|
||||
}
|
|
@ -1,536 +0,0 @@
|
|||
SMODS.compat_0_9_8 = {}
|
||||
SMODS.compat_0_9_8.load_done = false
|
||||
|
||||
function SMODS.compat_0_9_8.load()
|
||||
if SMODS.compat_0_9_8.load_done then
|
||||
return
|
||||
end
|
||||
|
||||
function SMODS.compat_0_9_8.delay_register(cls, self)
|
||||
if self.delay_register then
|
||||
self.delay_register = nil
|
||||
return
|
||||
end
|
||||
cls.super.register(self)
|
||||
end
|
||||
|
||||
function SMODS.compat_0_9_8.joker_loc_vars(self, info_queue, card)
|
||||
local vars, main_end
|
||||
if self.loc_def and type(self.loc_def) == 'function' then
|
||||
vars, main_end = self.loc_def(card, info_queue)
|
||||
end
|
||||
if self.tooltip and type(self.tooltip) == 'function' then
|
||||
self.tooltip(self, info_queue)
|
||||
end
|
||||
if vars then
|
||||
return {
|
||||
vars = vars,
|
||||
main_end = main_end
|
||||
}
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
-- Applies to Tarot, Planet, Spectral and Voucher
|
||||
function SMODS.compat_0_9_8.tarot_loc_vars(self, info_queue, card)
|
||||
local vars, main_end
|
||||
if self.loc_def and type(self.loc_def) == 'function' then
|
||||
vars, main_end = self.loc_def(self, info_queue)
|
||||
end
|
||||
if self.tooltip and type(self.tooltip) == 'function' then
|
||||
self.tooltip(self, info_queue)
|
||||
end
|
||||
if vars then
|
||||
return {
|
||||
vars = vars,
|
||||
main_end = main_end
|
||||
}
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
SMODS.compat_0_9_8.init_queue = {}
|
||||
SMODS.INIT = setmetatable({}, {
|
||||
__newindex = function(t, k, v)
|
||||
SMODS.compat_0_9_8.init_queue[k] = v
|
||||
rawset(t, k, v)
|
||||
end
|
||||
})
|
||||
function SMODS.findModByID(id)
|
||||
return SMODS.Mods[id]
|
||||
end
|
||||
function SMODS.end_calculate_context(c)
|
||||
return c.joker_main
|
||||
end
|
||||
function SMODS.LOAD_LOC()
|
||||
init_localization()
|
||||
end
|
||||
|
||||
SMODS.SOUND_SOURCES = SMODS.Sounds
|
||||
function register_sound(name, path, filename)
|
||||
SMODS.Sound {
|
||||
key = name,
|
||||
path = filename,
|
||||
}
|
||||
end
|
||||
function modded_play_sound(sound_code, stop_previous_instance, volume, pitch)
|
||||
return SMODS.Sound.play(nil, pitch, volume, stop_previous_instance, sound_code)
|
||||
end
|
||||
|
||||
SMODS.Card = {
|
||||
SUITS = SMODS.Suits,
|
||||
RANKS = SMODS.Ranks,
|
||||
SUIT_LIST = SMODS.Suit.obj_buffer,
|
||||
RANK_LIST = SMODS.Rank.obj_buffer,
|
||||
}
|
||||
|
||||
SMODS.compat_0_9_8.Deck_new = SMODS.Back:extend {
|
||||
register = function(self)
|
||||
SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Deck_new, self)
|
||||
end,
|
||||
__index = function(t, k)
|
||||
if k == 'slug' then return t.key
|
||||
elseif k == 'spritePos' then return t.pos
|
||||
end
|
||||
return getmetatable(t)[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if k == 'slug' then t.key = v; return
|
||||
elseif k == 'spritePos' then t.pos = v; return
|
||||
end
|
||||
rawset(t, k, v)
|
||||
end,
|
||||
}
|
||||
SMODS.Deck = {}
|
||||
function SMODS.Deck.new(self, name, slug, config, spritePos, loc_txt, unlocked, discovered)
|
||||
return SMODS.compat_0_9_8.Deck_new {
|
||||
name = name,
|
||||
key = slug,
|
||||
config = config,
|
||||
pos = spritePos,
|
||||
loc_txt = loc_txt,
|
||||
unlocked = unlocked,
|
||||
discovered = discovered,
|
||||
atlas = config and config.atlas,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
SMODS.Decks = SMODS.Centers
|
||||
|
||||
SMODS.Sprites = {}
|
||||
SMODS.compat_0_9_8.Sprite_new = SMODS.Atlas:extend {
|
||||
register = function(self)
|
||||
if self.delay_register then
|
||||
self.delay_register = nil
|
||||
return
|
||||
end
|
||||
if self.registered then
|
||||
sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set)
|
||||
return
|
||||
end
|
||||
SMODS.compat_0_9_8.Sprite_new.super.register(self)
|
||||
table.insert(SMODS.Sprites, self)
|
||||
end,
|
||||
__index = function(t, k)
|
||||
if k == 'name' then return t.key
|
||||
end
|
||||
return getmetatable(t)[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if k == 'name' then t.key = v; return
|
||||
end
|
||||
rawset(t, k, v)
|
||||
end,
|
||||
}
|
||||
SMODS.Sprite = {}
|
||||
function SMODS.Sprite.new(self, name, top_lpath, path, px, py, type, frames)
|
||||
local atlas_table
|
||||
if type == 'animation_atli' then
|
||||
atlas_table = 'ANIMATION_ATLAS'
|
||||
else
|
||||
atlas_table = 'ASSET_ATLAS'
|
||||
end
|
||||
return SMODS.compat_0_9_8.Sprite_new {
|
||||
key = name,
|
||||
path = path,
|
||||
atlas_table = atlas_table,
|
||||
px = px,
|
||||
py = py,
|
||||
frames = frames,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
|
||||
SMODS.compat_0_9_8.Joker_new = SMODS.Joker:extend {
|
||||
loc_vars = SMODS.compat_0_9_8.joker_loc_vars,
|
||||
register = function(self)
|
||||
SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Joker_new, self)
|
||||
end,
|
||||
__index = function(t, k)
|
||||
if k == 'slug' then return t.key
|
||||
elseif k == 'atlas' and SMODS.Atlases[t.key] then return t.key
|
||||
elseif k == 'spritePos' then return t.pos
|
||||
end
|
||||
return getmetatable(t)[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if k == 'slug' then t.key = v; return
|
||||
elseif k == 'spritePos' then t.pos = v; return
|
||||
end
|
||||
if k == 'calculate' or k == 'set_ability' or k == 'set_badges' or k == 'update' then
|
||||
local v_ref = v
|
||||
v = function(self, ...)
|
||||
return v_ref(...)
|
||||
end
|
||||
end
|
||||
rawset(t, k, v)
|
||||
end,
|
||||
}
|
||||
function SMODS.Joker.new(self, name, slug, config, spritePos, loc_txt, rarity, cost, unlocked, discovered,
|
||||
blueprint_compat, eternal_compat, effect, atlas, soul_pos)
|
||||
local x = SMODS.compat_0_9_8.Joker_new {
|
||||
name = name,
|
||||
key = slug,
|
||||
config = config,
|
||||
pos = spritePos,
|
||||
loc_txt = loc_txt,
|
||||
rarity = rarity,
|
||||
cost = cost,
|
||||
unlocked = unlocked,
|
||||
discovered = discovered,
|
||||
blueprint_compat = blueprint_compat,
|
||||
eternal_compat = eternal_compat,
|
||||
effect = effect,
|
||||
atlas = atlas,
|
||||
soul_pos = soul_pos,
|
||||
delay_register = true
|
||||
}
|
||||
return x
|
||||
end
|
||||
SMODS.Jokers = SMODS.Centers
|
||||
|
||||
function SMODS.compat_0_9_8.extend_consumable_class(SMODS_cls)
|
||||
local cls
|
||||
cls = SMODS_cls:extend {
|
||||
loc_vars = SMODS.compat_0_9_8.tarot_loc_vars,
|
||||
register = function(self)
|
||||
SMODS.compat_0_9_8.delay_register(cls, self)
|
||||
end,
|
||||
__index = function(t, k)
|
||||
if k == 'slug' then
|
||||
return t.key
|
||||
elseif k == 'atlas' and SMODS.Atlases[t.key] then
|
||||
return t.key
|
||||
end
|
||||
return getmetatable(t)[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if k == 'slug' then
|
||||
t.key = v; return
|
||||
elseif k == 'spritePos' then
|
||||
t.pos = v; return
|
||||
end
|
||||
if k == 'set_badges' or k == 'use' or k == 'can_use' or k == 'update' then
|
||||
local v_ref = v
|
||||
v = function(self, ...)
|
||||
return v_ref(...)
|
||||
end
|
||||
end
|
||||
rawset(t, k, v)
|
||||
end
|
||||
}
|
||||
return cls
|
||||
end
|
||||
|
||||
SMODS.compat_0_9_8.Tarot_new = SMODS.compat_0_9_8.extend_consumable_class(SMODS.Tarot)
|
||||
function SMODS.Tarot.new(self, name, slug, config, pos, loc_txt, cost, cost_mult, effect, consumeable, discovered,
|
||||
atlas)
|
||||
return SMODS.compat_0_9_8.Tarot_new {
|
||||
name = name,
|
||||
key = slug,
|
||||
config = config,
|
||||
pos = pos,
|
||||
loc_txt = loc_txt,
|
||||
cost = cost,
|
||||
cost_mult = cost_mult,
|
||||
effect = effect,
|
||||
consumeable = consumeable,
|
||||
discovered = discovered,
|
||||
atlas = atlas,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
SMODS.Tarots = SMODS.Centers
|
||||
|
||||
SMODS.compat_0_9_8.Planet_new = SMODS.compat_0_9_8.extend_consumable_class(SMODS.Planet)
|
||||
function SMODS.Planet.new(self, name, slug, config, pos, loc_txt, cost, cost_mult, effect, freq, consumeable,
|
||||
discovered, atlas)
|
||||
return SMODS.compat_0_9_8.Planet_new {
|
||||
name = name,
|
||||
key = slug,
|
||||
config = config,
|
||||
pos = pos,
|
||||
loc_txt = loc_txt,
|
||||
cost = cost,
|
||||
cost_mult = cost_mult,
|
||||
effect = effect,
|
||||
freq = freq,
|
||||
consumeable = consumeable,
|
||||
discovered = discovered,
|
||||
atlas = atlas,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
SMODS.Planets = SMODS.Centers
|
||||
|
||||
SMODS.compat_0_9_8.Spectral_new = SMODS.compat_0_9_8.extend_consumable_class(SMODS.Spectral)
|
||||
function SMODS.Spectral.new(self, name, slug, config, pos, loc_txt, cost, consumeable, discovered, atlas)
|
||||
return SMODS.compat_0_9_8.Spectral_new {
|
||||
name = name,
|
||||
key = slug,
|
||||
config = config,
|
||||
pos = pos,
|
||||
loc_txt = loc_txt,
|
||||
cost = cost,
|
||||
consumeable = consumeable,
|
||||
discovered = discovered,
|
||||
atlas = atlas,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
SMODS.Spectrals = SMODS.Centers
|
||||
|
||||
SMODS.compat_0_9_8.Seal_new = SMODS.Seal:extend {
|
||||
class_prefix = false,
|
||||
register = function(self)
|
||||
if self.delay_register then
|
||||
self.delay_register = nil
|
||||
return
|
||||
end
|
||||
if self.registered then
|
||||
sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set)
|
||||
return
|
||||
end
|
||||
if self:check_dependencies() and not self.obj_table[self.label] then
|
||||
self.obj_table[self.label] = self
|
||||
self.obj_buffer[#self.obj_buffer + 1] = self.label
|
||||
self.registered = true
|
||||
end
|
||||
end,
|
||||
__index = function(t, k)
|
||||
if k == 'name' then return t.key
|
||||
end
|
||||
return getmetatable(t)[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if k == 'name' then t.key = v; return
|
||||
end
|
||||
rawset(t, k, v)
|
||||
end,
|
||||
}
|
||||
function SMODS.Seal.new(self, name, label, full_name, pos, loc_txt, atlas, discovered, color)
|
||||
return SMODS.compat_0_9_8.Seal_new {
|
||||
key = name,
|
||||
label = label,
|
||||
full_name = full_name,
|
||||
pos = pos,
|
||||
loc_txt = {
|
||||
description = loc_txt,
|
||||
label = full_name
|
||||
},
|
||||
atlas = atlas,
|
||||
discovered = discovered,
|
||||
colour = color,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
|
||||
SMODS.compat_0_9_8.Voucher_new = SMODS.Voucher:extend {
|
||||
loc_vars = SMODS.compat_0_9_8.tarot_loc_vars,
|
||||
register = function(self)
|
||||
SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Voucher_new, self)
|
||||
end,
|
||||
__index = function(t, k)
|
||||
if k == 'slug' then return t.key
|
||||
elseif k == 'atlas' and SMODS.Atlases[t.key] then return t.key
|
||||
end
|
||||
return getmetatable(t)[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if k == 'slug' then t.key = v; return
|
||||
end
|
||||
if k == 'update' then
|
||||
local v_ref = v
|
||||
v = function(self, ...)
|
||||
return v_ref(...)
|
||||
end
|
||||
elseif k == 'redeem' then
|
||||
local v_ref = v
|
||||
v = function(center, card)
|
||||
local center_table = {
|
||||
name = center and center.name or card and card.ability.name,
|
||||
extra = center and center.config.extra or card and card.ability.extra
|
||||
}
|
||||
return v_ref(center_table)
|
||||
end
|
||||
end
|
||||
rawset(t, k, v)
|
||||
end
|
||||
}
|
||||
function SMODS.Voucher.new(self, name, slug, config, pos, loc_txt, cost, unlocked, discovered, available, requires,
|
||||
atlas)
|
||||
return SMODS.compat_0_9_8.Voucher_new {
|
||||
name = name,
|
||||
key = slug,
|
||||
config = config,
|
||||
pos = pos,
|
||||
loc_txt = loc_txt,
|
||||
cost = cost,
|
||||
unlocked = unlocked,
|
||||
discovered = discovered,
|
||||
available = available,
|
||||
requires = requires,
|
||||
atlas = atlas,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
SMODS.Vouchers = SMODS.Centers
|
||||
|
||||
SMODS.compat_0_9_8.Blind_new = SMODS.Blind:extend {
|
||||
register = function(self)
|
||||
SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Blind_new, self)
|
||||
end,
|
||||
__index = function(t, k)
|
||||
if k == 'slug' then return t.key
|
||||
end
|
||||
return getmetatable(t)[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if k == 'slug' then t.key = v; return
|
||||
end
|
||||
if k == 'set_blind'
|
||||
or k == 'disable'
|
||||
or k == 'defeat'
|
||||
or k == 'debuff_card'
|
||||
or k == 'stay_flipped'
|
||||
or k == 'drawn_to_hand'
|
||||
or k == 'debuff_hand'
|
||||
or k == 'modify_hand'
|
||||
or k == 'press_play'
|
||||
or k == 'get_loc_debuff_text' then
|
||||
local v_ref = v
|
||||
v = function(self, ...)
|
||||
return v_ref(G.GAME.blind, ...)
|
||||
end
|
||||
end
|
||||
rawset(t, k, v)
|
||||
end
|
||||
}
|
||||
function SMODS.Blind.new(self, name, slug, loc_txt, dollars, mult, vars, debuff, pos, boss, boss_colour, defeated,
|
||||
atlas)
|
||||
return SMODS.compat_0_9_8.Blind_new {
|
||||
name = name,
|
||||
key = slug,
|
||||
loc_txt = loc_txt,
|
||||
dollars = dollars,
|
||||
mult = mult,
|
||||
loc_vars = {
|
||||
vars = vars,
|
||||
},
|
||||
debuff = debuff,
|
||||
pos = pos,
|
||||
boss = boss,
|
||||
boss_colour = boss_colour,
|
||||
defeated = defeated,
|
||||
atlas = atlas,
|
||||
delay_register = true
|
||||
}
|
||||
end
|
||||
|
||||
SMODS.compat_0_9_8.loc_proxies = setmetatable({}, {__mode = 'k'})
|
||||
-- Indexing a table `t` that has this metatable instead indexes `t.capture_table`.
|
||||
-- Handles nested indices by instead indexing `t.capture_table` with the
|
||||
-- concatenation of all indices, separated by dots.
|
||||
SMODS.compat_0_9_8.loc_proxy_mt = {
|
||||
__index = function(t, k)
|
||||
if rawget(t, 'stop_capture') then
|
||||
return t.orig_t[k]
|
||||
end
|
||||
local new_idx_str = t.idx_str .. "." .. k
|
||||
-- first check capture_table
|
||||
if t.capture_table[new_idx_str] ~= nil then
|
||||
return t.capture_table[new_idx_str]
|
||||
end
|
||||
-- then fall back to orig_t
|
||||
local orig_v = t.orig_t[k]
|
||||
if type(orig_v) ~= 'table' then
|
||||
-- reached a non-table value, stop proxying
|
||||
return orig_v
|
||||
end
|
||||
local ret = setmetatable({
|
||||
-- concatenation of all indexes, starting from G.localization
|
||||
-- separated by dots and preceded by a dot
|
||||
idx_str = new_idx_str,
|
||||
-- table we would be indexing
|
||||
orig_t = orig_v,
|
||||
capture_table = t.capture_table,
|
||||
}, SMODS.compat_0_9_8.loc_proxy_mt)
|
||||
SMODS.compat_0_9_8.loc_proxies[ret] = true
|
||||
return ret
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
if rawget(t, 'stop_capture') then
|
||||
t.orig_t[k] = v; return
|
||||
end
|
||||
local new_idx_str = t.idx_str .. "." .. k
|
||||
t.capture_table[new_idx_str] = v
|
||||
end
|
||||
}
|
||||
-- Drop-in replacement for G.localization. Captures changes in `capture_table`
|
||||
function SMODS.compat_0_9_8.loc_proxy(capture_table)
|
||||
local ret = setmetatable({
|
||||
idx_str = '',
|
||||
orig_t = G.localization,
|
||||
capture_table = capture_table,
|
||||
}, SMODS.compat_0_9_8.loc_proxy_mt)
|
||||
SMODS.compat_0_9_8.loc_proxies[ret] = true
|
||||
return ret
|
||||
end
|
||||
function SMODS.compat_0_9_8.stop_loc_proxies()
|
||||
collectgarbage()
|
||||
for proxy, _ in pairs(SMODS.compat_0_9_8.loc_proxies) do
|
||||
rawset(proxy, 'stop_capture', true)
|
||||
SMODS.compat_0_9_8.loc_proxies[proxy] = nil
|
||||
end
|
||||
end
|
||||
|
||||
SMODS.compat_0_9_8.load_done = true
|
||||
end
|
||||
|
||||
function SMODS.compat_0_9_8.with_compat(func)
|
||||
SMODS.compat_0_9_8.load()
|
||||
local localization_ref = G.localization
|
||||
init_localization_ref = init_localization
|
||||
local captured_loc = {}
|
||||
G.localization = SMODS.compat_0_9_8.loc_proxy(captured_loc)
|
||||
function init_localization()
|
||||
G.localization = localization_ref
|
||||
init_localization_ref()
|
||||
G.localization = SMODS.compat_0_9_8.loc_proxy(captured_loc)
|
||||
end
|
||||
func()
|
||||
G.localization = localization_ref
|
||||
init_localization = init_localization_ref
|
||||
SMODS.compat_0_9_8.stop_loc_proxies()
|
||||
function SMODS.current_mod.process_loc_text()
|
||||
for idx_str, v in pairs(captured_loc) do
|
||||
local t = G
|
||||
local k = 'localization'
|
||||
for cur_k in idx_str:gmatch("[^%.]+") do
|
||||
t, k = t[k], cur_k
|
||||
end
|
||||
t[k] = v
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,77 +0,0 @@
|
|||
--- STEAMODDED CORE
|
||||
--- MODULE CORE
|
||||
|
||||
SMODS = {}
|
||||
MODDED_VERSION = require'SMODS.version'
|
||||
SMODS.id = 'Steamodded'
|
||||
SMODS.version = MODDED_VERSION:gsub('%-STEAMODDED', '')
|
||||
SMODS.can_load = true
|
||||
SMODS.meta_mod = true
|
||||
SMODS.config_file = 'config.lua'
|
||||
|
||||
-- Include lovely and nativefs modules
|
||||
local nativefs = require "nativefs"
|
||||
local lovely = require "lovely"
|
||||
local json = require "json"
|
||||
|
||||
local lovely_mod_dir = lovely.mod_dir:gsub("/$", "")
|
||||
NFS = nativefs
|
||||
-- make lovely_mod_dir an absolute path.
|
||||
-- respects symlink/.. combos
|
||||
NFS.setWorkingDirectory(lovely_mod_dir)
|
||||
lovely_mod_dir = NFS.getWorkingDirectory()
|
||||
-- make sure NFS behaves the same as love.filesystem
|
||||
NFS.setWorkingDirectory(love.filesystem.getSaveDirectory())
|
||||
|
||||
JSON = json
|
||||
|
||||
local function set_mods_dir()
|
||||
local love_dirs = {
|
||||
love.filesystem.getSaveDirectory(),
|
||||
love.filesystem.getSourceBaseDirectory()
|
||||
}
|
||||
for _, love_dir in ipairs(love_dirs) do
|
||||
if lovely_mod_dir:sub(1, #love_dir) == love_dir then
|
||||
-- relative path from love_dir
|
||||
SMODS.MODS_DIR = lovely_mod_dir:sub(#love_dir+2)
|
||||
NFS.setWorkingDirectory(love_dir)
|
||||
return
|
||||
end
|
||||
end
|
||||
SMODS.MODS_DIR = lovely_mod_dir
|
||||
end
|
||||
set_mods_dir()
|
||||
|
||||
local function find_self(directory, target_filename, target_line, depth)
|
||||
depth = depth or 1
|
||||
if depth > 3 then return end
|
||||
for _, filename in ipairs(NFS.getDirectoryItems(directory)) do
|
||||
local file_path = directory .. "/" .. filename
|
||||
local file_type = NFS.getInfo(file_path).type
|
||||
if file_type == 'directory' or file_type == 'symlink' then
|
||||
local f = find_self(file_path, target_filename, target_line, depth+1)
|
||||
if f then return f end
|
||||
elseif filename == target_filename then
|
||||
local first_line = NFS.read(file_path):match('^(.-)\n')
|
||||
if first_line == target_line then
|
||||
-- use parent directory
|
||||
return directory:match('^(.+/)')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SMODS.path = find_self(SMODS.MODS_DIR, 'core.lua', '--- STEAMODDED CORE')
|
||||
|
||||
for _, path in ipairs {
|
||||
"src/ui.lua",
|
||||
"src/index.lua",
|
||||
"src/utils.lua",
|
||||
"src/overrides.lua",
|
||||
"src/game_object.lua",
|
||||
"src/logging.lua",
|
||||
"src/compat_0_9_8.lua",
|
||||
"src/loader.lua",
|
||||
} do
|
||||
assert(load(NFS.read(SMODS.path..path), ('=[SMODS _ "%s"]'):format(path)))()
|
||||
end
|
|
@ -1,855 +0,0 @@
|
|||
--- STEAMODDED CORE
|
||||
--- MODULE STACKTRACE
|
||||
-- NOTE: This is a modifed version of https://github.com/ignacio/StackTracePlus/blob/master/src/StackTracePlus.lua
|
||||
-- Licensed under the MIT License. See https://github.com/ignacio/StackTracePlus/blob/master/LICENSE
|
||||
-- The MIT License
|
||||
-- Copyright (c) 2010 Ignacio Burgueño
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
-- THE SOFTWARE.
|
||||
-- tables
|
||||
function loadStackTracePlus()
|
||||
local _G = _G
|
||||
local string, io, debug, coroutine = string, io, debug, coroutine
|
||||
|
||||
-- functions
|
||||
local tostring, print, require = tostring, print, require
|
||||
local next, assert = next, assert
|
||||
local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs
|
||||
local error = error
|
||||
|
||||
assert(debug, "debug table must be available at this point")
|
||||
|
||||
local io_open = io.open
|
||||
local string_gmatch = string.gmatch
|
||||
local string_sub = string.sub
|
||||
local table_concat = table.concat
|
||||
|
||||
local _M = {
|
||||
max_tb_output_len = 70 -- controls the maximum length of the 'stringified' table before cutting with ' (more...)'
|
||||
}
|
||||
|
||||
-- this tables should be weak so the elements in them won't become uncollectable
|
||||
local m_known_tables = {
|
||||
[_G] = "_G (global table)"
|
||||
}
|
||||
local function add_known_module(name, desc)
|
||||
local ok, mod = pcall(require, name)
|
||||
if ok then
|
||||
m_known_tables[mod] = desc
|
||||
end
|
||||
end
|
||||
|
||||
add_known_module("string", "string module")
|
||||
add_known_module("io", "io module")
|
||||
add_known_module("os", "os module")
|
||||
add_known_module("table", "table module")
|
||||
add_known_module("math", "math module")
|
||||
add_known_module("package", "package module")
|
||||
add_known_module("debug", "debug module")
|
||||
add_known_module("coroutine", "coroutine module")
|
||||
|
||||
-- lua5.2
|
||||
add_known_module("bit32", "bit32 module")
|
||||
-- luajit
|
||||
add_known_module("bit", "bit module")
|
||||
add_known_module("jit", "jit module")
|
||||
-- lua5.3
|
||||
if _VERSION >= "Lua 5.3" then
|
||||
add_known_module("utf8", "utf8 module")
|
||||
end
|
||||
|
||||
local m_user_known_tables = {}
|
||||
|
||||
local m_known_functions = {}
|
||||
for _, name in ipairs { -- Lua 5.2, 5.1
|
||||
"assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "load", "loadfile", "next", "pairs",
|
||||
"pcall", "print", "rawequal", "rawget", "rawlen", "rawset", "require", "select", "setmetatable", "tonumber",
|
||||
"tostring", "type", "xpcall", -- Lua 5.1
|
||||
"gcinfo", "getfenv", "loadstring", "module", "newproxy", "setfenv", "unpack" -- TODO: add table.* etc functions
|
||||
} do
|
||||
if _G[name] then
|
||||
m_known_functions[_G[name]] = name
|
||||
end
|
||||
end
|
||||
|
||||
local m_user_known_functions = {}
|
||||
|
||||
local function safe_tostring(value)
|
||||
local ok, err = pcall(tostring, value)
|
||||
if ok then
|
||||
return err
|
||||
else
|
||||
return ("<failed to get printable value>: '%s'"):format(err)
|
||||
end
|
||||
end
|
||||
|
||||
-- Private:
|
||||
-- Parses a line, looking for possible function definitions (in a very naïve way)
|
||||
-- Returns '(anonymous)' if no function name was found in the line
|
||||
local function ParseLine(line)
|
||||
assert(type(line) == "string")
|
||||
-- print(line)
|
||||
local match = line:match("^%s*function%s+(%w+)")
|
||||
if match then
|
||||
-- print("+++++++++++++function", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("^%s*local%s+function%s+(%w+)")
|
||||
if match then
|
||||
-- print("++++++++++++local", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("^%s*local%s+(%w+)%s+=%s+function")
|
||||
if match then
|
||||
-- print("++++++++++++local func", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("%s*function%s*%(") -- this is an anonymous function
|
||||
if match then
|
||||
-- print("+++++++++++++function2", match)
|
||||
return "(anonymous)"
|
||||
end
|
||||
return "(anonymous)"
|
||||
end
|
||||
|
||||
-- Private:
|
||||
-- Tries to guess a function's name when the debug info structure does not have it.
|
||||
-- It parses either the file or the string where the function is defined.
|
||||
-- Returns '?' if the line where the function is defined is not found
|
||||
local function GuessFunctionName(info)
|
||||
-- print("guessing function name")
|
||||
if type(info.source) == "string" and info.source:sub(1, 1) == "@" then
|
||||
local file, err = io_open(info.source:sub(2), "r")
|
||||
if not file then
|
||||
print("file not found: " .. tostring(err)) -- whoops!
|
||||
return "?"
|
||||
end
|
||||
local line
|
||||
for _ = 1, info.linedefined do
|
||||
line = file:read("*l")
|
||||
end
|
||||
if not line then
|
||||
print("line not found") -- whoops!
|
||||
return "?"
|
||||
end
|
||||
return ParseLine(line)
|
||||
elseif type(info.source) == "string" and info.source:sub(1, 6) == "=[love" then
|
||||
return "(LÖVE Function)"
|
||||
else
|
||||
local line
|
||||
local lineNumber = 0
|
||||
for l in string_gmatch(info.source, "([^\n]+)\n-") do
|
||||
lineNumber = lineNumber + 1
|
||||
if lineNumber == info.linedefined then
|
||||
line = l
|
||||
break
|
||||
end
|
||||
end
|
||||
if not line then
|
||||
print("line not found") -- whoops!
|
||||
return "?"
|
||||
end
|
||||
return ParseLine(line)
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Dumper instances are used to analyze stacks and collect its information.
|
||||
--
|
||||
local Dumper = {}
|
||||
|
||||
Dumper.new = function(thread)
|
||||
local t = {
|
||||
lines = {}
|
||||
}
|
||||
for k, v in pairs(Dumper) do
|
||||
t[k] = v
|
||||
end
|
||||
|
||||
t.dumping_same_thread = (thread == coroutine.running())
|
||||
|
||||
-- if a thread was supplied, bind it to debug.info and debug.get
|
||||
-- we also need to skip this additional level we are introducing in the callstack (only if we are running
|
||||
-- in the same thread we're inspecting)
|
||||
if type(thread) == "thread" then
|
||||
t.getinfo = function(level, what)
|
||||
if t.dumping_same_thread and type(level) == "number" then
|
||||
level = level + 1
|
||||
end
|
||||
return debug.getinfo(thread, level, what)
|
||||
end
|
||||
t.getlocal = function(level, loc)
|
||||
if t.dumping_same_thread then
|
||||
level = level + 1
|
||||
end
|
||||
return debug.getlocal(thread, level, loc)
|
||||
end
|
||||
else
|
||||
t.getinfo = debug.getinfo
|
||||
t.getlocal = debug.getlocal
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- helpers for collecting strings to be used when assembling the final trace
|
||||
function Dumper:add(text)
|
||||
self.lines[#self.lines + 1] = text
|
||||
end
|
||||
function Dumper:add_f(fmt, ...)
|
||||
self:add(fmt:format(...))
|
||||
end
|
||||
function Dumper:concat_lines()
|
||||
return table_concat(self.lines)
|
||||
end
|
||||
|
||||
---
|
||||
-- Private:
|
||||
-- Iterates over the local variables of a given function.
|
||||
--
|
||||
-- @param level The stack level where the function is.
|
||||
--
|
||||
function Dumper:DumpLocals(level)
|
||||
local prefix = "\t "
|
||||
local i = 1
|
||||
|
||||
if self.dumping_same_thread then
|
||||
level = level + 1
|
||||
end
|
||||
|
||||
local name, value = self.getlocal(level, i)
|
||||
if not name then
|
||||
return
|
||||
end
|
||||
self:add("\tLocal variables:\r\n")
|
||||
while name do
|
||||
if type(value) == "number" then
|
||||
self:add_f("%s%s = number: %g\r\n", prefix, name, value)
|
||||
elseif type(value) == "boolean" then
|
||||
self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value))
|
||||
elseif type(value) == "string" then
|
||||
self:add_f("%s%s = string: %q\r\n", prefix, name, value)
|
||||
elseif type(value) == "userdata" then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value))
|
||||
elseif type(value) == "nil" then
|
||||
self:add_f("%s%s = nil\r\n", prefix, name)
|
||||
elseif type(value) == "table" then
|
||||
if m_known_tables[value] then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value])
|
||||
elseif m_user_known_tables[value] then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value])
|
||||
else
|
||||
local txt = "{"
|
||||
for k, v in pairs(value) do
|
||||
txt = txt .. safe_tostring(k) .. ":" .. safe_tostring(v)
|
||||
if #txt > _M.max_tb_output_len then
|
||||
txt = txt .. " (more...)"
|
||||
break
|
||||
end
|
||||
if next(value, k) then
|
||||
txt = txt .. ", "
|
||||
end
|
||||
end
|
||||
self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt .. "}")
|
||||
end
|
||||
elseif type(value) == "function" then
|
||||
local info = self.getinfo(value, "nS")
|
||||
local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value]
|
||||
if info.what == "C" then
|
||||
self:add_f("%s%s = C %s\r\n", prefix, name,
|
||||
(fun_name and ("function: " .. fun_name) or tostring(value)))
|
||||
else
|
||||
local source = info.short_src
|
||||
if source:sub(2, 7) == "string" then
|
||||
source = source:sub(9) -- uno más, por el espacio que viene (string "Baragent.Main", por ejemplo)
|
||||
end
|
||||
-- for k,v in pairs(info) do print(k,v) end
|
||||
fun_name = fun_name or GuessFunctionName(info)
|
||||
self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name,
|
||||
info.linedefined, source)
|
||||
end
|
||||
elseif type(value) == "thread" then
|
||||
self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value))
|
||||
end
|
||||
i = i + 1
|
||||
name, value = self.getlocal(level, i)
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Public:
|
||||
-- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc.
|
||||
-- This function is suitable to be used as an error handler with pcall or xpcall
|
||||
--
|
||||
-- @param thread An optional thread whose stack is to be inspected (defaul is the current thread)
|
||||
-- @param message An optional error string or object.
|
||||
-- @param level An optional number telling at which level to start the traceback (default is 1)
|
||||
--
|
||||
-- Returns a string with the stack trace and a string with the original error.
|
||||
--
|
||||
function _M.stacktrace(thread, message, level)
|
||||
if type(thread) ~= "thread" then
|
||||
-- shift parameters left
|
||||
thread, message, level = nil, thread, message
|
||||
end
|
||||
|
||||
thread = thread or coroutine.running()
|
||||
|
||||
level = level or 1
|
||||
|
||||
local dumper = Dumper.new(thread)
|
||||
|
||||
local original_error
|
||||
|
||||
if type(message) == "table" then
|
||||
dumper:add("an error object {\r\n")
|
||||
local first = true
|
||||
for k, v in pairs(message) do
|
||||
if first then
|
||||
dumper:add(" ")
|
||||
first = false
|
||||
else
|
||||
dumper:add(",\r\n ")
|
||||
end
|
||||
dumper:add(safe_tostring(k))
|
||||
dumper:add(": ")
|
||||
dumper:add(safe_tostring(v))
|
||||
end
|
||||
dumper:add("\r\n}")
|
||||
original_error = dumper:concat_lines()
|
||||
elseif type(message) == "string" then
|
||||
dumper:add(message)
|
||||
original_error = message
|
||||
end
|
||||
|
||||
dumper:add("\r\n")
|
||||
dumper:add [[
|
||||
Stack Traceback
|
||||
===============
|
||||
]]
|
||||
-- print(error_message)
|
||||
|
||||
local level_to_show = level
|
||||
if dumper.dumping_same_thread then
|
||||
level = level + 1
|
||||
end
|
||||
|
||||
local info = dumper.getinfo(level, "nSlf")
|
||||
while info do
|
||||
if info.what == "main" then
|
||||
if string_sub(info.source, 1, 1) == "@" then
|
||||
dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show,
|
||||
string_sub(info.source, 2), info.currentline)
|
||||
elseif info.source and info.source:sub(1, 1) == "=" then
|
||||
local str = info.source:sub(3, -2)
|
||||
local props = {}
|
||||
-- Split by space
|
||||
for v in string.gmatch(str, "[^%s]+") do
|
||||
table.insert(props, v)
|
||||
end
|
||||
local source = table.remove(props, 1)
|
||||
if source == "love" then
|
||||
dumper:add_f("(%d) main chunk of LÖVE file '%s' at line %d\r\n", level_to_show,
|
||||
table.concat(props, " "):sub(2, -2), info.currentline)
|
||||
elseif source == "SMODS" then
|
||||
local modID = table.remove(props, 1)
|
||||
local fileName = table.concat(props, " ")
|
||||
if modID == '_' then
|
||||
dumper:add_f("(%d) main chunk of Steamodded file '%s' at line %d\r\n", level_to_show,
|
||||
fileName:sub(2, -2), info.currentline)
|
||||
else
|
||||
dumper:add_f("(%d) main chunk of file '%s' at line %d (from mod with id %s)\r\n",
|
||||
level_to_show, fileName:sub(2, -2), info.currentline, modID)
|
||||
end
|
||||
elseif source == "lovely" then
|
||||
local module = table.remove(props, 1)
|
||||
local fileName = table.concat(props, " ")
|
||||
dumper:add_f("(%d) main chunk of file '%s' at line %d (from lovely module %s)\r\n",
|
||||
level_to_show, fileName:sub(2, -2), info.currentline, module)
|
||||
else
|
||||
dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.source,
|
||||
info.currentline)
|
||||
end
|
||||
else
|
||||
dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.source, info.currentline)
|
||||
end
|
||||
elseif info.what == "C" then
|
||||
-- print(info.namewhat, info.name)
|
||||
-- for k,v in pairs(info) do print(k,v, type(v)) end
|
||||
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or
|
||||
tostring(info.func)
|
||||
dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name)
|
||||
-- dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value)))
|
||||
elseif info.what == "tail" then
|
||||
-- print("tail")
|
||||
-- for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name)
|
||||
dumper:add_f("(%d) tail call\r\n", level_to_show)
|
||||
dumper:DumpLocals(level)
|
||||
elseif info.what == "Lua" then
|
||||
local source = info.short_src
|
||||
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name
|
||||
if source:sub(2, 7) == "string" then
|
||||
source = source:sub(9)
|
||||
end
|
||||
local was_guessed = false
|
||||
if not function_name or function_name == "?" then
|
||||
-- for k,v in pairs(info) do print(k,v, type(v)) end
|
||||
function_name = GuessFunctionName(info)
|
||||
was_guessed = true
|
||||
end
|
||||
-- test if we have a file name
|
||||
local function_type = (info.namewhat == "") and "function" or info.namewhat
|
||||
if info.source and info.source:sub(1, 1) == "@" then
|
||||
dumper:add_f("(%d) Lua %s '%s' at file '%s:%d'%s\r\n", level_to_show, function_type, function_name,
|
||||
info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
|
||||
elseif info.source and info.source:sub(1, 1) == '#' then
|
||||
dumper:add_f("(%d) Lua %s '%s' at template '%s:%d'%s\r\n", level_to_show, function_type,
|
||||
function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
|
||||
elseif info.source and info.source:sub(1, 1) == "=" then
|
||||
local str = info.source:sub(3, -2)
|
||||
local props = {}
|
||||
-- Split by space
|
||||
for v in string.gmatch(str, "[^%s]+") do
|
||||
table.insert(props, v)
|
||||
end
|
||||
local source = table.remove(props, 1)
|
||||
if source == "love" then
|
||||
dumper:add_f("(%d) LÖVE %s at file '%s:%d'%s\r\n", level_to_show, function_type,
|
||||
table.concat(props, " "):sub(2, -2), info.currentline, was_guessed and " (best guess)" or "")
|
||||
elseif source == "SMODS" then
|
||||
local modID = table.remove(props, 1)
|
||||
local fileName = table.concat(props, " ")
|
||||
if modID == '_' then
|
||||
dumper:add_f("(%d) Lua %s '%s' at Steamodded file '%s:%d' %s\r\n", level_to_show,
|
||||
function_type, function_name, fileName:sub(2, -2), info.currentline,
|
||||
was_guessed and " (best guess)" or "")
|
||||
else
|
||||
dumper:add_f("(%d) Lua %s '%s' at file '%s:%d' (from mod with id %s)%s\r\n", level_to_show,
|
||||
function_type, function_name, fileName:sub(2, -2), info.currentline, modID,
|
||||
was_guessed and " (best guess)" or "")
|
||||
end
|
||||
elseif source == "lovely" then
|
||||
local module = table.remove(props, 1)
|
||||
local fileName = table.concat(props, " ")
|
||||
dumper:add_f("(%d) Lua %s '%s' at file '%s:%d' (from lovely module %s)%s\r\n", level_to_show,
|
||||
function_type, function_name, fileName:sub(2, -2), info.currentline, module,
|
||||
was_guessed and " (best guess)" or "")
|
||||
else
|
||||
dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type,
|
||||
function_name, info.currentline, source)
|
||||
end
|
||||
else
|
||||
dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type,
|
||||
function_name, info.currentline, source)
|
||||
end
|
||||
dumper:DumpLocals(level)
|
||||
else
|
||||
dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what)
|
||||
end
|
||||
|
||||
level = level + 1
|
||||
level_to_show = level_to_show + 1
|
||||
info = dumper.getinfo(level, "nSlf")
|
||||
end
|
||||
|
||||
return dumper:concat_lines(), original_error
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds a table to the list of known tables
|
||||
function _M.add_known_table(tab, description)
|
||||
if m_known_tables[tab] then
|
||||
error("Cannot override an already known table")
|
||||
end
|
||||
m_user_known_tables[tab] = description
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds a function to the list of known functions
|
||||
function _M.add_known_function(fun, description)
|
||||
if m_known_functions[fun] then
|
||||
error("Cannot override an already known function")
|
||||
end
|
||||
m_user_known_functions[fun] = description
|
||||
end
|
||||
|
||||
return _M
|
||||
end
|
||||
|
||||
-- Note: The below code is not from the original StackTracePlus.lua
|
||||
local stackTraceAlreadyInjected = false
|
||||
|
||||
function getDebugInfoForCrash()
|
||||
local version = VERSION
|
||||
if not version or type(version) ~= "string" then
|
||||
local versionFile = love.filesystem.read("version.jkr")
|
||||
if versionFile then
|
||||
version = versionFile:match("[^\n]*") .. " (best guess)"
|
||||
else
|
||||
version = "???"
|
||||
end
|
||||
end
|
||||
local modded_version = MODDED_VERSION
|
||||
if not modded_version or type(modded_version) ~= "string" then
|
||||
local moddedSuccess, reqVersion = pcall(require, "SMODS.version")
|
||||
if moddedSuccess and type(reqVersion) == "string" then
|
||||
modded_version = reqVersion
|
||||
else
|
||||
modded_version = "???"
|
||||
end
|
||||
end
|
||||
|
||||
local info = "Additional Context:\nBalatro Version: " .. version .. "\nModded Version: " ..
|
||||
(modded_version)
|
||||
local major, minor, revision, codename = love.getVersion()
|
||||
info = info .. string.format("\nLÖVE Version: %d.%d.%d", major, minor, revision)
|
||||
local lovely_success, lovely = pcall(require, "lovely")
|
||||
if lovely_success then
|
||||
info = info .. "\nLovely Version: " .. lovely.version
|
||||
end
|
||||
info = info .. "\nPlatform: " .. (love.system.getOS() or "???")
|
||||
if SMODS and SMODS.Mods then
|
||||
local mod_strings = ""
|
||||
local lovely_strings = ""
|
||||
local i = 1
|
||||
local lovely_i = 1
|
||||
for _, v in pairs(SMODS.Mods) do
|
||||
if (v.can_load and (not v.meta_mod or v.lovely_only)) or (v.lovely and not v.can_load and not v.disabled) then
|
||||
if v.lovely_only or (v.lovely and not v.can_load) then
|
||||
lovely_strings = lovely_strings .. "\n " .. lovely_i .. ": " .. v.name
|
||||
lovely_i = lovely_i + 1
|
||||
if not v.can_load then
|
||||
lovely_strings = lovely_strings .. "\n Has Steamodded mod that failed to load."
|
||||
if #v.load_issues.dependencies > 0 then
|
||||
lovely_strings = lovely_strings .. "\n Missing Dependencies:"
|
||||
for k, v in ipairs(v.load_issues.dependencies) do
|
||||
lovely_strings = lovely_strings .. "\n " .. k .. ". " .. v
|
||||
end
|
||||
end
|
||||
if #v.load_issues.conflicts > 0 then
|
||||
lovely_strings = lovely_strings .. "\n Conflicts:"
|
||||
for k, v in ipairs(v.load_issues.conflicts) do
|
||||
lovely_strings = lovely_strings .. "\n " .. k .. ". " .. v
|
||||
end
|
||||
end
|
||||
if v.load_issues.outdated then
|
||||
lovely_strings = lovely_strings .. "\n Outdated Mod."
|
||||
end
|
||||
if v.load_issues.main_file_not_found then
|
||||
lovely_strings = lovely_strings .. "\n Main file not found. (" .. v.main_file ..")"
|
||||
end
|
||||
end
|
||||
else
|
||||
mod_strings = mod_strings .. "\n " .. i .. ": " .. v.name .. " by " ..
|
||||
table.concat(v.author, ", ") .. " [ID: " .. v.id ..
|
||||
(v.priority ~= 0 and (", Priority: " .. v.priority) or "") ..
|
||||
(v.version and v.version ~= '0.0.0' and (", Version: " .. v.version) or "") ..
|
||||
(v.lovely and (", Uses Lovely") or "") .. "]"
|
||||
i = i + 1
|
||||
local debugInfo = v.debug_info
|
||||
if debugInfo then
|
||||
if type(debugInfo) == "string" then
|
||||
if #debugInfo ~= 0 then
|
||||
mod_strings = mod_strings .. "\n " .. debugInfo
|
||||
end
|
||||
elseif type(debugInfo) == "table" then
|
||||
for kk, vv in pairs(debugInfo) do
|
||||
if type(vv) ~= 'nil' then
|
||||
vv = tostring(vv)
|
||||
end
|
||||
if #vv ~= 0 then
|
||||
mod_strings = mod_strings .. "\n " .. kk .. ": " .. vv
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
info = info .. "\nSteamodded Mods:" .. mod_strings .. "\nLovely Mods:" .. lovely_strings
|
||||
end
|
||||
return info
|
||||
end
|
||||
|
||||
function injectStackTrace()
|
||||
if (stackTraceAlreadyInjected) then
|
||||
return
|
||||
end
|
||||
stackTraceAlreadyInjected = true
|
||||
local STP = loadStackTracePlus()
|
||||
local utf8 = require("utf8")
|
||||
|
||||
-- Modifed from https://love2d.org/wiki/love.errorhandler
|
||||
function love.errorhandler(msg)
|
||||
msg = tostring(msg)
|
||||
|
||||
if not sendErrorMessage then
|
||||
function sendErrorMessage(msg)
|
||||
print(msg)
|
||||
end
|
||||
end
|
||||
if not sendInfoMessage then
|
||||
function sendInfoMessage(msg)
|
||||
print(msg)
|
||||
end
|
||||
end
|
||||
|
||||
sendErrorMessage("Oops! The game crashed\n" .. STP.stacktrace(msg), 'StackTrace')
|
||||
|
||||
if not love.window or not love.graphics or not love.event then
|
||||
return
|
||||
end
|
||||
|
||||
if not love.graphics.isCreated() or not love.window.isOpen() then
|
||||
local success, status = pcall(love.window.setMode, 800, 600)
|
||||
if not success or not status then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset state.
|
||||
if love.mouse then
|
||||
love.mouse.setVisible(true)
|
||||
love.mouse.setGrabbed(false)
|
||||
love.mouse.setRelativeMode(false)
|
||||
if love.mouse.isCursorSupported() then
|
||||
love.mouse.setCursor()
|
||||
end
|
||||
end
|
||||
if love.joystick then
|
||||
-- Stop all joystick vibrations.
|
||||
for i, v in ipairs(love.joystick.getJoysticks()) do
|
||||
v:setVibration()
|
||||
end
|
||||
end
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
|
||||
love.graphics.reset()
|
||||
local font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20)
|
||||
|
||||
local background = {0, 0, 1}
|
||||
if G and G.C and G.C.BLACK then
|
||||
background = G.C.BLACK
|
||||
end
|
||||
love.graphics.clear(background)
|
||||
love.graphics.origin()
|
||||
|
||||
local trace = STP.stacktrace("", 3)
|
||||
|
||||
local sanitizedmsg = {}
|
||||
for char in msg:gmatch(utf8.charpattern) do
|
||||
table.insert(sanitizedmsg, char)
|
||||
end
|
||||
sanitizedmsg = table.concat(sanitizedmsg)
|
||||
|
||||
local err = {}
|
||||
|
||||
table.insert(err, "Oops! The game crashed:")
|
||||
if sanitizedmsg:find("Syntax error: game.lua:4: '=' expected near 'Game'") then
|
||||
table.insert(err,
|
||||
'Duplicate installation of Steamodded detected! Please clean your installation: Steam Library > Balatro > Properties > Installed Files > Verify integrity of game files.')
|
||||
else
|
||||
table.insert(err, sanitizedmsg)
|
||||
end
|
||||
if #sanitizedmsg ~= #msg then
|
||||
table.insert(err, "Invalid UTF-8 string in error message.")
|
||||
end
|
||||
|
||||
local success, msg = pcall(getDebugInfoForCrash)
|
||||
if success and msg then
|
||||
table.insert(err, '\n' .. msg)
|
||||
sendInfoMessage(msg, 'StackTrace')
|
||||
else
|
||||
table.insert(err, "\n" .. "Failed to get additional context :/")
|
||||
sendErrorMessage("Failed to get additional context :/\n" .. msg, 'StackTrace')
|
||||
end
|
||||
|
||||
for l in trace:gmatch("(.-)\n") do
|
||||
table.insert(err, l)
|
||||
end
|
||||
|
||||
local p = table.concat(err, "\n")
|
||||
|
||||
p = p:gsub("\t", "")
|
||||
p = p:gsub("%[string \"(.-)\"%]", "%1")
|
||||
|
||||
local scrollOffset = 0
|
||||
local endHeight = 0
|
||||
love.keyboard.setKeyRepeat(true)
|
||||
|
||||
local function scrollDown(amt)
|
||||
if amt == nil then
|
||||
amt = 18
|
||||
end
|
||||
scrollOffset = scrollOffset + amt
|
||||
if scrollOffset > endHeight then
|
||||
scrollOffset = endHeight
|
||||
end
|
||||
end
|
||||
|
||||
local function scrollUp(amt)
|
||||
if amt == nil then
|
||||
amt = 18
|
||||
end
|
||||
scrollOffset = scrollOffset - amt
|
||||
if scrollOffset < 0 then
|
||||
scrollOffset = 0
|
||||
end
|
||||
end
|
||||
|
||||
local pos = 70
|
||||
local arrowSize = 20
|
||||
|
||||
local function calcEndHeight()
|
||||
local font = love.graphics.getFont()
|
||||
local rw, lines = font:getWrap(p, love.graphics.getWidth() - pos * 2)
|
||||
local lineHeight = font:getHeight()
|
||||
local atBottom = scrollOffset == endHeight and scrollOffset ~= 0
|
||||
endHeight = #lines * lineHeight - love.graphics.getHeight() + pos * 2
|
||||
if (endHeight < 0) then
|
||||
endHeight = 0
|
||||
end
|
||||
if scrollOffset > endHeight or atBottom then
|
||||
scrollOffset = endHeight
|
||||
end
|
||||
end
|
||||
|
||||
local function draw()
|
||||
if not love.graphics.isActive() then
|
||||
return
|
||||
end
|
||||
love.graphics.clear(background)
|
||||
calcEndHeight()
|
||||
love.graphics.printf(p, pos, pos - scrollOffset, love.graphics.getWidth() - pos * 2)
|
||||
if scrollOffset ~= endHeight then
|
||||
love.graphics.polygon("fill", love.graphics.getWidth() - (pos / 2),
|
||||
love.graphics.getHeight() - arrowSize, love.graphics.getWidth() - (pos / 2) + arrowSize,
|
||||
love.graphics.getHeight() - (arrowSize * 2), love.graphics.getWidth() - (pos / 2) - arrowSize,
|
||||
love.graphics.getHeight() - (arrowSize * 2))
|
||||
end
|
||||
if scrollOffset ~= 0 then
|
||||
love.graphics.polygon("fill", love.graphics.getWidth() - (pos / 2), arrowSize,
|
||||
love.graphics.getWidth() - (pos / 2) + arrowSize, arrowSize * 2,
|
||||
love.graphics.getWidth() - (pos / 2) - arrowSize, arrowSize * 2)
|
||||
end
|
||||
love.graphics.present()
|
||||
end
|
||||
|
||||
local fullErrorText = p
|
||||
local function copyToClipboard()
|
||||
if not love.system then
|
||||
return
|
||||
end
|
||||
love.system.setClipboardText(fullErrorText)
|
||||
p = p .. "\nCopied to clipboard!"
|
||||
end
|
||||
|
||||
p = p .. "\n\nPress ESC to exit\nPress R to restart the game"
|
||||
if love.system then
|
||||
p = p .. "\nPress Ctrl+C or tap to copy this error"
|
||||
end
|
||||
|
||||
if G then
|
||||
-- Kill threads (makes restarting possible)
|
||||
if G.SOUND_MANAGER and G.SOUND_MANAGER.channel then
|
||||
G.SOUND_MANAGER.channel:push({
|
||||
type = 'kill'
|
||||
})
|
||||
end
|
||||
if G.SAVE_MANAGER and G.SAVE_MANAGER.channel then
|
||||
G.SAVE_MANAGER.channel:push({
|
||||
type = 'kill'
|
||||
})
|
||||
end
|
||||
if G.HTTP_MANAGER and G.HTTP_MANAGER.channel then
|
||||
G.HTTP_MANAGER.channel:push({
|
||||
type = 'kill'
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return function()
|
||||
love.event.pump()
|
||||
|
||||
for e, a, b, c in love.event.poll() do
|
||||
if e == "quit" then
|
||||
return 1
|
||||
elseif e == "keypressed" and a == "escape" then
|
||||
return 1
|
||||
elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
|
||||
copyToClipboard()
|
||||
elseif e == "keypressed" and a == "r" then
|
||||
SMODS.restart_game()
|
||||
elseif e == "keypressed" and a == "down" then
|
||||
scrollDown()
|
||||
elseif e == "keypressed" and a == "up" then
|
||||
scrollUp()
|
||||
elseif e == "keypressed" and a == "pagedown" then
|
||||
scrollDown(love.graphics.getHeight())
|
||||
elseif e == "keypressed" and a == "pageup" then
|
||||
scrollUp(love.graphics.getHeight())
|
||||
elseif e == "keypressed" and a == "home" then
|
||||
scrollOffset = 0
|
||||
elseif e == "keypressed" and a == "end" then
|
||||
scrollOffset = endHeight
|
||||
elseif e == "wheelmoved" then
|
||||
scrollUp(b * 20)
|
||||
elseif e == "gamepadpressed" and b == "dpdown" then
|
||||
scrollDown()
|
||||
elseif e == "gamepadpressed" and b == "dpup" then
|
||||
scrollUp()
|
||||
elseif e == "gamepadpressed" and b == "a" then
|
||||
return "restart"
|
||||
elseif e == "gamepadpressed" and b == "x" then
|
||||
copyToClipboard()
|
||||
elseif e == "gamepadpressed" and (b == "b" or b == "back" or b == "start") then
|
||||
return 1
|
||||
elseif e == "touchpressed" then
|
||||
local name = love.window.getTitle()
|
||||
if #name == 0 or name == "Untitled" then
|
||||
name = "Game"
|
||||
end
|
||||
local buttons = {"OK", "Cancel", "Restart"}
|
||||
if love.system then
|
||||
buttons[4] = "Copy to clipboard"
|
||||
end
|
||||
local pressed = love.window.showMessageBox("Quit " .. name .. "?", "", buttons)
|
||||
if pressed == 1 then
|
||||
return 1
|
||||
elseif pressed == 3 then
|
||||
return "restart"
|
||||
elseif pressed == 4 then
|
||||
copyToClipboard()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
draw()
|
||||
|
||||
if love.timer then
|
||||
love.timer.sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
injectStackTrace()
|
||||
|
||||
-- ----------------------------------------------
|
||||
-- --------MOD CORE API STACKTRACE END-----------
|
File diff suppressed because it is too large
Load diff
|
@ -1,32 +0,0 @@
|
|||
SMODS.fetch_index = function()
|
||||
SMODS.index = {}
|
||||
local https = require"https"
|
||||
local status, contents = https.request("https://github.com/Aurelius7309/Steamodded.index/archive/refs/heads/main.zip")
|
||||
if status ~= 200 then return false end
|
||||
love.filesystem.write('index.zip', contents)
|
||||
if not love.filesystem.mount('index.zip', 'index') then return false end
|
||||
local path = 'index/Steamodded.index-main/mods/'
|
||||
for _, filename in ipairs(love.filesystem.getDirectoryItems(path)) do
|
||||
local key, ext = filename:sub(1, -6), filename:sub(-5)
|
||||
if ext:lower() == '.json' then
|
||||
local success, data = pcall(function() return JSON.decode(love.filesystem.read(path..filename)) end)
|
||||
if success and data.id == key then SMODS.index[key] = data end
|
||||
end
|
||||
end
|
||||
love.filesystem.unmount('index.zip')
|
||||
return true
|
||||
end
|
||||
|
||||
SMODS.update_mod_files = function(id)
|
||||
local mod = SMODS.Mods[id]
|
||||
if not mod then return false end
|
||||
local use_git = os.execute('git -v') == 0
|
||||
if false and use_git and os.execute(('cd %s & git pull'):format(mod.path)) == 0 then
|
||||
return true
|
||||
end
|
||||
local https = require"https"
|
||||
local url = mod.github or (SMODS.index[id] or {}).github
|
||||
local status, contents = https.request(url) -- TODO account for branches
|
||||
local hash = contents:match('"currentOid":"([^"]*)"')
|
||||
sendWarnMessage(hash, "Index")
|
||||
end
|
|
@ -1,728 +0,0 @@
|
|||
--- STEAMODDED CORE
|
||||
--- MODULE MODLOADER
|
||||
|
||||
function loadMods(modsDirectory)
|
||||
SMODS.Mods = {}
|
||||
SMODS.Mods[SMODS.id] = SMODS
|
||||
SMODS.Mods['Lovely'] = {
|
||||
id = 'Lovely',
|
||||
can_load = true,
|
||||
version = require'lovely'.version,
|
||||
meta_mod = true,
|
||||
}
|
||||
SMODS.Mods['Balatro'] = {
|
||||
id = 'Balatro',
|
||||
can_load = true,
|
||||
version = G.VERSION,
|
||||
meta_mod = true,
|
||||
}
|
||||
SMODS.mod_priorities = {}
|
||||
SMODS.mod_list = {}
|
||||
SMODS.provided_mods = {}
|
||||
-- for legacy header support
|
||||
local header_components = {
|
||||
name = { pattern = '%-%-%- MOD_NAME: ([^\n]+)\n', required = true },
|
||||
id = { pattern = '%-%-%- MOD_ID: ([^ \n]+)\n', required = true },
|
||||
author = { pattern = '%-%-%- MOD_AUTHOR: %[(.-)%]\n', required = true, parse_array = true },
|
||||
description = { pattern = '%-%-%- MOD_DESCRIPTION: (.-)\n', required = true },
|
||||
priority = { pattern = '%-%-%- PRIORITY: (%-?%d+)\n', handle = function(x) return x and x + 0 or 0 end },
|
||||
badge_colour = { pattern = '%-%-%- BADGE_COLO[U]?R: (%x-)\n', handle = function(x) return HEX(x or '666666FF') end },
|
||||
badge_text_colour = { pattern = '%-%-%- BADGE_TEXT_COLO[U]?R: (%x-)\n', handle = function(x) return HEX(x or 'FFFFFF') end },
|
||||
display_name = { pattern = '%-%-%- DISPLAY_NAME: (.-)\n' },
|
||||
dependencies = {
|
||||
pattern = {
|
||||
'%-%-%- DEPENDENCIES: %[(.-)%]\n',
|
||||
'%-%-%- DEPENDS: %[(.-)%]\n',
|
||||
'%-%-%- DEPS: %[(.-)%]\n',
|
||||
},
|
||||
parse_array = true,
|
||||
handle = function(x)
|
||||
local t = {}
|
||||
for _, v in ipairs(x) do
|
||||
table.insert(t, {
|
||||
id = v:match '(.-)[<>]' or v,
|
||||
min_version = v:match '>=([^<>]+)',
|
||||
max_version = v:match '<=([^<>]+)',
|
||||
})
|
||||
end
|
||||
return t
|
||||
end,
|
||||
},
|
||||
conflicts = {
|
||||
pattern = '%-%-%- CONFLICTS: %[(.-)%]\n',
|
||||
parse_array = true,
|
||||
handle = function(x)
|
||||
local t = {}
|
||||
for _, v in ipairs(x) do
|
||||
table.insert(t, {
|
||||
id = v:match '(.-)[<>]',
|
||||
min_version = v:match '>=([^<>]+)',
|
||||
max_version = v:match '<=([^<>]+)',
|
||||
})
|
||||
if t.min_version and not V(t[#t].min_version):is_valid() then t[#t].min_version = nil end
|
||||
if t.max_version and not V(t[#t].max_version):is_valid() then t[#t].max_version = nil end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
},
|
||||
prefix = { pattern = '%-%-%- PREFIX: (.-)\n' },
|
||||
version = { pattern = '%-%-%- VERSION: (.-)\n', handle = function(x) return x and V(x):is_valid() and x or '0.0.0' end },
|
||||
outdated = { pattern = { 'SMODS%.INIT', 'SMODS%.Deck[:.]new' } },
|
||||
dump_loc = { pattern = { '%-%-%- DUMP_LOCALIZATION\n'}}
|
||||
}
|
||||
|
||||
|
||||
local json_spec = {
|
||||
id = { type = 'string', required = true },
|
||||
author = { type = 'table', required = true, check = function(mod, t)
|
||||
for k, v in pairs(t) do
|
||||
if type(k) ~= 'number' or type(v) ~= 'string' then t[k] = nil end
|
||||
end
|
||||
return t
|
||||
end },
|
||||
name = { type = 'string', required = true },
|
||||
display_name = { type = 'string', check = function(mod, s) mod.display_name = s or mod.name end },
|
||||
description = { type = 'string', required = true },
|
||||
priority = { type = 'number', default = 0 },
|
||||
badge_colour = { type = 'string', check = function(mod, s) local success, hex = pcall(HEX, s); mod.badge_colour = success and hex or HEX('666665FF') end },
|
||||
badge_text_colour = { type = 'string', check = function(mod, s) local success, hex = pcall(HEX, s); mod.badge_text_colour = success and hex or HEX('FFFFFFFF') end},
|
||||
prefix = { type = 'string', required = true },
|
||||
version = { type = 'string', check = function(mod, x) return x and V(x):is_valid() and x or '0.0.0' end },
|
||||
dump_loc = { type = 'boolean' },
|
||||
dependencies = { type = 'table', check = function(mod, t)
|
||||
local ops = {
|
||||
['<<'] = function(a,b) return a<b end,
|
||||
-- ['<~'] = function(a,b) return a<b end,
|
||||
['>>'] = function(a,b) return a>b end,
|
||||
['<='] = function(a,b) return a<=b end,
|
||||
['>='] = function(a,b) return a>=b end,
|
||||
['=='] = function(a,b) return a==b end
|
||||
}
|
||||
for i,v in ipairs(t or {}) do
|
||||
local parts = {}
|
||||
parts.str = v
|
||||
for part in v:gmatch('([^|]+)') do
|
||||
local x = {}
|
||||
x.id = part:match '^([^(%s]+)'
|
||||
local j = 1
|
||||
for version_string in string.gmatch(part, '%((.-)%)') do
|
||||
local operator, version = string.match(version_string, '^(..)(.*)$')
|
||||
local op = ops[operator]
|
||||
local ver = V(version)
|
||||
-- if operator == '<<' and not ver.rev then
|
||||
-- ver.beta = -1
|
||||
-- ver.rev = '~'
|
||||
-- end
|
||||
if op and ver:is_valid(true) then
|
||||
x[j] = { op = op, ver = ver }
|
||||
j = j+1
|
||||
end
|
||||
end
|
||||
parts[#parts+1] = x
|
||||
end
|
||||
t[i] = parts
|
||||
end
|
||||
end},
|
||||
conflicts = { type = 'table', check = function(mod, t)
|
||||
local ops = {
|
||||
['<<'] = function(a,b) return a<b end,
|
||||
--['<~'] = function(a,b) return a<b end,
|
||||
['>>'] = function(a,b) return a>b end,
|
||||
['<='] = function(a,b) return a<=b end,
|
||||
['>='] = function(a,b) return a>=b end,
|
||||
['=='] = function(a,b) return a==b end
|
||||
}
|
||||
for i,v in ipairs(t or {}) do
|
||||
v = v:gsub('%s', '')
|
||||
local x = {}
|
||||
x.str = v
|
||||
v = v:gsub('%s', '')
|
||||
x.id = v:match '^([^(%s]+)'
|
||||
local j = 1
|
||||
for version_string in string.gmatch(v, '%((.-)%)') do
|
||||
local operator, version = string.match(version_string, '^(..)(.*)$')
|
||||
local op = ops[operator]
|
||||
local ver = V(version)
|
||||
-- if operator == '<<' and not ver.rev then
|
||||
-- ver.beta = -1
|
||||
-- ver.rev = '~'
|
||||
-- end
|
||||
if op and ver:is_valid(true) then
|
||||
x[j] = { op = op, ver = ver, str = '('..version_string..')' }
|
||||
j = j+1
|
||||
end
|
||||
end
|
||||
t[i] = x
|
||||
end
|
||||
end},
|
||||
main_file = { type = 'string', required = true },
|
||||
config_file = {type = 'string', default = 'config.lua' },
|
||||
__ = { check = function(mod)
|
||||
if SMODS.Mods[mod.id] then error('dupe') end
|
||||
end},
|
||||
provides = { type = 'table', check = function(mod, t)
|
||||
t = t or {}
|
||||
for _,v in pairs(t) do
|
||||
v = v:gsub('%s', '')
|
||||
local id = v:match '^([^(%s]+)'
|
||||
local ver = v:match '%((.-)%)'
|
||||
ver = (ver and V(ver):is_valid()) and ver or mod.version
|
||||
if id and ver then
|
||||
SMODS.provided_mods[id] = SMODS.provided_mods[id] or {}
|
||||
table.insert(SMODS.provided_mods[id], { version = ver, mod = mod })
|
||||
end
|
||||
end
|
||||
end}
|
||||
|
||||
}
|
||||
|
||||
|
||||
local used_prefixes = {}
|
||||
local lovely_directories = {}
|
||||
|
||||
-- Function to process each directory (including subdirectories) with depth tracking
|
||||
local function processDirectory(directory, depth)
|
||||
if depth > 3 or directory..'/' == SMODS.path then
|
||||
return
|
||||
end
|
||||
|
||||
local isDirLovely = false
|
||||
|
||||
for _, filename in ipairs(NFS.getDirectoryItems(directory)) do
|
||||
local file_path = directory .. "/" .. filename
|
||||
|
||||
-- Check if the current file is a directory
|
||||
local file_type = NFS.getInfo(file_path).type
|
||||
if file_type == 'directory' or file_type == 'symlink' then
|
||||
-- Lovely patches
|
||||
if depth == 2 and filename == "lovely" and not isDirLovely then
|
||||
isDirLovely = true
|
||||
table.insert(lovely_directories, directory .. "/")
|
||||
end
|
||||
-- If it's a directory and depth is within limit, recursively process it
|
||||
if depth < 2 or (filename:lower() ~= 'localization' and filename:lower() ~= 'assets') then
|
||||
processDirectory(file_path, depth + 1)
|
||||
end
|
||||
elseif depth == 2 and filename == "lovely.toml" and not isDirLovely then
|
||||
isDirLovely = true
|
||||
table.insert(lovely_directories, directory .. "/")
|
||||
elseif filename:lower():match('%.json') and depth > 1 then
|
||||
local json_str = NFS.read(file_path)
|
||||
local parsed, mod = pcall(JSON.decode, json_str)
|
||||
local valid = true
|
||||
local err
|
||||
if not parsed then
|
||||
valid = false
|
||||
err = mod
|
||||
else
|
||||
mod.json = true
|
||||
mod.path = directory .. '/'
|
||||
mod.optional_dependencies = {}
|
||||
local success, e = pcall(function()
|
||||
-- remove invalid fields and check required ones first
|
||||
for k, v in pairs(json_spec) do
|
||||
if v.type and type(mod[k]) ~= v.type then mod[k] = nil end
|
||||
if v.required and mod[k] == nil then error(k) end
|
||||
end
|
||||
-- perform additional checks and fill in defaults
|
||||
for k, v in pairs(json_spec) do
|
||||
if v.default then mod[k] = mod[k] or v.default end
|
||||
if v.check then v.check(mod, mod[k]) end
|
||||
end
|
||||
end)
|
||||
if not success then
|
||||
valid = false
|
||||
err = e
|
||||
end
|
||||
end
|
||||
if not valid then
|
||||
sendErrorMessage(('Found invalid metadata JSON file at %s, ignoring: %s'):format(file_path, err), 'Loader')
|
||||
else
|
||||
sendInfoMessage('Valid JSON file found')
|
||||
if NFS.getInfo(directory..'/.lovelyignore') then
|
||||
mod.disabled = true
|
||||
end
|
||||
if mod.prefix and used_prefixes[mod.prefix] then
|
||||
mod.can_load = false
|
||||
mod.load_issues = {
|
||||
prefix_conflict = used_prefixes[mod.prefix],
|
||||
dependencies = {},
|
||||
conflicts = {},
|
||||
}
|
||||
sendWarnMessage(('Duplicate Mod prefix %s used by %s, %s'):format(mod.prefix, mod.id, used_prefixes[mod.prefix]), 'Loader')
|
||||
end
|
||||
if not NFS.getInfo(mod.path..mod.main_file) then
|
||||
mod.can_load = false
|
||||
mod.load_issues = {
|
||||
main_file_not_found = true,
|
||||
dependencies = {},
|
||||
conflicts = {},
|
||||
}
|
||||
sendWarnMessage(('Unable to load Mod %s: cannot find main file'):format(mod.id), 'Loader')
|
||||
end
|
||||
if mod.dump_loc then
|
||||
SMODS.dump_loc = {
|
||||
path = mod.path,
|
||||
}
|
||||
end
|
||||
SMODS.Mods[mod.id] = mod
|
||||
SMODS.mod_priorities[mod.priority] = SMODS.mod_priorities[mod.priority] or {}
|
||||
table.insert(SMODS.mod_priorities[mod.priority], mod)
|
||||
end
|
||||
elseif filename:lower():match("%.lua$") then -- Check for legacy headers
|
||||
if depth == 1 then
|
||||
sendWarnMessage(('Found lone Lua file %s in Mods directory :: Please place the files for each mod in its own subdirectory.'):format(filename), 'Loader')
|
||||
end
|
||||
local file_content = NFS.read(file_path)
|
||||
|
||||
-- Convert CRLF in LF
|
||||
file_content = file_content:gsub("\r\n", "\n")
|
||||
|
||||
-- Check the header lines using string.match
|
||||
local headerLine = file_content:match("^(.-)\n")
|
||||
if headerLine == "--- STEAMODDED HEADER" then
|
||||
sendTraceMessage('Processing Mod file (Legacy header): ' .. filename, "Loader")
|
||||
local mod = {}
|
||||
local sane = true
|
||||
for k, v in pairs(header_components) do
|
||||
local component = nil
|
||||
if type(v.pattern) == "table" then
|
||||
for _, pattern in ipairs(v.pattern) do
|
||||
component = file_content:match(pattern) or component
|
||||
if component then break end
|
||||
end
|
||||
else
|
||||
component = file_content:match(v.pattern)
|
||||
end
|
||||
if v.required and not component then
|
||||
sane = false
|
||||
sendWarnMessage(string.format('Mod file %s is missing required header component: %s',
|
||||
filename, k), 'Loader')
|
||||
break
|
||||
end
|
||||
if v.parse_array then
|
||||
local list = {}
|
||||
component = component or ''
|
||||
for val in string.gmatch(component, "([^,]+)") do
|
||||
table.insert(list, val:match("^%s*(.-)%s*$")) -- Trim spaces
|
||||
end
|
||||
component = list
|
||||
end
|
||||
if v.handle and type(v.handle) == 'function' then
|
||||
component = v.handle(component)
|
||||
end
|
||||
mod[k] = component
|
||||
end
|
||||
if NFS.getInfo(directory..'/.lovelyignore') then
|
||||
mod.disabled = true
|
||||
end
|
||||
if SMODS.Mods[mod.id] then
|
||||
sane = false
|
||||
sendWarnMessage("Duplicate Mod ID: " .. mod.id, 'Loader')
|
||||
end
|
||||
|
||||
if mod.outdated then
|
||||
mod.prefix_config = { key = { mod = false }, atlas = false }
|
||||
else
|
||||
mod.prefix = mod.prefix or (mod.id or ''):lower():sub(1, 4)
|
||||
end
|
||||
if mod.prefix and used_prefixes[mod.prefix] then
|
||||
mod.can_load = false
|
||||
mod.load_issues = {
|
||||
prefix_conflict = used_prefixes[mod.prefix],
|
||||
dependencies = {},
|
||||
conflicts = {},
|
||||
}
|
||||
sendWarnMessage(('Duplicate Mod prefix %s used by %s, %s'):format(mod.prefix, mod.id, used_prefixes[mod.prefix]), 'Loader')
|
||||
end
|
||||
|
||||
if sane then
|
||||
sendTraceMessage('Saving Mod Info: ' .. mod.id, 'Loader')
|
||||
mod.path = directory .. '/'
|
||||
mod.main_file = filename
|
||||
mod.display_name = mod.display_name or mod.name
|
||||
if mod.prefix then
|
||||
used_prefixes[mod.prefix] = mod.id
|
||||
end
|
||||
mod.optional_dependencies = {}
|
||||
if mod.dump_loc then
|
||||
SMODS.dump_loc = {
|
||||
path = mod.path,
|
||||
}
|
||||
end
|
||||
SMODS.Mods[mod.id] = mod
|
||||
SMODS.mod_priorities[mod.priority] = SMODS.mod_priorities[mod.priority] or {}
|
||||
table.insert(SMODS.mod_priorities[mod.priority], mod)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
boot_print_stage('Processing Mod Files')
|
||||
-- Start processing with the initial directory at depth 1
|
||||
processDirectory(modsDirectory, 1)
|
||||
for _, path in ipairs(lovely_directories) do
|
||||
local hasSMOD = false
|
||||
for _, mod in pairs(SMODS.Mods) do
|
||||
if mod.path == path then
|
||||
mod.lovely = true
|
||||
hasSMOD = true
|
||||
end
|
||||
end
|
||||
if not hasSMOD then
|
||||
local name = string.match(path, "[/\\]([^/\\]+)[/\\]?$")
|
||||
local disabled = not not NFS.getInfo(path .. '/.lovelyignore')
|
||||
local mod = {
|
||||
name = name,
|
||||
id = "lovely-compat-" .. name,
|
||||
author = {"???"},
|
||||
description = "A lovely mod.",
|
||||
prefix_config = { key = { mod = false }, atlas = false },
|
||||
priority = 0,
|
||||
badge_colour = HEX("666666FF"),
|
||||
badge_text_colour = HEX('FFFFFF'),
|
||||
path = path,
|
||||
main_file = "",
|
||||
display_name = name,
|
||||
dependencies = {},
|
||||
optional_dependencies = {},
|
||||
conflicts = {},
|
||||
version = "0.0.0",
|
||||
can_load = not disabled,
|
||||
lovely = true,
|
||||
lovely_only = true,
|
||||
meta_mod = true,
|
||||
disabled = disabled,
|
||||
load_issues = {
|
||||
dependencies = {},
|
||||
conflicts = {},
|
||||
disabled = disabled
|
||||
}
|
||||
|
||||
}
|
||||
SMODS.mod_priorities[mod.priority] = SMODS.mod_priorities[mod.priority] or {}
|
||||
table.insert(SMODS.mod_priorities[mod.priority], mod)
|
||||
SMODS.Mods[mod.id] = mod
|
||||
end
|
||||
end
|
||||
|
||||
-- sort by priority
|
||||
local keyset = {}
|
||||
for k, _ in pairs(SMODS.mod_priorities) do
|
||||
keyset[#keyset + 1] = k
|
||||
end
|
||||
table.sort(keyset)
|
||||
|
||||
local function check_dependencies(mod, seen)
|
||||
if not (mod.can_load == nil) then return mod.can_load end
|
||||
seen = seen or {}
|
||||
local can_load = true
|
||||
if seen[mod.id] then return true end
|
||||
seen[mod.id] = true
|
||||
local load_issues = {
|
||||
dependencies = {},
|
||||
conflicts = {},
|
||||
}
|
||||
if not mod.json then
|
||||
for _, v in ipairs(mod.conflicts or {}) do
|
||||
-- block load even if the conflict is also blocked
|
||||
if
|
||||
SMODS.Mods[v.id] and
|
||||
(not v.max_version or V(SMODS.Mods[v.id].version) <= V(v.max_version)) and
|
||||
(not v.min_version or V(SMODS.Mods[v.id].version) >= V(v.min_version))
|
||||
then
|
||||
can_load = false
|
||||
table.insert(load_issues.conflicts, v.id..(v.max_version and '<='..v.max_version or '')..(v.min_version and '>='..v.min_version or ''))
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(mod.dependencies or {}) do
|
||||
-- recursively check dependencies of dependencies to make sure they are actually fulfilled
|
||||
if
|
||||
not SMODS.Mods[v.id] or
|
||||
not check_dependencies(SMODS.Mods[v.id], seen) or
|
||||
(v.max_version and V(SMODS.Mods[v.id].version) > V(v.max_version)) or
|
||||
(v.min_version and V(SMODS.Mods[v.id].version) < V(v.min_version))
|
||||
then
|
||||
can_load = false
|
||||
table.insert(load_issues.dependencies,
|
||||
v.id .. (v.min_version and '>=' .. v.min_version or '') .. (v.max_version and '<=' .. v.max_version or ''))
|
||||
if v.id == 'Steamodded' then
|
||||
load_issues.version_mismatch = ''..(v.min_version and '>='..v.min_version or '')..(v.max_version and '<='..v.max_version or '')
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, x in ipairs(mod.dependencies or {}) do
|
||||
local fulfilled
|
||||
for _, y in ipairs(x) do
|
||||
if fulfilled then break end
|
||||
local id = y.id
|
||||
if SMODS.Mods[id] and check_dependencies(SMODS.Mods[id], seen) then
|
||||
fulfilled = true
|
||||
local dep_ver = V(SMODS.Mods[id].version)
|
||||
for _, v in ipairs(y) do
|
||||
if not v.op(dep_ver, v.ver) then
|
||||
fulfilled = false
|
||||
end
|
||||
end
|
||||
if fulfilled then y.fulfilled = true end
|
||||
else
|
||||
for _, provided in ipairs(SMODS.provided_mods[id] or {}) do
|
||||
if provided.mod ~= mod and check_dependencies(provided.mod, seen) then
|
||||
fulfilled = true
|
||||
local dep_ver = V(provided.version)
|
||||
for _, v in ipairs(y) do
|
||||
if not v.op(dep_ver, v.ver) then
|
||||
fulfilled = false
|
||||
end
|
||||
end
|
||||
if fulfilled then y.fulfilled = true; y.provided = provided end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not fulfilled then
|
||||
can_load = false
|
||||
table.insert(load_issues.dependencies, x.str)
|
||||
end
|
||||
end
|
||||
for _, y in ipairs(mod.conflicts or {}) do
|
||||
local id = y.id
|
||||
local conflict = false
|
||||
if SMODS.Mods[id] and check_dependencies(SMODS.Mods[id], seen) then
|
||||
conflict = true
|
||||
local dep_ver = V(SMODS.Mods[id].version)
|
||||
for _, v in ipairs(y) do
|
||||
if not v.op(dep_ver, v.ver) then
|
||||
conflict = false
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, provided in ipairs(SMODS.provided_mods[id] or {}) do
|
||||
if provided.mod ~= mod and check_dependencies(provided.mod, seen) then
|
||||
conflict = true
|
||||
local dep_ver = V(provided.version)
|
||||
for _, v in ipairs(y) do
|
||||
if not v.op(dep_ver, v.ver) then
|
||||
conflict = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if conflict then
|
||||
can_load = false
|
||||
table.insert(load_issues.conflicts, y.str)
|
||||
end
|
||||
end
|
||||
end
|
||||
if mod.disabled then
|
||||
can_load = false
|
||||
load_issues.disabled = true
|
||||
end
|
||||
if not can_load then
|
||||
mod.load_issues = load_issues
|
||||
return false
|
||||
end
|
||||
for _, x in ipairs(mod.dependencies or {}) do
|
||||
for _, y in ipairs(x) do
|
||||
if y.fulfilled then
|
||||
if y.provided then
|
||||
y.provided.mod.can_load = true
|
||||
else
|
||||
SMODS.Mods[y.id].can_load = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- check dependencies first (for object dependencies)
|
||||
for _, mod in pairs(SMODS.Mods) do mod.can_load = check_dependencies(mod) end
|
||||
|
||||
boot_print_stage('Loading Mods')
|
||||
-- load the mod files
|
||||
for _, priority in ipairs(keyset) do
|
||||
table.sort(SMODS.mod_priorities[priority],
|
||||
function(mod_a, mod_b)
|
||||
return mod_a.id < mod_b.id
|
||||
end)
|
||||
for _, mod in ipairs(SMODS.mod_priorities[priority]) do
|
||||
SMODS.mod_list[#SMODS.mod_list + 1] = mod -- keep mod list in prioritized load order
|
||||
if mod.can_load and not mod.lovely_only then
|
||||
SMODS.current_mod = mod
|
||||
if mod.outdated then
|
||||
SMODS.compat_0_9_8.with_compat(function()
|
||||
mod.config = {}
|
||||
assert(load(NFS.read(mod.path..mod.main_file), ('=[SMODS %s "%s"]'):format(mod.id, mod.main_file)))()
|
||||
for k, v in pairs(SMODS.compat_0_9_8.init_queue) do
|
||||
v()
|
||||
SMODS.compat_0_9_8.init_queue[k] = nil
|
||||
end
|
||||
end)
|
||||
else
|
||||
SMODS.load_mod_config(mod)
|
||||
assert(load(NFS.read(mod.path..mod.main_file), ('=[SMODS %s "%s"]'):format(mod.id, mod.main_file)))()
|
||||
end
|
||||
SMODS.current_mod = nil
|
||||
elseif not mod.lovely_only then
|
||||
sendTraceMessage(string.format("Mod %s was unable to load: %s%s%s%s", mod.id,
|
||||
mod.load_issues.outdated and
|
||||
'Outdated: Steamodded versions 0.9.8 and below are no longer supported!\n' or '',
|
||||
mod.load_issues.main_file_not_found and "The main file could not be found.\n" or '',
|
||||
next(mod.load_issues.dependencies) and
|
||||
('Missing Dependencies: ' .. inspect(mod.load_issues.dependencies) .. '\n') or '',
|
||||
next(mod.load_issues.conflicts) and
|
||||
('Unresolved Conflicts: ' .. inspect(mod.load_issues.conflicts) .. '\n') or ''
|
||||
), 'Loader')
|
||||
end
|
||||
end
|
||||
end
|
||||
SMODS.get_optional_features()
|
||||
-- compat after loading mods
|
||||
if SMODS.compat_0_9_8.load_done then
|
||||
-- Invasive change to Card:generate_UIBox_ability_table()
|
||||
local Card_generate_UIBox_ability_table_ref = Card.generate_UIBox_ability_table
|
||||
function Card:generate_UIBox_ability_table(...)
|
||||
SMODS.compat_0_9_8.generate_UIBox_ability_table_card = self
|
||||
local ret = Card_generate_UIBox_ability_table_ref(self, ...)
|
||||
SMODS.compat_0_9_8.generate_UIBox_ability_table_card = nil
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SMODS.injectItems()
|
||||
-- Set .key for vanilla undiscovered, locked objects
|
||||
for k, v in pairs(G) do
|
||||
if type(k) == 'string' and (k:sub(-12, -1) == 'undiscovered' or k:sub(-6, -1) == 'locked') then
|
||||
v.key = k
|
||||
end
|
||||
end
|
||||
SMODS.injectObjects(SMODS.GameObject)
|
||||
if SMODS.dump_loc then
|
||||
boot_print_stage('Dumping Localization')
|
||||
SMODS.create_loc_dump()
|
||||
end
|
||||
boot_print_stage('Initializing Localization')
|
||||
init_localization()
|
||||
SMODS.SAVE_UNLOCKS()
|
||||
table.sort(G.P_CENTER_POOLS["Back"], function (a, b) return (a.order - (a.unlocked and 100 or 0)) < (b.order - (b.unlocked and 100 or 0)) end)
|
||||
for _, t in ipairs{
|
||||
G.P_CENTERS,
|
||||
G.P_BLINDS,
|
||||
G.P_TAGS,
|
||||
G.P_SEALS,
|
||||
} do
|
||||
for k, v in pairs(t) do
|
||||
assert(v._discovered_unlocked_overwritten)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function initializeModUIFunctions()
|
||||
for id, modInfo in pairs(SMODS.mod_list) do
|
||||
G.FUNCS["openModUI_" .. modInfo.id] = function(e)
|
||||
G.ACTIVE_MOD_UI = modInfo
|
||||
G.FUNCS.overlay_menu({
|
||||
definition = create_UIBox_mods(e)
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function checkForLoadFailure()
|
||||
SMODS.mod_button_alert = false
|
||||
for k,v in pairs(SMODS.Mods) do
|
||||
if v and not v.can_load and not v.disabled then
|
||||
SMODS.mod_button_alert = true
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function initSteamodded()
|
||||
initGlobals()
|
||||
boot_print_stage("Loading APIs")
|
||||
loadAPIs()
|
||||
loadMods(SMODS.MODS_DIR)
|
||||
checkForLoadFailure()
|
||||
initializeModUIFunctions()
|
||||
boot_print_stage("Injecting Items")
|
||||
SMODS.injectItems()
|
||||
SMODS.booted = true
|
||||
end
|
||||
|
||||
-- re-inject on reload
|
||||
local init_item_prototypes_ref = Game.init_item_prototypes
|
||||
function Game:init_item_prototypes()
|
||||
init_item_prototypes_ref(self)
|
||||
convert_save_data()
|
||||
if SMODS.booted then
|
||||
SMODS.injectItems()
|
||||
end
|
||||
end
|
||||
|
||||
SMODS.booted = false
|
||||
function boot_print_stage(stage)
|
||||
if not SMODS.booted then
|
||||
boot_timer(nil, "STEAMODDED - " .. stage, 0.95)
|
||||
end
|
||||
end
|
||||
|
||||
function boot_timer(_label, _next, progress)
|
||||
progress = progress or 0
|
||||
G.LOADING = G.LOADING or {
|
||||
font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20),
|
||||
love.graphics.dis
|
||||
}
|
||||
local realw, realh = love.window.getMode()
|
||||
love.graphics.setCanvas()
|
||||
love.graphics.push()
|
||||
love.graphics.setShader()
|
||||
love.graphics.clear(0, 0, 0, 1)
|
||||
love.graphics.setColor(0.6, 0.8, 0.9, 1)
|
||||
if progress > 0 then love.graphics.rectangle('fill', realw / 2 - 150, realh / 2 - 15, progress * 300, 30, 5) end
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.setLineWidth(3)
|
||||
love.graphics.rectangle('line', realw / 2 - 150, realh / 2 - 15, 300, 30, 5)
|
||||
love.graphics.print("LOADING: " .. _next, realw / 2 - 150, realh / 2 + 40)
|
||||
love.graphics.pop()
|
||||
love.graphics.present()
|
||||
|
||||
G.ARGS.bt = G.ARGS.bt or love.timer.getTime()
|
||||
G.ARGS.bt = love.timer.getTime()
|
||||
end
|
||||
|
||||
function SMODS.load_file(path, id)
|
||||
if not path or path == "" then
|
||||
error("No path was provided to load.")
|
||||
end
|
||||
local mod
|
||||
if not id then
|
||||
if not SMODS.current_mod then
|
||||
error("No ID was provided! Usage without an ID is only available when file is first loaded.")
|
||||
end
|
||||
mod = SMODS.current_mod
|
||||
else
|
||||
mod = SMODS.Mods[id]
|
||||
end
|
||||
if not mod then
|
||||
error("Mod not found. Ensure you are passing the correct ID.")
|
||||
end
|
||||
local file_path = mod.path .. path
|
||||
local file_content, err = NFS.read(file_path)
|
||||
if not file_content then return nil, "Error reading file '" .. path .. "' for mod with ID '" .. mod.id .. "': " .. err end
|
||||
local chunk, err = load(file_content, "=[SMODS " .. mod.id .. ' "' .. path .. '"]')
|
||||
if not chunk then return nil, "Error processing file '" .. path .. "' for mod with ID '" .. mod.id .. "': " .. err end
|
||||
return chunk
|
||||
end
|
||||
|
||||
----------------------------------------------
|
||||
------------MOD LOADER END--------------------
|
|
@ -1,58 +0,0 @@
|
|||
--- STEAMODDED CORE
|
||||
--- MODULE LOGGING
|
||||
|
||||
function initializeSocketConnection()
|
||||
local socket = require("socket")
|
||||
client = socket.connect("localhost", 53153)
|
||||
if not client then
|
||||
print("Failed to connect to the debug server")
|
||||
end
|
||||
end
|
||||
|
||||
-- message, logger in this order to preserve backward compatibility
|
||||
function sendTraceMessage(message, logger)
|
||||
sendMessageToConsole("TRACE", logger, message)
|
||||
end
|
||||
|
||||
function sendDebugMessage(message, logger)
|
||||
sendMessageToConsole("DEBUG", logger, message)
|
||||
end
|
||||
|
||||
function sendInfoMessage(message, logger)
|
||||
-- space in info string to align the logs in console
|
||||
sendMessageToConsole("INFO ", logger, message)
|
||||
end
|
||||
|
||||
function sendWarnMessage(message, logger)
|
||||
-- space in warn string to align the logs in console
|
||||
sendMessageToConsole("WARN ", logger, message)
|
||||
end
|
||||
|
||||
function sendErrorMessage(message, logger)
|
||||
sendMessageToConsole("ERROR", logger, message)
|
||||
end
|
||||
|
||||
function sendFatalMessage(message, logger)
|
||||
sendMessageToConsole("FATAL", logger, message)
|
||||
end
|
||||
|
||||
function sendMessageToConsole(level, logger, message)
|
||||
level = level or "DEBUG"
|
||||
logger = logger or "DefaultLogger"
|
||||
message = message or "Default log message"
|
||||
date = os.date('%Y-%m-%d %H:%M:%S')
|
||||
print(date .. " :: " .. level .. " :: " .. logger .. " :: " .. message)
|
||||
if client then
|
||||
-- naive way to separate the logs if the console receive multiple logs at the same time
|
||||
client:send(date .. " :: " .. level .. " :: " .. logger .. " :: " .. message .. "ENDOFLOG")
|
||||
end
|
||||
end
|
||||
|
||||
initializeSocketConnection()
|
||||
|
||||
-- Use the function to send messages
|
||||
sendDebugMessage("Steamodded Debug Socket started !", "DebugConsole")
|
||||
|
||||
|
||||
-----------------------------------------------
|
||||
---------------MOD LOGGING END-----------------
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,649 +0,0 @@
|
|||
import re
|
||||
import socket
|
||||
import string
|
||||
import threading
|
||||
import tkinter as tk
|
||||
from datetime import datetime
|
||||
from tkinter import filedialog
|
||||
|
||||
log_levels = {
|
||||
"TRACE": 0,
|
||||
"DEBUG": 1,
|
||||
"INFO ": 2,
|
||||
"WARN ": 3,
|
||||
"ERROR": 4,
|
||||
"FATAL": 5
|
||||
}
|
||||
|
||||
|
||||
# might or might not be a copy paste from https://stackoverflow.com/a/16375233
|
||||
class TextLineNumbers(tk.Canvas):
|
||||
def __init__(self, *args, **kwargs):
|
||||
tk.Canvas.__init__(self, *args, **kwargs, highlightthickness=0)
|
||||
self.textwidget = None
|
||||
|
||||
def attach(self, text_widget):
|
||||
self.textwidget = text_widget
|
||||
|
||||
def redraw(self, *args):
|
||||
'''redraw line numbers'''
|
||||
self.delete("all")
|
||||
|
||||
i = self.textwidget.index("@0,0")
|
||||
while True:
|
||||
dline = self.textwidget.dlineinfo(i)
|
||||
if dline is None:
|
||||
break
|
||||
y = dline[1]
|
||||
linenum = str(i).split(".")[0]
|
||||
self.create_text(2, y, anchor="nw", text=linenum, fill="#606366")
|
||||
i = self.textwidget.index("%s+1line" % i)
|
||||
|
||||
|
||||
class CustomText(tk.Text):
|
||||
def __init__(self, *args, **kwargs):
|
||||
tk.Text.__init__(self, *args, **kwargs)
|
||||
|
||||
# create a proxy for the underlying widget
|
||||
self._orig = self._w + "_orig"
|
||||
self.tk.call("rename", self._w, self._orig)
|
||||
self.tk.createcommand(self._w, self._proxy)
|
||||
|
||||
def _proxy(self, *args):
|
||||
# let the actual widget perform the requested action
|
||||
cmd = (self._orig,) + args
|
||||
result = self.tk.call(cmd)
|
||||
|
||||
# generate an event if something was added or deleted,
|
||||
# or the cursor position changed
|
||||
if (args[0] in ("insert", "replace", "delete") or
|
||||
args[0:3] == ("mark", "set", "insert") or
|
||||
args[0:2] == ("xview", "moveto") or
|
||||
args[0:2] == ("xview", "scroll") or
|
||||
args[0:2] == ("yview", "moveto") or
|
||||
args[0:2] == ("yview", "scroll")
|
||||
):
|
||||
self.event_generate("<<Change>>", when="tail")
|
||||
|
||||
# return what the actual widget returned
|
||||
return result
|
||||
|
||||
|
||||
class Log:
|
||||
def __init__(self, log: str):
|
||||
self.parse_error = False
|
||||
log_parts = log.split(" :: ")
|
||||
if len(log_parts) == 4:
|
||||
self.timestamp_str = log_parts[0]
|
||||
self.log_level = log_parts[1]
|
||||
self.logger = log_parts[2]
|
||||
self.log_str = log_parts[3]
|
||||
elif len(log_parts) == 3:
|
||||
self.timestamp_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
|
||||
self.logger = log_parts[0]
|
||||
self.log_str = log_parts[1]
|
||||
self.log_str = log_parts[2]
|
||||
else:
|
||||
self.parse_error = True
|
||||
self.timestamp_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
|
||||
self.log_level = "DEBUG"
|
||||
self.logger = "DefaultLogger"
|
||||
self.log_str = log
|
||||
|
||||
def __str__(self):
|
||||
if not self.parse_error:
|
||||
return f"{self.timestamp_str} :: {self.log_level} :: {self.logger} :: {self.log_str}\n"
|
||||
return f"{self.timestamp_str} :: {self.log_str}\n"
|
||||
|
||||
|
||||
class PlaceholderEntry(tk.Entry):
|
||||
def __init__(self, *args, placeholder="", **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.placeholder = placeholder
|
||||
self.user_has_interacted = False
|
||||
self.insert(0, self.placeholder)
|
||||
self.word_pattern = re.compile(r'[\s\W]|$')
|
||||
self.config(fg='grey')
|
||||
self.bind('<FocusOut>', self.on_focus_out)
|
||||
self.bind('<FocusIn>', self.on_focus_in)
|
||||
self.bind('<Control-BackSpace>', self.handle_ctrl_backspace)
|
||||
self.bind('<Control-Delete>', self.handle_ctrl_delete)
|
||||
self.bind('<Key>', self.on_key_press) # Bind key press event
|
||||
|
||||
def on_focus_in(self, event):
|
||||
if not self.user_has_interacted and self.get() == self.placeholder:
|
||||
self.delete(0, 'end')
|
||||
self.config(fg='black')
|
||||
|
||||
def on_focus_out(self, event):
|
||||
if not self.get():
|
||||
self.insert(0, self.placeholder)
|
||||
self.config(fg='grey')
|
||||
self.user_has_interacted = False # Reset flag if entry is empty
|
||||
else:
|
||||
self.user_has_interacted = True
|
||||
|
||||
def on_key_press(self, event):
|
||||
self.user_has_interacted = True # User has interacted when any key is pressed
|
||||
|
||||
def reset_interaction_flag(self):
|
||||
self.user_has_interacted = False
|
||||
|
||||
def handle_ctrl_backspace(self, event: tk.Event):
|
||||
# Get the current content of the entry and the cursor position
|
||||
content = self.get()
|
||||
cursor_pos = self.index(tk.INSERT)
|
||||
|
||||
# If the last character before the cursor is a space or punctuation, delete it
|
||||
if cursor_pos > 0 and (content[cursor_pos - 1] == ' ' or content[cursor_pos - 1] in string.punctuation):
|
||||
self.delete(cursor_pos - 1, tk.INSERT)
|
||||
return "break" # Prevent default behavior
|
||||
|
||||
# Find the start of the word to the left of the cursor
|
||||
pre_cursor = content[:cursor_pos]
|
||||
match = self.word_pattern.search(pre_cursor[::-1]) # [\s\W]|$ matches spaces, punctuation, or end of string
|
||||
word_start = cursor_pos - match.start() if match else 0
|
||||
|
||||
# Delete the word
|
||||
self.delete(word_start, cursor_pos)
|
||||
return "break" # Prevent default behavior
|
||||
|
||||
def handle_ctrl_delete(self, event: tk.Event):
|
||||
# Get the current content of the entry and the cursor position
|
||||
content = self.get()
|
||||
cursor_pos = self.index(tk.INSERT)
|
||||
|
||||
# If the first character after the cursor is a space or punctuation, delete it
|
||||
if len(content) > cursor_pos and (content[cursor_pos] == ' ' or content[cursor_pos] in string.punctuation):
|
||||
self.delete(cursor_pos, cursor_pos + 1)
|
||||
return "break" # Prevent default behavior
|
||||
|
||||
# Find the end of the word to the right of the cursor
|
||||
post_cursor = content[cursor_pos:]
|
||||
match = self.word_pattern.search(post_cursor) # [\s\W]|$ matches spaces, punctuation, or end of string
|
||||
word_end = match.start() if match else len(post_cursor)
|
||||
|
||||
# Delete the word
|
||||
self.delete(cursor_pos, cursor_pos + word_end)
|
||||
return "break" # Prevent default behavior
|
||||
|
||||
|
||||
class OptionsFrame(tk.Frame):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.global_search_frame = GlobalSearchFrame(self, parent)
|
||||
self.specific_search_frame = SpecificSearchFrame(self, parent)
|
||||
self.create_widgets()
|
||||
|
||||
def inject_console(self, console):
|
||||
self.global_search_frame.inject_console(console)
|
||||
self.specific_search_frame.inject_console(console)
|
||||
|
||||
def create_widgets(self):
|
||||
self.global_search_frame.pack(side=tk.TOP, fill='x', expand=True)
|
||||
self.specific_search_frame.pack(side=tk.BOTTOM, fill='x', expand=True)
|
||||
|
||||
|
||||
class GlobalSearchFrame(tk.Frame):
|
||||
def __init__(self, parent, root):
|
||||
super().__init__(parent)
|
||||
|
||||
self.after_id = None
|
||||
self.root = root
|
||||
self.console = None
|
||||
|
||||
# Global search entry
|
||||
self.search_entry_placeholder = "Search"
|
||||
self.search_entry_var = tk.StringVar()
|
||||
self.search_entry = PlaceholderEntry(
|
||||
self,
|
||||
placeholder=self.search_entry_placeholder,
|
||||
textvariable=self.search_entry_var
|
||||
)
|
||||
self.search_entry_var.trace("w", self.on_entry_changed)
|
||||
self.search_entry.bind('<Escape>', lambda event: self.console.text_widget.focus())
|
||||
self.search_entry.config(fg='grey')
|
||||
|
||||
self.search_modes = []
|
||||
self.search_mode_var = tk.StringVar(value='normal')
|
||||
self.search_mode_var.trace("w", self.apply_search_mode)
|
||||
for mode, text in [('normal', 'normal'), ('match_case', 'match case'), ('regex', 'regex')]:
|
||||
self.search_modes.append(tk.Radiobutton(self, text=text, variable=self.search_mode_var, value=mode))
|
||||
|
||||
self.create_widgets()
|
||||
|
||||
def apply_search_mode(self, *args):
|
||||
self.console.set_filter(global_search_mode=self.search_mode_var.get())
|
||||
|
||||
def inject_console(self, console):
|
||||
self.console = console
|
||||
|
||||
def create_widgets(self):
|
||||
self.search_entry.pack(side=tk.LEFT, fill='x', expand=True, padx=(5, 0))
|
||||
for mode in self.search_modes:
|
||||
mode.pack(side=tk.LEFT, padx=(5, 0))
|
||||
self.search_entry.bind('<Return>', lambda event: self.console.next_occurrence())
|
||||
self.search_entry.bind('<Shift-Return>', lambda event: self.console.previous_occurrence())
|
||||
|
||||
def on_entry_changed(self, *args):
|
||||
if self.after_id:
|
||||
self.root.after_cancel(self.after_id)
|
||||
self.after_id = self.root.after(300, self.apply_search_entry_var)
|
||||
|
||||
def apply_search_entry_var(self):
|
||||
self.console.set_filter(global_search_str=self.search_entry_var.get())
|
||||
self.after_id = None
|
||||
|
||||
|
||||
class Console(tk.Frame):
|
||||
def __init__(self, parent, option_frame: OptionsFrame):
|
||||
super().__init__(parent)
|
||||
self.global_search_mode = "normal"
|
||||
self.all_logs = []
|
||||
self.shown_logs = []
|
||||
self.option_frame = option_frame
|
||||
self.text_widget = CustomText(self)
|
||||
self.linenumbers = TextLineNumbers(self, width=30)
|
||||
self.linenumbers.attach(self.text_widget)
|
||||
self.text_widget.bind("<<Change>>", self._on_change)
|
||||
self.text_widget.bind("<Configure>", self._on_change)
|
||||
self.scrollbar = tk.Scrollbar(self, command=self.text_widget.yview)
|
||||
self.global_search_str = ""
|
||||
self.logger_name = ""
|
||||
self.log_level = "TRACE"
|
||||
self.and_above = True
|
||||
self.create_widgets()
|
||||
|
||||
def _on_change(self, event):
|
||||
self.linenumbers.redraw()
|
||||
|
||||
def create_widgets(self):
|
||||
self.scrollbar.pack(side=tk.RIGHT, fill='y')
|
||||
self.linenumbers.pack(side=tk.LEFT, fill="y")
|
||||
self.text_widget.pack(side=tk.LEFT, expand=True, fill='both')
|
||||
self.text_widget.config(yscrollcommand=self.scrollbar.set)
|
||||
self.text_widget.config(state=tk.DISABLED)
|
||||
|
||||
def set_filter(
|
||||
self,
|
||||
global_search_str: str = None,
|
||||
global_search_mode: str = None,
|
||||
logger_name: str = None,
|
||||
log_level: str = None,
|
||||
and_above: bool = None
|
||||
):
|
||||
if global_search_str is not None and self.option_frame.global_search_frame.search_entry.user_has_interacted:
|
||||
self.global_search_str = global_search_str
|
||||
|
||||
if logger_name is not None and self.option_frame.specific_search_frame.logger_entry.user_has_interacted:
|
||||
self.logger_name = logger_name
|
||||
|
||||
if global_search_mode is not None:
|
||||
self.global_search_mode = global_search_mode
|
||||
|
||||
if log_level is not None:
|
||||
self.log_level = log_level
|
||||
|
||||
if and_above is not None:
|
||||
self.and_above = and_above
|
||||
|
||||
self.apply_filters()
|
||||
|
||||
def append_log(self, log: str):
|
||||
log_obj = Log(log)
|
||||
self.all_logs.append(log_obj)
|
||||
if self.filter_log(log_obj):
|
||||
self.shown_logs.append(log_obj)
|
||||
# Check if the user is at the end before appending
|
||||
at_end = self.text_widget.yview()[1] == 1.0
|
||||
self.text_widget.config(state=tk.NORMAL)
|
||||
self.text_widget.insert(tk.END, str(log_obj))
|
||||
self.text_widget.config(state=tk.DISABLED)
|
||||
if at_end:
|
||||
self.text_widget.see(tk.END)
|
||||
if self.global_search_str:
|
||||
self.search_text()
|
||||
|
||||
def clear_logs(self):
|
||||
self.text_widget.config(state=tk.NORMAL)
|
||||
self.text_widget.delete('1.0', tk.END)
|
||||
self.text_widget.config(state=tk.DISABLED)
|
||||
self.shown_logs.clear()
|
||||
self.all_logs.clear()
|
||||
self.apply_filters()
|
||||
|
||||
def apply_filters(self):
|
||||
# Re-filter all logs and update the text widget only if necessary
|
||||
filtered_logs = [log for log in self.all_logs if self.filter_log(log)]
|
||||
self.shown_logs = filtered_logs
|
||||
self.update_text_widget()
|
||||
|
||||
def filter_log(self, log):
|
||||
# print(self.global_search_str, self.global_search_mode, self.logger_name, self.log_level, self.and_above)
|
||||
if self.and_above:
|
||||
flag = log_levels[log.log_level] >= log_levels[self.log_level]
|
||||
else:
|
||||
flag = log.log_level == self.log_level
|
||||
|
||||
if self.logger_name:
|
||||
flag = flag and self.logger_name in log.logger
|
||||
|
||||
return flag
|
||||
|
||||
def update_text_widget(self):
|
||||
# Preserve the current view position unless at the end
|
||||
at_end = self.text_widget.yview()[1] == 1.0
|
||||
self.text_widget.config(state=tk.NORMAL)
|
||||
self.text_widget.delete('1.0', tk.END)
|
||||
self.text_widget.config(state=tk.DISABLED)
|
||||
for log in self.shown_logs:
|
||||
self.text_widget.config(state=tk.NORMAL)
|
||||
self.text_widget.insert(tk.END, str(log))
|
||||
self.text_widget.config(state=tk.DISABLED)
|
||||
if at_end:
|
||||
self.text_widget.see(tk.END)
|
||||
|
||||
if self.global_search_str:
|
||||
self.search_text()
|
||||
|
||||
def search_text(self):
|
||||
self.text_widget.tag_remove('found', '1.0', tk.END)
|
||||
search_query = self.global_search_str.strip()
|
||||
if not search_query:
|
||||
return
|
||||
|
||||
if self.global_search_mode == 'match_case':
|
||||
pattern = re.escape(search_query)
|
||||
elif self.global_search_mode == 'regex':
|
||||
# Directly use the user input for regex, but be cautious of Tkinter's limited regex support
|
||||
pattern = search_query
|
||||
else: # normal mode, make it case-insensitive
|
||||
pattern = '(?i)' + re.escape(search_query) # Add (?i) for case-insensitive search in Tkinter
|
||||
|
||||
start = '1.0'
|
||||
while True:
|
||||
match_start = self.text_widget.search(pattern, start, tk.END, regexp=True)
|
||||
if not match_start:
|
||||
break
|
||||
match_end = f"{match_start}+{len(search_query)}c"
|
||||
self.text_widget.tag_add('found', match_start, match_end)
|
||||
start = match_end
|
||||
|
||||
self.text_widget.tag_config('found', background='yellow')
|
||||
at_end = self.text_widget.yview()[1] == 1.0
|
||||
if at_end:
|
||||
first_occurrence = self.text_widget.tag_ranges('found')
|
||||
if first_occurrence:
|
||||
self.text_widget.see(first_occurrence[0])
|
||||
self.next_occurrence()
|
||||
|
||||
def prepare_occurrence_navigation(self):
|
||||
current_tags = self.text_widget.tag_ranges('found')
|
||||
if not current_tags:
|
||||
return None, None
|
||||
|
||||
# Ensure the 'current_found' tag exists with a blue background.
|
||||
self.text_widget.tag_config('current_found', background='#ADD8E6')
|
||||
|
||||
# Get the current position of the cursor in the text widget.
|
||||
cursor_index = self.text_widget.index(tk.INSERT)
|
||||
|
||||
# Remove the 'current_found' tag from the entire text widget.
|
||||
self.text_widget.tag_remove('current_found', '1.0', tk.END)
|
||||
|
||||
# Convert the current cursor index to a comparable value.
|
||||
cursor_line, cursor_char = map(int, cursor_index.split('.'))
|
||||
|
||||
return current_tags, (cursor_line, cursor_char)
|
||||
|
||||
def next_occurrence(self):
|
||||
current_tags, cursor_position = self.prepare_occurrence_navigation()
|
||||
if not current_tags or not cursor_position:
|
||||
return
|
||||
|
||||
cursor_line, cursor_char = cursor_position
|
||||
|
||||
for i in range(0, len(current_tags), 2):
|
||||
tag_start = current_tags[i]
|
||||
tag_end = current_tags[i + 1]
|
||||
|
||||
# Convert tag start index to comparable values.
|
||||
tag_start_line, tag_start_char = map(int, str(tag_start).split('.'))
|
||||
|
||||
# Check if the tag start is greater than the cursor position.
|
||||
if tag_start_line > cursor_line or (tag_start_line == cursor_line and tag_start_char > cursor_char):
|
||||
self.text_widget.mark_set(tk.INSERT, tag_start)
|
||||
self.text_widget.see(tag_start)
|
||||
|
||||
# Apply the 'current_found' tag to the current occurrence.
|
||||
self.text_widget.tag_add('current_found', tag_start, tag_end)
|
||||
break
|
||||
else:
|
||||
# Wrap to the first tag if no next tag is found.
|
||||
self.text_widget.mark_set(tk.INSERT, str(current_tags[0]))
|
||||
self.text_widget.see(str(current_tags[0]))
|
||||
self.text_widget.tag_add('current_found', current_tags[0], current_tags[1])
|
||||
|
||||
def previous_occurrence(self):
|
||||
current_tags, cursor_position = self.prepare_occurrence_navigation()
|
||||
if not current_tags or not cursor_position:
|
||||
return
|
||||
|
||||
cursor_line, cursor_char = cursor_position
|
||||
|
||||
for i in range(len(current_tags) - 2, -1, -2):
|
||||
tag_start = current_tags[i]
|
||||
tag_end = current_tags[i + 1]
|
||||
|
||||
# Convert tag start index to comparable values.
|
||||
tag_start_line, tag_start_char = map(int, str(tag_start).split('.'))
|
||||
|
||||
# Check if the tag start is less than the cursor position.
|
||||
if tag_start_line < cursor_line or (tag_start_line == cursor_line and tag_start_char < cursor_char):
|
||||
self.text_widget.mark_set(tk.INSERT, tag_start)
|
||||
self.text_widget.see(tag_start)
|
||||
|
||||
# Apply the 'current_found' tag to the current occurrence.
|
||||
self.text_widget.tag_add('current_found', tag_start, tag_end)
|
||||
break
|
||||
else:
|
||||
# Wrap to the last tag if no previous tag is found.
|
||||
self.text_widget.mark_set(tk.INSERT, str(current_tags[-2]))
|
||||
self.text_widget.see(str(current_tags[-2]))
|
||||
self.text_widget.tag_add('current_found', current_tags[-2], current_tags[-1])
|
||||
|
||||
|
||||
class SpecificSearchFrame(tk.Frame):
|
||||
def __init__(self, parent, root):
|
||||
super().__init__(parent)
|
||||
self.root = root
|
||||
self.after_id = None
|
||||
self.console = None
|
||||
|
||||
# Logger name entry
|
||||
self.logger_entry_placeholder = "Logger Name"
|
||||
self.logger_entry_var = tk.StringVar()
|
||||
self.logger_entry = PlaceholderEntry(
|
||||
self,
|
||||
placeholder=self.logger_entry_placeholder,
|
||||
textvariable=self.logger_entry_var
|
||||
)
|
||||
self.logger_entry_var.trace("w", self.on_entry_changed)
|
||||
self.logger_entry.bind('<Escape>', lambda event: self.console.text_widget.focus())
|
||||
self.logger_entry.config(fg='grey')
|
||||
|
||||
# Log level dropdown
|
||||
self.log_level_dropdown_var = tk.StringVar()
|
||||
self.log_level_dropdown_var.set("TRACE")
|
||||
self.log_level_dropdown = tk.OptionMenu(
|
||||
self,
|
||||
self.log_level_dropdown_var,
|
||||
*log_levels.keys()
|
||||
)
|
||||
self.log_level_dropdown_var.trace(
|
||||
"w",
|
||||
lambda *args: self.console.set_filter(log_level=self.log_level_dropdown_var.get())
|
||||
)
|
||||
|
||||
# And above checkbox
|
||||
self.and_above_var = tk.BooleanVar()
|
||||
self.and_above_var.set(True)
|
||||
self.and_above_checkbox = tk.Checkbutton(
|
||||
self,
|
||||
text="And above",
|
||||
variable=self.and_above_var,
|
||||
onvalue=True,
|
||||
offvalue=False,
|
||||
command=lambda: self.console.set_filter(and_above=self.and_above_var.get())
|
||||
)
|
||||
|
||||
self.clear_log_button: tk.Button | None = None
|
||||
|
||||
self.create_widgets()
|
||||
|
||||
def inject_console(self, console):
|
||||
self.console = console
|
||||
self.clear_log_button = tk.Button(
|
||||
self,
|
||||
text="Clear Logs",
|
||||
command=self.console.clear_logs
|
||||
)
|
||||
self.clear_log_button.pack(side=tk.RIGHT, padx=(5, 0), fill='x', expand=True)
|
||||
|
||||
def create_widgets(self):
|
||||
self.logger_entry.pack(side=tk.LEFT, fill='x', expand=True, padx=(5, 0))
|
||||
self.log_level_dropdown.pack(side=tk.LEFT, padx=(5, 0), fill='x', expand=True)
|
||||
self.and_above_checkbox.pack(side=tk.LEFT, padx=(5, 0), fill='x', expand=True)
|
||||
|
||||
def on_entry_changed(self, *args):
|
||||
if self.after_id:
|
||||
self.root.after_cancel(self.after_id)
|
||||
self.after_id = self.root.after(250, self.apply_logger_entry_var)
|
||||
|
||||
def apply_logger_entry_var(self):
|
||||
if self.logger_entry.user_has_interacted:
|
||||
self.console.set_filter(logger_name=self.logger_entry_var.get())
|
||||
self.after_id = None
|
||||
|
||||
|
||||
class ExportMenuBar(tk.Menu):
|
||||
def __init__(self, parent, console: Console, *args, **kwargs):
|
||||
super().__init__(parent, *args, **kwargs)
|
||||
self.parent = parent
|
||||
self.initialize_menu()
|
||||
self.console = console
|
||||
|
||||
def initialize_menu(self):
|
||||
# Create a File menu
|
||||
file_menu = tk.Menu(self, tearoff=0)
|
||||
file_menu.add_command(label="All logs", command=self.export_all_logs)
|
||||
file_menu.add_separator()
|
||||
file_menu.add_command(label="Filtered logs", command=self.export_filtered_logs)
|
||||
|
||||
# Adding the "File" menu to the menubar
|
||||
self.add_cascade(label="Export", menu=file_menu)
|
||||
|
||||
def export_all_logs(self):
|
||||
file_path = filedialog.asksaveasfilename(
|
||||
defaultextension=".log",
|
||||
filetypes=[("Log files", "*.log"), ("All files", "*.*")],
|
||||
initialfile=f"Balatro-AllLogs-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log"
|
||||
)
|
||||
if file_path:
|
||||
with open(file_path, "w") as f:
|
||||
for log in self.console.all_logs:
|
||||
f.write(str(log))
|
||||
|
||||
def export_filtered_logs(self):
|
||||
file_path = filedialog.asksaveasfilename(
|
||||
defaultextension=".log",
|
||||
filetypes=[("Log files", "*.log"), ("All files", "*.*")],
|
||||
initialfile=f"Balatro-FilteredLogs-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log"
|
||||
)
|
||||
if file_path:
|
||||
with open(file_path, "w") as f:
|
||||
for log in self.console.shown_logs:
|
||||
f.write(str(log))
|
||||
|
||||
|
||||
class MainWindow(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title("Steamodded Debug Console")
|
||||
self.options_frame = OptionsFrame(self)
|
||||
self.console = Console(self, self.options_frame)
|
||||
self.options_frame.inject_console(self.console)
|
||||
self.menu_bar = ExportMenuBar(self, self.console)
|
||||
self.create_widgets()
|
||||
|
||||
self.bind('<Control-f>', self.focus_search)
|
||||
self.bind('<Control-F>', self.focus_search)
|
||||
|
||||
self.bind('<Control-Shift-s>', lambda event: self.menu_bar.export_filtered_logs())
|
||||
self.bind('<Control-Shift-S>', lambda event: self.menu_bar.export_filtered_logs())
|
||||
|
||||
self.bind('<Control-s>', lambda event: self.menu_bar.export_all_logs())
|
||||
self.bind('<Control-S>', lambda event: self.menu_bar.export_all_logs())
|
||||
|
||||
self.bind('<Control-d>', lambda event: self.console.clear_logs())
|
||||
self.bind('<Control-D>', lambda event: self.console.clear_logs())
|
||||
|
||||
self.bind('<Control-l>', self.focus_logger)
|
||||
self.bind('<Control-L>', self.focus_logger)
|
||||
|
||||
def create_widgets(self):
|
||||
self.console.pack(side=tk.TOP, expand=True, fill='both')
|
||||
self.options_frame.pack(side=tk.BOTTOM, fill='x', expand=False)
|
||||
self.config(menu=self.menu_bar)
|
||||
|
||||
def get_console(self):
|
||||
return self.console
|
||||
|
||||
def focus_search(self, event):
|
||||
self.options_frame.global_search_frame.search_entry.focus()
|
||||
|
||||
def focus_logger(self, event):
|
||||
self.options_frame.specific_search_frame.logger_entry.focus()
|
||||
|
||||
|
||||
def client_handler(client_socket, console: Console):
|
||||
buffer = []
|
||||
while True:
|
||||
data = client_socket.recv(1024)
|
||||
if not data:
|
||||
break
|
||||
|
||||
decoded_data = data.decode()
|
||||
buffer.append(decoded_data) # Append new data to the buffer list
|
||||
|
||||
# Join the buffer and split by "ENDOFLOG"
|
||||
# This handles cases where "ENDOFLOG" is spread across multiple recv calls
|
||||
combined_data = ''.join(buffer)
|
||||
logs = combined_data.split("ENDOFLOG")
|
||||
|
||||
# The last element might be an incomplete log; keep it in the buffer
|
||||
buffer = [logs.pop()] if logs[-1] else []
|
||||
|
||||
# Append each complete log to the console
|
||||
for log in logs:
|
||||
if log:
|
||||
console.append_log(log)
|
||||
|
||||
# Handle any remaining data in the buffer after the connection is closed
|
||||
if ''.join(buffer):
|
||||
console.append_log(''.join(buffer))
|
||||
|
||||
|
||||
def listen_for_clients(console: Console):
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server_socket.bind(('localhost', 53153))
|
||||
server_socket.listen()
|
||||
while True:
|
||||
client, addr = server_socket.accept()
|
||||
threading.Thread(target=client_handler, args=(client, console)).start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = MainWindow()
|
||||
threading.Thread(target=listen_for_clients, daemon=True, args=(root.get_console(),)).start()
|
||||
root.mainloop()
|
|
@ -1 +0,0 @@
|
|||
return "1.0.0~ALPHA-1410b-STEAMODDED"
|
Loading…
Reference in a new issue