Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 20 additions & 21 deletions lovely/center.toml
Original file line number Diff line number Diff line change
Expand Up @@ -383,30 +383,29 @@ for _, v in ipairs(rates) do'''
[[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 = '''
local soul_total_rate = 0
local non_soul_rate = 1
local modded_souls = {}
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 SMODS.showman(v.key) and not v.can_repeat_soul) and SMODS.add_to_pool(v) then
soul_total_rate = soul_total_rate + v.soul_rate
non_soul_rate = non_soul_rate * (1 - v.soul_rate)
non_soul_rate = math.max(non_soul_rate, 0)
table.insert(modded_souls, v)
pattern = '''
if not forced_key and soulable and (not G.GAME.banned_keys['c_soul']) then
if (_type == 'Tarot' or _type == 'Spectral' or _type == 'Tarot_Planet') and
not (G.GAME.used_jokers['c_soul'] and not next(find_joker("Showman"))) then
if pseudorandom('soul_'.._type..G.GAME.round_resets.ante) > 0.997 then
forced_key = 'c_soul'
end
end
local roll = pseudorandom('soul_smods_'.._type..G.GAME.round_resets.ante)
local threshold = 1
for _, v in ipairs(modded_souls) do
threshold = threshold - v.soul_rate/soul_total_rate * (1-non_soul_rate)
if roll > threshold then
forced_key = v.key
break
if (_type == 'Planet' or _type == 'Spectral') and
not (G.GAME.used_jokers['c_black_hole'] and not next(find_joker("Showman"))) then
if pseudorandom('soul_'.._type..G.GAME.round_resets.ante) > 0.997 then
forced_key = 'c_black_hole'
end
end'''
end
end
'''
match_indent = true
position = 'at'
payload = '''
if not forced_key and soulable then
forced_key = SMODS.poll_soul{set = _type}
end
'''

[[patches]]
[patches.pattern]
Expand Down
18 changes: 15 additions & 3 deletions lsp_def/classes/consumable.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
---@class SMODS.Consumable: SMODS.Center
---@field super? SMODS.Center|table Parent class.
---@field hidden? boolean Sets if this consumable is considered "legendary" (e.x. behaves like "The Soul").
---@field soul_set? string Key to the ConsumableType set this consumable can replace. Requires `hidden` to be true.
---@field soul_rate? number Chance this card replaces a consumable. Requires `hidden` to be true.
---@field soul_set? string|table Key(s) to the ConsumableType set(s) this consumable can replace. Requires `hidden` to be true.
---@field soul_rate? number|fun(set: string):number Chance this card replaces a consumable. Requires `hidden` to be true.
---@field type? SMODS.ConsumableType|table ConsumableType this center belongs to.
---@field legendaries? (SMODS.Consumable|table)[] All injected "legendary" consumables.
---@field __call? fun(self: SMODS.Consumable|table, o: SMODS.Consumable|table): nil|table|SMODS.Consumable
Expand All @@ -30,4 +30,16 @@ SMODS.Consumable = setmetatable({}, {
__call = function(self)
return self
end
})
})

---@param args table|{set: string, key?: string, mod?: number, guaranteed?: boolean, allow_duplicates?: boolean, ignore_vanilla?: boolean}
---@return string?
--- Polls hidden consumables.
function SMODS.poll_soul(args) end

---@param consumable SMODS.Consumable|table|string
---@param set string
---@return number?
---@return string
--- Gets soul rate of a consumable.
function SMODS.get_soul_rate(consumable, set) end
67 changes: 67 additions & 0 deletions src/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,73 @@ function SMODS.poll_edition(args)
return poll_edition(args.key or 'editiongeneric', args.mod, args.no_negative, args.guaranteed, args.options)
end

function SMODS.poll_soul(args)
assert(args and args.set, "SMODS.poll_soul called without a set")

local set = args.set
local forced_key
local soul_total_rate = 0
local non_soul_rate = 1
local modded_souls = {}

for _, v in ipairs(SMODS.Consumable.legendaries) do
if not G.GAME.banned_keys[v.key] then
local can_repeat = SMODS.showman(v.key) or v.can_repeat_soul or args.allow_duplicates
local is_soul_set = set == v.type.key
if not is_soul_set and v.soul_set then
local consumable_soul_sets = type(v.soul_set) == "table" and v.soul_set or { v.soul_set }
for _, soul_set in ipairs(consumable_soul_sets) do
is_soul_set = set == soul_set
if is_soul_set then break end
end
end
if is_soul_set and not (G.GAME.used_jokers[v.key] and not can_repeat) and SMODS.add_to_pool(v) then
local soul_rate = SMODS.get_soul_rate(v, set) * (args.mod or 1)
v.temp_soul_rate = soul_rate -- to ensure it doesn't change and so it isn't calculated twice
soul_total_rate = soul_total_rate + soul_rate
non_soul_rate = non_soul_rate * (1 - soul_rate)
non_soul_rate = math.max(non_soul_rate, 0)
table.insert(modded_souls, v)
end
end
end
local roll = pseudorandom(args.key or ('soul_smods_' .. set .. G.GAME.round_resets.ante))
local threshold = 1
non_soul_rate = args.guaranteed and 0 or non_soul_rate
for _, v in ipairs(modded_souls) do
local soul_rate = v.temp_soul_rate
threshold = threshold - soul_rate / soul_total_rate * (1 - non_soul_rate)
v.temp_soul_rate = nil
if roll > threshold then
forced_key = v.key
break
end
end
if not G.GAME.banned_keys['c_soul'] and not args.ignore_vanilla then
if (set == 'Tarot' or set == 'Spectral' or set == 'Tarot_Planet') and
not (G.GAME.used_jokers['c_soul'] and not SMODS.showman('c_soul')) then
if (not forced_key and args.guaranteed) or pseudorandom(args.key or ('soul_' .. set .. G.GAME.round_resets.ante)) > 0.997 - (0.003 * ((G.GAME.soul_mod or 1) * (args.mod or 1) - 1)) then
forced_key = 'c_soul'
end
end
if (set == 'Planet' or set == 'Spectral') and
not (G.GAME.used_jokers['c_black_hole'] and not SMODS.showman('c_black_hole')) then
if ((not forced_key or forced_key == 'c_soul') and args.guaranteed) or pseudorandom(args.key or ('soul_' .. set .. G.GAME.round_resets.ante)) > 0.997 - (0.003 * ((G.GAME.soul_mod or 1) * (args.mod or 1) - 1)) then
forced_key = 'c_black_hole'
end
end
end
return forced_key
end

function SMODS.get_soul_rate(consumable, set)
local prototype = type(consumable) == "string" and G.P_CENTERS[consumable] or consumable
if not prototype then return nil, "SMODS.get_soul_rate was called with an invalid consumable" end
local soul_rate = type(prototype.soul_rate) == "function" and prototype.soul_rate(set) or
prototype.soul_rate or 0
return soul_rate * (G.GAME.soul_mod or 1)
end

function SMODS.poll_seal(args)
args = args or {}
local key = args.key or 'stdseal'
Expand Down