local lovely = require("lovely") local nativefs = require("nativefs") if not nativefs.getInfo(lovely.mod_dir .. "/Talisman") then error( 'Could not find proper Talisman folder.\nPlease make sure the folder for Talisman is named exactly "Talisman" and not "Talisman-main" or anything else.') end Talisman = {config_file = {disable_anims = true, break_infinity = "omeganum", score_opt_id = 2}} if nativefs.read(lovely.mod_dir.."/Talisman/config.lua") then Talisman.config_file = STR_UNPACK(nativefs.read(lovely.mod_dir.."/Talisman/config.lua")) if Talisman.config_file.break_infinity and type(Talisman.config_file.break_infinity) ~= 'string' then Talisman.config_file.break_infinity = "omeganum" end end if not SMODS or not JSON then local createOptionsRef = create_UIBox_options function create_UIBox_options() contents = createOptionsRef() local m = UIBox_button({ minw = 5, button = "talismanMenu", label = { "Talisman" }, colour = G.C.GOLD }) table.insert(contents.nodes[1].nodes[1].nodes[1].nodes, #contents.nodes[1].nodes[1].nodes[1].nodes + 1, m) return contents end end Talisman.config_tab = function() tal_nodes = {{n=G.UIT.R, config={align = "cm"}, nodes={ {n=G.UIT.O, config={object = DynaText({string = "Select features to enable:", colours = {G.C.WHITE}, shadow = true, scale = 0.4})}}, }},create_toggle({label = "Disable Scoring Animations", ref_table = Talisman.config_file, ref_value = "disable_anims", callback = function(_set_toggle) nativefs.write(lovely.mod_dir .. "/Talisman/config.lua", STR_PACK(Talisman.config_file)) end}), create_option_cycle({ label = "Score Limit (requires game restart)", scale = 0.8, w = 6, options = {"Vanilla (e308)", "BigNum (ee308)", "OmegaNum (e10##1000)"}, opt_callback = 'talisman_upd_score_opt', current_option = Talisman.config_file.score_opt_id, })} return { n = G.UIT.ROOT, config = { emboss = 0.05, minh = 6, r = 0.1, minw = 10, align = "cm", padding = 0.2, colour = G.C.BLACK }, nodes = tal_nodes } end G.FUNCS.talismanMenu = function(e) local tabs = create_tabs({ snap_to_nav = true, tabs = { { label = "Talisman", chosen = true, tab_definition_function = Talisman.config_tab }, }}) G.FUNCS.overlay_menu{ definition = create_UIBox_generic_options({ back_func = "options", contents = {tabs} }), config = {offset = {x=0,y=10}} } end G.FUNCS.talisman_upd_score_opt = function(e) Talisman.config_file.score_opt_id = e.to_key local score_opts = {"", "bignumber", "omeganum"} Talisman.config_file.break_infinity = score_opts[e.to_key] nativefs.write(lovely.mod_dir .. "/Talisman/config.lua", STR_PACK(Talisman.config_file)) end if Talisman.config_file.break_infinity then Big, err = nativefs.load(lovely.mod_dir.."/Talisman/big-num/"..Talisman.config_file.break_infinity..".lua") if not err then Big = Big() else Big = nil end Notations = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations.lua")() -- We call this after init_game_object to leave room for mods that add more poker hands Talisman.igo = function(obj) for _, v in pairs(obj.hands) do v.chips = to_big(v.chips) v.mult = to_big(v.mult) v.s_chips = to_big(v.s_chips) 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 local nf = number_format function number_format(num, e_switch_point) if type(num) == 'table' then num = to_big(num) G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 if num < to_big(e_switch_point or G.E_SWITCH_POINT) then return nf(num:to_number(), e_switch_point) else return Notations.Balatro:format(num, 3) end else return nf(num, e_switch_point) end end local mf = math.floor function math.floor(x) 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) if type(x) == 'table' then return l10(math.min(x:to_number(),1e300)) end--x:log10() end return l10(x) end local lg = math.log function math.log(x, y) if not y then y = 2.718281828459045 end if type(x) == 'table' then return lg(math.min(x:to_number(),1e300),y) end --x:log(y) end return lg(x,y) end if SMODS then function SMODS.get_blind_amount(ante) local k = to_big(0.75) local scale = G.GAME.modifiers.scaling local amounts = { to_big(300), to_big(700 + 100*scale), to_big(1400 + 600*scale), to_big(2100 + 2900*scale), to_big(15000 + 5000*scale*math.log(scale)), to_big(12000 + 8000*(scale+1)*(0.4*scale)), to_big(10000 + 25000*(scale+1)*((scale/4)^2)), to_big(50000 * (scale+1)^2 * (scale/7)^2) } if ante < 1 then return to_big(100) end if ante <= 8 then local amount = amounts[ante] if (amount:lt(R.E_MAX_SAFE_INTEGER)) then local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() amount = math.floor(amount / exponent):to_number() * exponent end amount:normalize() return amount end local a, b, c, d = amounts[8], amounts[8]/amounts[7], ante-8, 1 + 0.2*(ante-8) local amount = math.floor(a*(b + (b*k*c)^d)^c) if (amount:lt(R.E_MAX_SAFE_INTEGER)) then local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() amount = math.floor(amount / exponent):to_number() * exponent end amount:normalize() return amount end end -- There's too much to override here so we just fully replace this function -- Note that any ante scaling tweaks will need to manually changed... local gba = get_blind_amount function get_blind_amount(ante) if G.GAME.modifiers.scaling and G.GAME.modifiers.scaling > 3 then return SMODS.get_blind_amount(ante) end if type(to_big(1)) == 'number' then return gba(ante) end local k = to_big(0.75) if not G.GAME.modifiers.scaling or G.GAME.modifiers.scaling == 1 then local amounts = { to_big(300), to_big(800), to_big(2000), to_big(5000), to_big(11000), to_big(20000), to_big(35000), to_big(50000) } if ante < 1 then return to_big(100) end if ante <= 8 then return amounts[ante] end local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) local amount = a*(b+(k*c)^d)^c if (amount:lt(R.E_MAX_SAFE_INTEGER)) then local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() amount = math.floor(amount / exponent):to_number() * exponent end amount:normalize() return amount elseif G.GAME.modifiers.scaling == 2 then local amounts = { to_big(300), to_big(900), to_big(2600), to_big(8000), to_big(20000), to_big(36000), to_big(60000), to_big(100000) --300, 900, 2400, 7000, 18000, 32000, 56000, 90000 } if ante < 1 then return to_big(100) end if ante <= 8 then return amounts[ante] end local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) local amount = a*(b+(k*c)^d)^c if (amount:lt(R.E_MAX_SAFE_INTEGER)) then local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() amount = math.floor(amount / exponent):to_number() * exponent end amount:normalize() return amount elseif G.GAME.modifiers.scaling == 3 then local amounts = { to_big(300), to_big(1000), to_big(3200), to_big(9000), to_big(25000), to_big(60000), to_big(110000), to_big(200000) --300, 1000, 3000, 8000, 22000, 50000, 90000, 180000 } if ante < 1 then return to_big(100) end if ante <= 8 then return amounts[ante] end local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) local amount = a*(b+(k*c)^d)^c if (amount:lt(R.E_MAX_SAFE_INTEGER)) then local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() amount = math.floor(amount / exponent):to_number() * exponent end amount:normalize() return amount end end function check_and_set_high_score(score, amt) if G.GAME.round_scores[score] and to_big(math.floor(amt)) > to_big(G.GAME.round_scores[score].amt) then G.GAME.round_scores[score].amt = to_big(math.floor(amt)) end if G.GAME.seeded then return end --[[if G.PROFILES[G.SETTINGS.profile].high_scores[score] and math.floor(amt) > G.PROFILES[G.SETTINGS.profile].high_scores[score].amt then if G.GAME.round_scores[score] then G.GAME.round_scores[score].high_score = true end G.PROFILES[G.SETTINGS.profile].high_scores[score].amt = math.floor(amt) G:save_settings() 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 scale = to_big(scale) G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 if not number or not is_number(number) then return scale end if not max then max = 10000 end if to_big(number).e and to_big(number).e == 10^1000 then scale = scale*math.floor(math.log(max*10, 10))/7 end if to_big(number) >= to_big(e_switch_point or G.E_SWITCH_POINT) then if (to_big(to_big(number):log10()) <= to_big(999)) then scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(1000000*10, 10)) else scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.max(7,string.len(number_format(number))-1)) end elseif to_big(number) >= to_big(max) then scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(number*10, 10)) end return math.min(3, scale:to_number()) end local tsj = G.FUNCS.text_super_juice function G.FUNCS.text_super_juice(e, _amount) if _amount > 2 then _amount = 2 end return tsj(e, _amount) end local max = math.max --don't return a Big unless we have to - it causes nativefs to break function math.max(x, y) if type(x) == 'table' or type(y) == 'table' then x = to_big(x) y = to_big(y) if (x > y) then return x else return y end else return max(x,y) end end local min = math.min function math.min(x, y) if type(x) == 'table' or type(y) == 'table' then x = to_big(x) y = to_big(y) if (x < y) then return x else return y end else return min(x,y) end end local sqrt = math.sqrt function math.sqrt(x) if type(x) == 'table' then if getmetatable(x) == BigMeta then return x:sqrt() end if getmetatable(x) == OmegaMeta then return x:pow(0.5) end end return sqrt(x) end local old_abs = math.abs function math.abs(x) if type(x) == 'table' then x = to_big(x) if (x < to_big(0)) then return -1 * x else return x end else return old_abs(x) end end end function is_number(x) if type(x) == 'number' then return true end if type(x) == 'table' and ((x.e and x.m) or (x.array and x.sign)) then return true end return false end function to_big(x, y) if Big and Big.m then return Big:new(x,y) elseif Big and Big.array then local result = Big:create(x) result.sign = y or result.sign or x.sign or 1 return result elseif is_number(x) then return x * 10^(y or 0) elseif type(x) == "nil" then return 0 else if ((#x>=2) and ((x[2]>=2) or (x[2]==1) and (x[1]>308))) then return 1e309 end if (x[2]==1) then return math.pow(10,x[1]) end return x[1]*(y or 1); end end function to_number(x) if type(x) == 'table' and (getmetatable(x) == BigMeta or getmetatable(x) == OmegaMeta) then return x:to_number() else return x end end --patch to remove animations local cest = card_eval_status_text function card_eval_status_text(a,b,c,d,e,f) if not Talisman.config_file.disable_anims then cest(a,b,c,d,e,f) end end local jc = juice_card function juice_card(x) if not Talisman.config_file.disable_anims then jc(x) end end function tal_uht(config, vals) local col = G.C.GREEN if vals.chips and G.GAME.current_round.current_hand.chips ~= vals.chips then local delta = (is_number(vals.chips) and is_number(G.GAME.current_round.current_hand.chips)) and (vals.chips - G.GAME.current_round.current_hand.chips) or 0 if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) else delta = number_format(delta) end if type(vals.chips) == 'string' then delta = vals.chips end G.GAME.current_round.current_hand.chips = vals.chips if G.hand_text_area.chips.config.object then G.hand_text_area.chips:update(0) end end if vals.mult and G.GAME.current_round.current_hand.mult ~= vals.mult then local delta = (is_number(vals.mult) and is_number(G.GAME.current_round.current_hand.mult))and (vals.mult - G.GAME.current_round.current_hand.mult) or 0 if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) else delta = number_format(delta) end if type(vals.mult) == 'string' then delta = vals.mult end G.GAME.current_round.current_hand.mult = vals.mult if G.hand_text_area.mult.config.object then G.hand_text_area.mult:update(0) end end if vals.handname and G.GAME.current_round.current_hand.handname ~= vals.handname then G.GAME.current_round.current_hand.handname = vals.handname end if vals.chip_total then G.GAME.current_round.current_hand.chip_total = vals.chip_total;G.hand_text_area.chip_total.config.object:pulse(0.5) end if vals.level and G.GAME.current_round.current_hand.hand_level ~= ' '..localize('k_lvl')..tostring(vals.level) then if vals.level == '' then 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()] else G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[1] end end end return true end local uht = update_hand_text function update_hand_text(config, vals) if Talisman.config_file.disable_anims then if G.latest_uht then local chips = G.latest_uht.vals.chips local mult = G.latest_uht.vals.mult if not vals.chips then vals.chips = chips end if not vals.mult then vals.mult = mult end end G.latest_uht = {config = config, vals = vals} else uht(config, vals) end end local upd = Game.update function Game:update(dt) upd(self, dt) if G.latest_uht and G.latest_uht.config and G.latest_uht.vals then tal_uht(G.latest_uht.config, G.latest_uht.vals) G.latest_uht = nil end if Talisman.dollar_update then G.HUD:get_UIE_by_ID('dollar_text_UI').config.object:update() G.HUD:recalculate() 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 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 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)" 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. 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, 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 end --[[local ec = eval_card function eval_card() Talisman.calculating_card = true local ret = ec() Talisman.calculating_card = false return ret end--]] local sm = Card.start_materialize function Card:start_materialize(a,b,c) if Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then return end return sm(self,a,b,c) end local sd = Card.start_dissolve function Card:start_dissolve(a,b,c,d) if Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then self:remove() return end return sd(self,a,b,c,d) end local ss = Card.set_seal function Card:set_seal(a,b,immediate) return ss(self,a,b,Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) or immediate) end function Card:get_chip_x_bonus() if self.debuff then return 0 end if self.ability.set == 'Joker' then return 0 end if (self.ability.x_chips or 0) <= 1 then return 0 end return self.ability.x_chips end function Card:get_chip_e_bonus() if self.debuff then return 0 end if self.ability.set == 'Joker' then return 0 end if (self.ability.e_chips or 0) <= 1 then return 0 end return self.ability.e_chips end function Card:get_chip_ee_bonus() if self.debuff then return 0 end if self.ability.set == 'Joker' then return 0 end if (self.ability.ee_chips or 0) <= 1 then return 0 end return self.ability.ee_chips end function Card:get_chip_eee_bonus() if self.debuff then return 0 end if self.ability.set == 'Joker' then return 0 end if (self.ability.eee_chips or 0) <= 1 then return 0 end return self.ability.eee_chips end function Card:get_chip_hyper_bonus() if self.debuff then return {0,0} end if self.ability.set == 'Joker' then return {0,0} end if type(self.ability.hyper_chips) ~= 'table' then return {0,0} end if (self.ability.hyper_chips[1] <= 0 or self.ability.hyper_chips[2] <= 0) then return {0,0} end return self.ability.hyper_chips end function Card:get_chip_e_mult() if self.debuff then return 0 end if self.ability.set == 'Joker' then return 0 end if (self.ability.e_mult or 0) <= 1 then return 0 end return self.ability.e_mult end function Card:get_chip_ee_mult() if self.debuff then return 0 end if self.ability.set == 'Joker' then return 0 end if (self.ability.ee_mult or 0) <= 1 then return 0 end return self.ability.ee_mult end function Card:get_chip_eee_mult() if self.debuff then return 0 end if self.ability.set == 'Joker' then return 0 end if (self.ability.eee_mult or 0) <= 1 then return 0 end return self.ability.eee_mult end function Card:get_chip_hyper_mult() if self.debuff then return {0,0} end if self.ability.set == 'Joker' then return {0,0} end if type(self.ability.hyper_mult) ~= 'table' then return {0,0} end if (self.ability.hyper_mult[1] <= 0 or self.ability.hyper_mult[2] <= 0) then return {0,0} end return self.ability.hyper_mult end --Easing fixes --Changed this to always work; it's less pretty but fine for held in hand things 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 G.GAME.dollars = G.GAME.dollars + mod Talisman.dollar_update = true else return edo(mod, instant) end end local su = G.start_up function safe_str_unpack(str) local chunk, err = loadstring(str) if chunk then setfenv(chunk, {Big = Big, BigMeta = BigMeta, OmegaMeta = OmegaMeta, to_big = to_big, inf = 1.79769e308}) -- Use an empty environment to prevent access to potentially harmful functions local success, result = pcall(chunk) if success then return result else print("[Talisman] Error unpacking string: " .. result) print(tostring(str)) return nil end else print("[Talisman] Error loading string: " .. err) print(tostring(str)) return nil end end function G:start_up() STR_UNPACK = safe_str_unpack su(self) STR_UNPACK = safe_str_unpack end --Skip round animation things local gfer = G.FUNCS.evaluate_round function G.FUNCS.evaluate_round() if Talisman.config_file.disable_anims then if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) then add_round_eval_row({dollars = G.GAME.blind.dollars, name='blind1', pitch = 0.95}) else add_round_eval_row({dollars = 0, name='blind1', pitch = 0.95, saved = true}) end local arer = add_round_eval_row add_round_eval_row = function() return end local dollars = gfer() add_round_eval_row = arer add_round_eval_row({name = 'bottom', dollars = Talisman.dollars}) else return gfer() 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_mult(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_mult(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_mult(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_mult(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() -- Get debug info for the caller of the function that called printCallerInfo local info = debug.getinfo(3, "Sl") callstep = callstep+1 if info then print("["..callstep.."] "..(info.short_src or "???")..":"..(info.currentline or "unknown")) else print("Caller information not available") end end local emae = EventManager.add_event function EventManager:add_event(x,y,z) printCallerInfo() return emae(self,x,y,z) end--]]